summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2023-05-11 14:05:52 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2023-05-11 14:05:52 +0300
commitcd5ca0b11882cfa8eafa8c687ed3084423bc7e52 (patch)
tree92045e255e18d9acbe7cd8385c64c9f1830db257 /sql
parent78a1264cfae37667a2812c5bb5cc6638d9eed5ff (diff)
parentc271057288f71746d1816824f338f2d9c47f67c1 (diff)
downloadmariadb-git-bb-10.6-MDEV-29911.tar.gz
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc1
-rw-r--r--sql/ha_sequence.cc2
-rw-r--r--sql/item.cc10
-rw-r--r--sql/item.h27
-rw-r--r--sql/item_cmpfunc.cc26
-rw-r--r--sql/item_cmpfunc.h6
-rw-r--r--sql/item_subselect.cc14
-rw-r--r--sql/log.cc3
-rw-r--r--sql/log_event_server.cc52
-rw-r--r--sql/mysqld.cc5
-rw-r--r--sql/opt_split.cc240
-rw-r--r--sql/rpl_parallel.cc15
-rw-r--r--sql/slave.cc35
-rw-r--r--sql/slave.h3
-rw-r--r--sql/sql_insert.cc3
-rw-r--r--sql/sql_lex.cc43
-rw-r--r--sql/sql_lex.h48
-rw-r--r--sql/sql_priv.h1
-rw-r--r--sql/sql_schema.cc61
-rw-r--r--sql/sql_schema.h11
-rw-r--r--sql/sql_select.cc276
-rw-r--r--sql/sql_select.h31
-rw-r--r--sql/sql_sequence.cc17
-rw-r--r--sql/sql_statistics.cc44
-rw-r--r--sql/sql_view.cc5
-rw-r--r--sql/sql_yacc.yy59
-rw-r--r--sql/structs.h24
-rw-r--r--sql/sys_vars.cc8
-rw-r--r--sql/table.cc41
-rw-r--r--sql/wsrep_schema.cc5
-rw-r--r--sql/wsrep_thd.h9
31 files changed, 821 insertions, 304 deletions
diff --git a/sql/field.cc b/sql/field.cc
index 333a843f81c..f413d77be87 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2503,6 +2503,7 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table,
tmp->key_start.init(0);
tmp->part_of_key.init(0);
tmp->part_of_sortkey.init(0);
+ tmp->read_stats= NULL;
/*
TODO: it is not clear why this method needs to reset unireg_check.
Try not to reset it, or explain why it needs to be reset.
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
index b348e6e7025..bab0614706d 100644
--- a/sql/ha_sequence.cc
+++ b/sql/ha_sequence.cc
@@ -250,6 +250,8 @@ int ha_sequence::write_row(const uchar *buf)
on master and slaves
- Check that the new row is an accurate SEQUENCE object
*/
+ /* mark a full binlog image insert to force non-parallel slave */
+ thd->transaction->stmt.mark_trans_did_ddl();
if (table->s->tmp_table == NO_TMP_TABLE &&
thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
MDL_EXCLUSIVE,
diff --git a/sql/item.cc b/sql/item.cc
index 6ae53764096..c10eb877179 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -10529,7 +10529,8 @@ int Item_cache_str::save_in_field(Field *field, bool no_conversions)
bool Item_cache_row::allocate(THD *thd, uint num)
{
item_count= num;
- return (!(values=
+ return (!values &&
+ !(values=
(Item_cache **) thd->calloc(sizeof(Item_cache *)*item_count)));
}
@@ -10565,11 +10566,12 @@ bool Item_cache_row::setup(THD *thd, Item *item)
return 1;
for (uint i= 0; i < item_count; i++)
{
- Item_cache *tmp;
Item *el= item->element_index(i);
- if (!(tmp= values[i]= el->get_cache(thd)))
+
+ if ((!values[i]) && !(values[i]= el->get_cache(thd)))
return 1;
- tmp->setup(thd, el);
+
+ values[i]->setup(thd, el);
}
return 0;
}
diff --git a/sql/item.h b/sql/item.h
index 3ce8adafb33..486ca098dda 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -7082,6 +7082,9 @@ public:
}
virtual void keep_array() {}
+#ifndef DBUG_OFF
+ bool is_array_kept() { return TRUE; }
+#endif
void print(String *str, enum_query_type query_type) override;
bool eq_def(const Field *field)
{
@@ -7570,13 +7573,14 @@ public:
bool null_inside() override;
void bring_value() override;
void keep_array() override { save_array= 1; }
+#ifndef DBUG_OFF
+ bool is_array_kept() { return save_array; }
+#endif
void cleanup() override
{
DBUG_ENTER("Item_cache_row::cleanup");
Item_cache::cleanup();
- if (save_array)
- bzero(values, item_count*sizeof(Item**));
- else
+ if (!save_array)
values= 0;
DBUG_VOID_RETURN;
}
@@ -7807,7 +7811,7 @@ public:
Item *get_tmp_table_item(THD *thd)
{ return m_item->get_tmp_table_item(thd); }
Item *get_copy(THD *thd)
- { return m_item->get_copy(thd); }
+ { return get_item_copy<Item_direct_ref_to_item>(thd, this); }
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields,
COND_EQUAL **cond_equal_ref)
@@ -7875,7 +7879,20 @@ public:
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return m_item->excl_dep_on_grouping_fields(sel); }
bool is_expensive() { return m_item->is_expensive(); }
- Item* build_clone(THD *thd) { return get_copy(thd); }
+ void set_item(Item *item) { m_item= item; }
+ Item *build_clone(THD *thd)
+ {
+ Item *clone_item= m_item->build_clone(thd);
+ if (clone_item)
+ {
+ Item_direct_ref_to_item *copy= (Item_direct_ref_to_item *) get_copy(thd);
+ if (!copy)
+ return 0;
+ copy->set_item(clone_item);
+ return copy;
+ }
+ return 0;
+ }
void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 87d75ca61b7..4c2f30c1c26 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1298,9 +1298,22 @@ bool Item_in_optimizer::fix_left(THD *thd)
ref0= args[1]->get_IN_subquery()->left_exp_ptr();
args[0]= (*ref0);
}
- if ((*ref0)->fix_fields_if_needed(thd, ref0) ||
- (!cache && !(cache= (*ref0)->get_cache(thd))))
+ if ((*ref0)->fix_fields_if_needed(thd, ref0))
DBUG_RETURN(1);
+ if (!cache)
+ {
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ bool rc= !(cache= (*ref0)->get_cache(thd));
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ if (rc)
+ DBUG_RETURN(1);
+ cache->keep_array();
+ }
/*
During fix_field() expression could be substituted.
So we copy changes before use
@@ -1659,19 +1672,10 @@ longlong Item_in_optimizer::val_int()
}
-void Item_in_optimizer::keep_top_level_cache()
-{
- cache->keep_array();
- save_cache= 1;
-}
-
-
void Item_in_optimizer::cleanup()
{
DBUG_ENTER("Item_in_optimizer::cleanup");
Item_bool_func::cleanup();
- if (!save_cache)
- cache= 0;
expr_cache= 0;
DBUG_VOID_RETURN;
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 8e0a150f9c0..f11b668d546 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -374,8 +374,7 @@ class Item_in_optimizer: public Item_bool_func
protected:
Item_cache *cache;
Item *expr_cache;
- bool save_cache;
- /*
+ /*
Stores the value of "NULL IN (SELECT ...)" for uncorrelated subqueries:
UNKNOWN - "NULL in (SELECT ...)" has not yet been evaluated
FALSE - result is FALSE
@@ -385,7 +384,7 @@ protected:
public:
Item_in_optimizer(THD *thd, Item *a, Item *b):
Item_bool_func(thd, a, b), cache(0), expr_cache(0),
- save_cache(0), result_for_null_param(UNKNOWN)
+ result_for_null_param(UNKNOWN)
{
with_flags|= item_with_t::SUBQUERY;
}
@@ -402,7 +401,6 @@ public:
return name;
}
Item_cache **get_cache() { return &cache; }
- void keep_top_level_cache();
Item *transform(THD *thd, Item_transformer transformer, uchar *arg) override;
Item *expr_cache_insert_transformer(THD *thd, uchar *unused) override;
bool is_expensive_processor(void *arg) override;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 862dc15e3e3..eade1356da0 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -406,11 +406,11 @@ bool Item_subselect::mark_as_dependent(THD *thd, st_select_lex *select,
{
is_correlated= TRUE;
Ref_to_outside *upper;
- if (!(upper= new (thd->stmt_arena->mem_root) Ref_to_outside()))
+ if (!(upper= new (thd->mem_root) Ref_to_outside()))
return TRUE;
upper->select= select;
upper->item= item;
- if (upper_refs.push_back(upper, thd->stmt_arena->mem_root))
+ if (upper_refs.push_back(upper, thd->mem_root))
return TRUE;
}
return FALSE;
@@ -2067,7 +2067,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
thd->lex->current_select= current;
/* We will refer to upper level cache array => we have to save it for SP */
- optimizer->keep_top_level_cache();
+ DBUG_ASSERT(optimizer->get_cache()[0]->is_array_kept());
/*
As far as Item_in_optimizer does not substitute itself on fix_fields
@@ -2464,7 +2464,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
}
// we will refer to upper level cache array => we have to save it in PS
- optimizer->keep_top_level_cache();
+ DBUG_ASSERT(optimizer->get_cache()[0]->is_array_kept());
thd->lex->current_select= current;
/*
@@ -4616,6 +4616,12 @@ void subselect_uniquesubquery_engine::print(String *str,
{
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)
{
diff --git a/sql/log.cc b/sql/log.cc
index 76a94c2814e..811921d8322 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -11815,7 +11815,10 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
if (typ == START_ENCRYPTION_EVENT)
{
if (fdle->start_decryption((Start_encryption_log_event*) ev))
+ {
errormsg= "Could not set up decryption for binlog.";
+ break;
+ }
}
delete ev;
if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 013515850ef..f80cf68623a 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -7543,8 +7543,18 @@ Rows_log_event::write_row(rpl_group_info *rgi,
int Rows_log_event::update_sequence()
{
TABLE *table= m_table; // pointer to event's table
+ bool old_master= false;
+ int err= 0;
- if (!bitmap_is_set(table->rpl_write_set, MIN_VALUE_FIELD_NO))
+ if (!bitmap_is_set(table->rpl_write_set, MIN_VALUE_FIELD_NO) ||
+ (
+#if defined(WITH_WSREP)
+ ! WSREP(thd) &&
+#endif
+ !(table->in_use->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_DDL) &&
+ !(old_master=
+ rpl_master_has_bug(thd->rgi_slave->rli,
+ 29621, FALSE, FALSE, FALSE, TRUE))))
{
/* This event come from a setval function executed on the master.
Update the sequence next_number and round, like we do with setval()
@@ -7557,12 +7567,27 @@ int Rows_log_event::update_sequence()
return table->s->sequence->set_value(table, nextval, round, 0) > 0;
}
-
+ if (old_master && !WSREP(thd) && thd->rgi_slave->is_parallel_exec)
+ {
+ DBUG_ASSERT(thd->rgi_slave->parallel_entry);
+ /*
+ With parallel replication enabled, we can't execute alongside any other
+ transaction in which we may depend, so we force retry to release
+ the server layer table lock for possible prior in binlog order
+ same table transactions.
+ */
+ if (thd->rgi_slave->parallel_entry->last_committed_sub_id <
+ thd->rgi_slave->wait_commit_sub_id)
+ {
+ err= ER_LOCK_DEADLOCK;
+ my_error(err, MYF(0));
+ }
+ }
/*
Update all fields in table and update the active sequence, like with
ALTER SEQUENCE
*/
- return table->file->ha_write_row(table->record[0]);
+ return err == 0 ? table->file->ha_write_row(table->record[0]) : err;
}
@@ -7631,7 +7656,7 @@ uint8 Write_rows_log_event::get_trg_event_map()
Returns TRUE if different.
*/
-static bool record_compare(TABLE *table)
+static bool record_compare(TABLE *table, bool vers_from_plain= false)
{
bool result= FALSE;
/**
@@ -7664,10 +7689,19 @@ static bool record_compare(TABLE *table)
/* Compare fields */
for (Field **ptr=table->field ; *ptr ; ptr++)
{
- if (table->versioned() && (*ptr)->vers_sys_field())
- {
+ /*
+ If the table is versioned, don't compare using the version if there is a
+ primary key. If there isn't a primary key, we need the version to
+ identify the correct record if there are duplicate rows in the data set.
+ However, if the primary server is unversioned (vers_from_plain is true),
+ then we implicitly use row_end as the primary key on our side. This is
+ because the implicit row_end value will be set to the maximum value for
+ the latest row update (which is what we care about).
+ */
+ if (table->versioned() && (*ptr)->vers_sys_field() &&
+ (table->s->primary_key < MAX_KEY ||
+ (vers_from_plain && table->vers_start_field() == (*ptr))))
continue;
- }
/**
We only compare field contents that are not null.
NULL fields (i.e., their null bits) were compared
@@ -8064,7 +8098,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
/* We use this to test that the correct key is used in test cases. */
DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort(););
- while (record_compare(table))
+ while (record_compare(table, m_vers_from_plain))
{
while ((error= table->file->ha_index_next(table->record[0])))
{
@@ -8117,7 +8151,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
goto end;
}
}
- while (record_compare(table));
+ while (record_compare(table, m_vers_from_plain));
/*
Note: above record_compare will take into accout all record fields
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 2cb26845e43..3163ff36c9a 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -6372,8 +6372,6 @@ struct my_option my_long_options[]=
{"console", OPT_CONSOLE, "Write error output on screen; don't remove the console window on windows.",
&opt_console, &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 0},
- {"core-file", OPT_WANT_CORE, "Write core on errors.", 0, 0, 0, GET_NO_ARG,
- NO_ARG, 0, 0, 0, 0, 0, 0},
#ifdef DBUG_OFF
{"debug", '#', "Built in DBUG debugger. Disabled in this build.",
&current_dbug_option, &current_dbug_option, 0, GET_STR, OPT_ARG,
@@ -8160,9 +8158,6 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument,
case (int) OPT_SKIP_HOST_CACHE:
opt_specialflag|= SPECIAL_NO_HOST_CACHE;
break;
- case (int) OPT_WANT_CORE:
- test_flags |= TEST_CORE_ON_SIGNAL;
- break;
case OPT_CONSOLE:
if (opt_console)
opt_error_log= 0; // Force logs to stdout
diff --git a/sql/opt_split.cc b/sql/opt_split.cc
index 18710e85624..627aebe9dec 100644
--- a/sql/opt_split.cc
+++ b/sql/opt_split.cc
@@ -65,7 +65,7 @@
If we have only one equi-join condition then we either push it as
for Q1R or we don't. In a general case we may have much more options.
Consider the query (Q3)
- SELECT
+ SELECT *
FROM t1,t2 (SELECT t3.a, t3.b, MIN(t3.c) as min
FROM t3 GROUP BY a,b) t
WHERE t.a = t1.a AND t.b = t2.b
@@ -102,6 +102,47 @@
If we just drop the index on t3(a,b) the chances that the splitting
will be used becomes much lower but they still exists providing that
the fanout of the partial join of t1 and t2 is small enough.
+
+ The lateral derived table LT formed as a result of SM optimization applied
+ to a materialized derived table DT must be joined after all parameters
+ of splitting has been evaluated, i.e. after all expressions used in the
+ equalities pushed into DT that make the employed splitting effective
+ could be evaluated. With the chosen join order all the parameters can be
+ evaluated after the last table LPT that contains any columns referenced in
+ the parameters has been joined and the table APT following LPT in the chosen
+ join order is accessed.
+ Usually the formed lateral derived table LT is accessed right after the table
+ LPT. As in such cases table LT must be refilled for each combination of
+ splitting parameters this table must be populated before each access to LT
+ and the estimate of the expected number of refills that could be suggested in
+ such cases is the number of rows in the partial join ending with table LPT.
+ However in other cases the chosen join order may contain tables between LPT
+ and LT.
+ Consider the query (Q4)
+ SELECT *
+ FROM t1 JOIN t2 ON t1.b = t2.b
+ LEFT JOIN (SELECT t3.a, t3.b, MIN(t3.c) as min
+ FROM t3 GROUP BY a,b) t
+ ON t.a = t1.a AND t.c > 0
+ [WHERE P(t1,t2)];
+ Let's assume that the join order t1,t2,t was chosen for this query and
+ SP optimization was applied to t with splitting over t3.a using the index
+ on column t3.a. Here the table t1 serves as LPT, t2 as APT while t with
+ pushed condition t.a = t1.a serves as LT. Note that here LT is accessed
+ after t2, not right after t1. Here the number of refills of the lateral
+ derived is not more that the number of key values of t1.a that might be
+ less than the cardinality of the partial join (t1,t2). That's why it makes
+ sense to signal that t3 has to be refilled just before t2 is accessed.
+ However if the cardinality of the partial join (t1,t2) happens to be less
+ than the cardinality of the partial join (t1) due to additional selective
+ condition P(t1,t2) then the flag informing about necessity of a new refill
+ can be set either when accessing t2 or right after it has been joined.
+ The current code sets such flag right after generating a record of the
+ partial join with minimal cardinality for all those partial joins that
+ end between APT and LT. It allows sometimes to push extra conditions
+ into the lateral derived without any increase of the number of refills.
+ However this flag can be set only after the last join table between
+ APT and LT using join buffer has been joined.
*/
/*
@@ -249,6 +290,7 @@ public:
double unsplit_card;
/* Lastly evaluated execution plan for 'join' with pushed equalities */
SplM_plan_info *last_plan;
+ double last_refills;
SplM_plan_info *find_plan(TABLE *table, uint key, uint parts);
};
@@ -346,6 +388,9 @@ bool JOIN::check_for_splittable_materialized()
if (!partition_list)
return false;
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_split(thd, "check_split_materialized");
+
ORDER *ord;
Dynamic_array<SplM_field_ext_info> candidates(PSI_INSTRUMENT_MEM);
@@ -391,7 +436,10 @@ bool JOIN::check_for_splittable_materialized()
}
}
if (candidates.elements() == 0) // no candidates satisfying (8.1) && (8.2)
+ {
+ trace_split.add("not_applicable", "group list has no candidates");
return false;
+ }
/*
For each table from this join find the keys that can be used for ref access
@@ -450,7 +498,11 @@ bool JOIN::check_for_splittable_materialized()
}
if (!spl_field_cnt) // No candidate field can be accessed by ref => !(9)
+ {
+ trace_split.add("not_applicable",
+ "no candidate field can be accessed through ref");
return false;
+ }
/*
Create a structure of the type SplM_opt_info and fill it with
@@ -468,16 +520,22 @@ bool JOIN::check_for_splittable_materialized()
spl_opt_info->tables_usable_for_splitting= 0;
spl_opt_info->spl_field_cnt= spl_field_cnt;
spl_opt_info->spl_fields= spl_field;
- for (cand= cand_start; cand < cand_end; cand++)
+
{
- if (!cand->is_usable_for_ref_access)
- continue;
- spl_field->producing_item= cand->producing_item;
- spl_field->underlying_field= cand->underlying_field;
- spl_field->mat_field= cand->mat_field;
- spl_opt_info->tables_usable_for_splitting|=
- cand->underlying_field->table->map;
- spl_field++;
+ Json_writer_array trace_range(thd, "split_candidates");
+ for (cand= cand_start; cand < cand_end; cand++)
+ {
+ if (!cand->is_usable_for_ref_access)
+ continue;
+ trace_range.add(cand->producing_item);
+
+ spl_field->producing_item= cand->producing_item;
+ spl_field->underlying_field= cand->underlying_field;
+ spl_field->mat_field= cand->mat_field;
+ spl_opt_info->tables_usable_for_splitting|=
+ cand->underlying_field->table->map;
+ spl_field++;
+ }
}
/* Attach this info to the table T */
@@ -732,7 +790,7 @@ void JOIN::add_keyuses_for_splitting()
bzero((char*) &keyuse_ext_end, sizeof(keyuse_ext_end));
if (ext_keyuses_for_splitting->push(keyuse_ext_end))
goto err;
-
+ // psergey-todo: trace anything here?
spl_opt_info->unsplit_card= join_record_count;
rec_len= table->s->rec_buff_length;
@@ -830,13 +888,13 @@ SplM_plan_info *SplM_opt_info::find_plan(TABLE *table, uint key, uint parts)
static
void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start,
TABLE *table, uint key,
- table_map remaining_tables,
+ table_map excluded_tables,
bool validity_val)
{
KEYUSE_EXT *keyuse_ext= key_keyuse_ext_start;
do
{
- if (!(keyuse_ext->needed_in_prefix & remaining_tables))
+ if (!(keyuse_ext->needed_in_prefix & excluded_tables))
{
/*
The enabling/disabling flags are set just in KEYUSE_EXT structures.
@@ -856,8 +914,11 @@ void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start,
Choose the best splitting to extend the evaluated partial join
@param
- record_count estimated cardinality of the extended partial join
+ idx index for joined table T in current partial join P
remaining_tables tables not joined yet
+ spl_pd_boundary OUT bitmap of the table from P extended by T that
+ starts the sub-sequence of tables S from which
+ no conditions are allowed to be pushed into T.
@details
This function is called during the search for the best execution
@@ -872,17 +933,19 @@ void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start,
splitting the function set it as the true plan of materialization
of the table T.
The function caches the found plans for materialization of table T
- together if the info what key was used for splitting. Next time when
+ together with the info what key was used for splitting. Next time when
the optimizer prefers to use the same key the plan is taken from
the cache of plans
@retval
Pointer to the info on the found plan that employs the pushed equalities
if the plan has been chosen, NULL - otherwise.
+ If the function returns NULL the value of spl_param_tables is set to 0.
*/
-SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
- table_map remaining_tables)
+SplM_plan_info * JOIN_TAB::choose_best_splitting(uint idx,
+ table_map remaining_tables,
+ table_map *spl_pd_boundary)
{
SplM_opt_info *spl_opt_info= table->spl_opt_info;
DBUG_ASSERT(spl_opt_info != NULL);
@@ -897,7 +960,9 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
SplM_plan_info *spl_plan= 0;
uint best_key= 0;
uint best_key_parts= 0;
-
+ table_map best_param_tables;
+ Json_writer_object trace_obj(thd, "choose_best_splitting");
+ Json_writer_array trace_arr(thd, "considered_keys");
/*
Check whether there are keys that can be used to join T employing splitting
and if so, select the best out of such keys
@@ -915,6 +980,7 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
uint key= keyuse_ext->key;
KEYUSE_EXT *key_keyuse_ext_start= keyuse_ext;
key_part_map found_parts= 0;
+ table_map needed_in_prefix= 0;
do
{
if (keyuse_ext->needed_in_prefix & remaining_tables)
@@ -940,6 +1006,7 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
KEY *key_info= table->key_info + key;
double rec_per_key=
key_info->actual_rec_per_key(keyuse_ext->keypart);
+ needed_in_prefix|= keyuse_ext->needed_in_prefix;
if (rec_per_key < best_rec_per_key)
{
best_table= keyuse_ext->table;
@@ -947,6 +1014,14 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
best_key_parts= keyuse_ext->keypart + 1;
best_rec_per_key= rec_per_key;
best_key_keyuse_ext_start= key_keyuse_ext_start;
+ best_param_tables= needed_in_prefix;
+ // trace table, key_name, parts, needed_tables.
+ Json_writer_object cur_index(thd);
+ cur_index.
+ add("table_name", best_table->alias.ptr()).
+ add("index", best_table->key_info[best_key].name).
+ add("rec_per_key", best_rec_per_key).
+ add("param_tables", best_param_tables);
}
keyuse_ext++;
}
@@ -954,14 +1029,41 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
}
while (keyuse_ext->table == table);
}
+ trace_arr.end();
+
spl_opt_info->last_plan= 0;
+ double refills= DBL_MAX;
+ table_map excluded_tables= remaining_tables | this->join->sjm_lookup_tables;
if (best_table)
{
+ *spl_pd_boundary= this->table->map;
+ if (!best_param_tables)
+ refills= 1;
+ else
+ {
+ table_map last_found= this->table->map;
+ for (POSITION *pos= &this->join->positions[idx - 1]; ; pos--)
+ {
+ if (pos->table->table->map & excluded_tables)
+ continue;
+ if (pos->partial_join_cardinality < refills)
+ {
+ *spl_pd_boundary= last_found;
+ refills= pos->partial_join_cardinality;
+ }
+ last_found= pos->table->table->map;
+ if ((last_found & best_param_tables) || pos->use_join_buffer)
+ break;
+ }
+ }
+
+ trace_obj.add("refills", refills).
+ add("spl_pd_boundary", *spl_pd_boundary);
+
/*
The key for splitting was chosen, look for the plan for this key
in the cache
*/
- Json_writer_array spl_trace(thd, "choose_best_splitting");
spl_plan= spl_opt_info->find_plan(best_table, best_key, best_key_parts);
if (!spl_plan)
{
@@ -969,11 +1071,13 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
The plan for the chosen key has not been found in the cache.
Build a new plan and save info on it in the cache
*/
+ Json_writer_array wrapper(thd, "split_plan_search");
table_map all_table_map= (((table_map) 1) << join->table_count) - 1;
reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table,
- best_key, remaining_tables, true);
+ best_key, excluded_tables, true);
choose_plan(join, all_table_map & ~join->const_table_map);
+ wrapper.end();
/*
Check that the chosen plan is really a splitting plan.
If not or if there is not enough memory to save the plan in the cache
@@ -990,7 +1094,8 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
spl_opt_info->plan_cache.push_back(spl_plan))
{
reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table,
- best_key, remaining_tables, false);
+ best_key, excluded_tables, false);
+ trace_obj.add("split_plan_discarded", "constructed unapplicable query plan");
return 0;
}
@@ -1010,32 +1115,41 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
spl_plan->cost= join->best_positions[join->table_count-1].read_time +
+ oper_cost;
- if (unlikely(thd->trace_started()))
- {
- Json_writer_object wrapper(thd);
- Json_writer_object find_trace(thd, "best_splitting");
- find_trace.add("table", best_table->alias.c_ptr());
- find_trace.add("key", best_table->key_info[best_key].name);
- find_trace.add("record_count", record_count);
- find_trace.add("cost", spl_plan->cost);
- find_trace.add("unsplit_cost", spl_opt_info->unsplit_cost);
- }
memcpy((char *) spl_plan->best_positions,
(char *) join->best_positions,
sizeof(POSITION) * join->table_count);
reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table,
- best_key, remaining_tables, false);
+ best_key, excluded_tables, false);
}
+ else
+ trace_obj.add("cached_plan_found", 1);
+
if (spl_plan)
{
- if(record_count * spl_plan->cost < spl_opt_info->unsplit_cost - 0.01)
+ if (unlikely(thd->trace_started()))
+ {
+ trace_obj.
+ add("lead_table", spl_plan->table->alias.ptr()).
+ add("index", spl_plan->table->key_info[spl_plan->key].name).
+ add("parts", spl_plan->parts).
+ add("split_sel", spl_plan->split_sel).
+ add("cost", spl_plan->cost).
+ add("unsplit_cost", spl_opt_info->unsplit_cost).
+ add("records", (ha_rows) (records * spl_plan->split_sel));
+ }
+
+ if (refills * spl_plan->cost < spl_opt_info->unsplit_cost - 0.01)
{
/*
The best plan that employs splitting is cheaper than
the plan without splitting
*/
spl_opt_info->last_plan= spl_plan;
+ spl_opt_info->last_refills= refills;
+ trace_obj.add("chosen", true);
}
+ else
+ trace_obj.add("chosen", false);
}
}
@@ -1044,16 +1158,14 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
spl_plan= spl_opt_info->last_plan;
if (spl_plan)
{
- startup_cost= record_count * spl_plan->cost;
+ startup_cost= spl_opt_info->last_refills * spl_plan->cost;
records= (ha_rows) (records * spl_plan->split_sel);
-
- Json_writer_object trace(thd, "lateral_derived");
- trace.add("startup_cost", startup_cost);
- trace.add("splitting_cost", spl_plan->cost);
- trace.add("records", records);
}
else
+ {
startup_cost= spl_opt_info->unsplit_cost;
+ *spl_pd_boundary= 0;
+ }
return spl_plan;
}
@@ -1063,13 +1175,13 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
Inject equalities for splitting used by the materialization join
@param
- excluded_tables used to filter out the equalities that cannot
- be pushed.
+ excluded_tables used to filter out the equalities that are not
+ to be pushed.
@details
This function injects equalities pushed into a derived table T for which
the split optimization has been chosen by the optimizer. The function
- is called by JOIN::inject_splitting_cond_for_all_tables_with_split_op().
+ is called by JOIN::inject_splitting_cond_for_all_tables_with_split_opt().
All equalities usable for splitting T whose right parts do not depend on
any of the 'excluded_tables' can be pushed into the where clause of the
derived table T.
@@ -1157,7 +1269,7 @@ bool is_eq_cond_injected_for_split_opt(Item_func_eq *eq_item)
@param
spl_plan info on the splitting plan chosen for the splittable table T
- remaining_tables the table T is joined just before these tables
+ excluded_tables tables that cannot be used in equalities pushed into T
is_const_table the table T is a constant table
@details
@@ -1172,7 +1284,7 @@ bool is_eq_cond_injected_for_split_opt(Item_func_eq *eq_item)
*/
bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan,
- table_map remaining_tables,
+ table_map excluded_tables,
bool is_const_table)
{
SplM_opt_info *spl_opt_info= table->spl_opt_info;
@@ -1180,6 +1292,7 @@ bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan,
JOIN *md_join= spl_opt_info->join;
if (spl_plan && !is_const_table)
{
+ is_split_derived= true;
memcpy((char *) md_join->best_positions,
(char *) spl_plan->best_positions,
sizeof(POSITION) * md_join->table_count);
@@ -1190,7 +1303,7 @@ bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan,
reset_validity_vars_for_keyuses(spl_plan->keyuse_ext_start,
spl_plan->table,
spl_plan->key,
- remaining_tables,
+ excluded_tables,
true);
}
else if (md_join->save_qep)
@@ -1226,8 +1339,21 @@ bool JOIN::fix_all_splittings_in_plan()
if (tab->table->is_splittable())
{
SplM_plan_info *spl_plan= cur_pos->spl_plan;
+ table_map excluded_tables= (all_tables & ~prev_tables) |
+ sjm_lookup_tables;
+ ;
+ if (spl_plan)
+ {
+ POSITION *pos= cur_pos;
+ table_map spl_pd_boundary= pos->spl_pd_boundary;
+ do
+ {
+ excluded_tables|= pos->table->table->map;
+ }
+ while (!((pos--)->table->table->map & spl_pd_boundary));
+ }
if (tab->fix_splitting(spl_plan,
- all_tables & ~prev_tables,
+ excluded_tables,
tablenr < const_tables ))
return true;
}
@@ -1266,13 +1392,21 @@ bool JOIN::inject_splitting_cond_for_all_tables_with_split_opt()
continue;
SplM_opt_info *spl_opt_info= tab->table->spl_opt_info;
JOIN *join= spl_opt_info->join;
- /*
- Currently the equalities referencing columns of SJM tables with
- look-up access cannot be pushed into materialized derived.
- */
- if (join->inject_best_splitting_cond((all_tables & ~prev_tables) |
- sjm_lookup_tables))
- return true;
+ table_map excluded_tables= (all_tables & ~prev_tables) | sjm_lookup_tables;
+ table_map spl_pd_boundary= cur_pos->spl_pd_boundary;
+ for (POSITION *pos= cur_pos; ; pos--)
+ {
+ excluded_tables|= pos->table->table->map;
+ pos->table->no_forced_join_cache= true;
+ if (pos->table->table->map & spl_pd_boundary)
+ {
+ pos->table->split_derived_to_update|= tab->table->map;
+ break;
+ }
+ }
+
+ if (join->inject_best_splitting_cond(excluded_tables))
+ return true;
}
return false;
}
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 341f01ad9fa..78d81d973e4 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -2400,14 +2400,16 @@ rpl_parallel::find(uint32 domain_id)
e->domain_id= domain_id;
e->stop_on_error_sub_id= (uint64)ULONGLONG_MAX;
e->pause_sub_id= (uint64)ULONGLONG_MAX;
+ mysql_mutex_init(key_LOCK_parallel_entry, &e->LOCK_parallel_entry,
+ MY_MUTEX_INIT_FAST);
+ 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);
return NULL;
}
- mysql_mutex_init(key_LOCK_parallel_entry, &e->LOCK_parallel_entry,
- MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_COND_parallel_entry, &e->COND_parallel_entry, NULL);
}
else
e->force_abort= false;
@@ -2933,7 +2935,12 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
if (mode <= SLAVE_PARALLEL_MINIMAL ||
!(gtid_flags & Gtid_log_event::FL_GROUP_COMMIT_ID) ||
- e->last_commit_id != gtid_ev->commit_id)
+ e->last_commit_id != gtid_ev->commit_id ||
+ /*
+ MULTI_BATCH is also set when the current gtid even being a member
+ of a commit group is flagged as DDL which disallows parallel.
+ */
+ (gtid_flags & Gtid_log_event::FL_DDL))
flags|= group_commit_orderer::MULTI_BATCH;
/* Make sure we do not attempt to run DDL in parallel speculatively. */
if (gtid_flags & Gtid_log_event::FL_DDL)
diff --git a/sql/slave.cc b/sql/slave.cc
index 64a35ff0119..cb093560456 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -8171,14 +8171,15 @@ end:
@return TRUE if master has the bug, FALSE if it does not.
*/
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
- bool (*pred)(const void *), const void *param)
+ bool (*pred)(const void *), const void *param,
+ bool maria_master)
{
struct st_version_range_for_one_bug {
uint bug_id;
Version introduced_in; // first version with bug
Version fixed_in; // first version with fix
};
- static struct st_version_range_for_one_bug versions_for_all_bugs[]=
+ static struct st_version_range_for_one_bug versions_for_their_bugs[]=
{
{24432, { 5, 0, 24 }, { 5, 0, 38 } },
{24432, { 5, 1, 12 }, { 5, 1, 17 } },
@@ -8186,11 +8187,27 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
{33029, { 5, 1, 0 }, { 5, 1, 12 } },
{37426, { 5, 1, 0 }, { 5, 1, 26 } },
};
+ static struct st_version_range_for_one_bug versions_for_our_bugs[]=
+ {
+ {29621, { 10, 3, 36 }, { 10, 3, 39 } },
+ {29621, { 10, 4, 26 }, { 10, 4, 29 } },
+ {29621, { 10, 5, 17 }, { 10, 5, 20 } },
+ {29621, { 10, 6, 9 }, { 10, 6, 13 } },
+ {29621, { 10, 7, 5 }, { 10, 7, 9 } },
+ {29621, { 10, 8, 4 }, { 10, 8, 8 } },
+ {29621, { 10, 9, 2 }, { 10, 9, 6 } },
+ {29621, { 10, 10,1 }, { 10, 10,4 } },
+ {29621, { 10, 11,1 }, { 10, 11,3 } },
+ };
const Version &master_ver=
rli->relay_log.description_event_for_exec->server_version_split;
+ struct st_version_range_for_one_bug* versions_for_all_bugs= maria_master ?
+ versions_for_our_bugs : versions_for_their_bugs;
+ uint all_size= maria_master ?
+ sizeof(versions_for_our_bugs)/sizeof(*versions_for_our_bugs) :
+ sizeof(versions_for_their_bugs)/sizeof(*versions_for_their_bugs);
- for (uint i= 0;
- i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++)
+ for (uint i= 0; i < all_size; i++)
{
const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
@@ -8199,18 +8216,21 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
fixed_in > master_ver &&
(pred == NULL || (*pred)(param)))
{
+ const char *bug_source= maria_master ?
+ "https://jira.mariadb.org/browse/MDEV-" :
+ "http://bugs.mysql.com/bug.php?id=";
if (!report)
return TRUE;
// a short message for SHOW SLAVE STATUS (message length constraints)
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
- " http://bugs.mysql.com/bug.php?id=%u"
+ " %s%u"
" so slave stops; check error log on slave"
- " for more info", MYF(0), bug_id);
+ " for more info", MYF(0), bug_source, bug_id);
// a verbose message for the error log
rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, NULL,
"According to the master's version ('%s'),"
" it is probable that master suffers from this bug:"
- " http://bugs.mysql.com/bug.php?id=%u"
+ " %s%u"
" and thus replicating the current binary log event"
" may make the slave's data become different from the"
" master's data."
@@ -8224,6 +8244,7 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
" equal to '%d.%d.%d'. Then replication can be"
" restarted.",
rli->relay_log.description_event_for_exec->server_version,
+ bug_source,
bug_id,
fixed_in[0], fixed_in[1], fixed_in[2]);
return TRUE;
diff --git a/sql/slave.h b/sql/slave.h
index e2bd5cec1b9..02de9135c2a 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -231,7 +231,8 @@ bool show_all_master_info(THD* thd);
void show_binlog_info_get_fields(THD *thd, List<Item> *field_list);
bool show_binlog_info(THD* thd);
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
- bool (*pred)(const void *), const void *param);
+ bool (*pred)(const void *), const void *param,
+ bool maria_master= false);
bool rpl_master_erroneous_autoinc(THD* thd);
const char *print_slave_db_safe(const char *db);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index c891ff8d650..22374458c3d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2223,7 +2223,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *t
for (Field **field=entry->field ; *field ; field++)
{
if (!bitmap_is_set(write_set, (*field)->field_index) &&
- !(*field)->vers_sys_field() &&
+ !(*field)->vers_sys_field() && !(*field)->vcol_info &&
has_no_default_value(thd, *field, table_list) &&
((*field)->real_type() != MYSQL_TYPE_ENUM))
err=1;
@@ -4159,6 +4159,7 @@ bool select_insert::store_values(List<Item> &values)
DBUG_ENTER("select_insert::store_values");
bool error;
+ table->reset_default_fields();
if (fields->elements)
error= fill_record_n_invoke_before_triggers(thd, table, *fields, values,
true, TRG_EVENT_INSERT);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 61df2f153db..5967a4a452a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -886,7 +886,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
}
-size_t Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
+size_t Lex_input_stream::get_body_utf8_maximum_length(THD *thd) const
{
/*
String literals can grow during escaping:
@@ -1391,7 +1391,7 @@ Yacc_state::~Yacc_state()
}
int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
- uint len, bool function)
+ uint len, bool function) const
{
const char *tok= m_tok_start;
@@ -4090,7 +4090,7 @@ bool LEX::can_use_merged()
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool LEX::can_not_use_merged(bool no_update_or_delete)
+bool LEX::can_not_use_merged()
{
switch (sql_command) {
case SQLCOM_CREATE_VIEW:
@@ -4103,10 +4103,6 @@ bool LEX::can_not_use_merged(bool no_update_or_delete)
case SQLCOM_SHOW_FIELDS:
return TRUE;
- case SQLCOM_UPDATE_MULTI:
- case SQLCOM_DELETE_MULTI:
- return no_update_or_delete;
-
default:
return FALSE;
}
@@ -9384,22 +9380,6 @@ bool LEX::add_grant_command(THD *thd, const List<LEX_COLUMN> &columns)
}
-Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b, Item *c)
-{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_substr_oracle(thd, a, b, c) :
- new (thd->mem_root) Item_func_substr(thd, a, b, c);
-}
-
-
-Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b)
-{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_substr_oracle(thd, a, b) :
- new (thd->mem_root) Item_func_substr(thd, a, b);
-}
-
-
Item *LEX::make_item_func_sysdate(THD *thd, uint fsp)
{
/*
@@ -9420,17 +9400,6 @@ Item *LEX::make_item_func_sysdate(THD *thd, uint fsp)
}
-Item *LEX::make_item_func_replace(THD *thd,
- Item *org,
- Item *find,
- Item *replace)
-{
- return (thd->variables.sql_mode & MODE_ORACLE) ?
- new (thd->mem_root) Item_func_replace_oracle(thd, org, find, replace) :
- new (thd->mem_root) Item_func_replace(thd, org, find, replace);
-}
-
-
bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
const LEX_CSTRING field_name)
{
@@ -11847,3 +11816,9 @@ bool SELECT_LEX_UNIT::explainable() const
derived->is_materialized_derived() : // (3)
false;
}
+
+
+bool st_select_lex::is_query_topmost(THD *thd)
+{
+ return get_master() == &thd->lex->unit;
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 778c6105d6b..24185dbc734 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1384,6 +1384,7 @@ public:
return (st_select_lex_unit*) slave;
}
st_select_lex* outer_select();
+ bool is_query_topmost(THD *thd);
st_select_lex* next_select() { return (st_select_lex*) next; }
st_select_lex* next_select_in_list()
{
@@ -2506,7 +2507,7 @@ private:
Get the last character accepted.
@return the last character accepted.
*/
- unsigned char yyGetLast()
+ unsigned char yyGetLast() const
{
return m_ptr[-1];
}
@@ -2514,7 +2515,7 @@ private:
/**
Look at the next character to parse, but do not accept it.
*/
- unsigned char yyPeek()
+ unsigned char yyPeek() const
{
return m_ptr[0];
}
@@ -2523,7 +2524,7 @@ private:
Look ahead at some character to parse.
@param n offset of the character to look up
*/
- unsigned char yyPeekn(int n)
+ unsigned char yyPeekn(int n) const
{
return m_ptr[n];
}
@@ -2584,7 +2585,7 @@ private:
@param n number of characters expected
@return true if there are less than n characters to parse
*/
- bool eof(int n)
+ bool eof(int n) const
{
return ((m_ptr + n) >= m_end_of_query);
}
@@ -2615,10 +2616,10 @@ private:
Get the maximum length of the utf8-body buffer.
The utf8 body can grow because of the character set conversion and escaping.
*/
- size_t get_body_utf8_maximum_length(THD *thd);
+ size_t get_body_utf8_maximum_length(THD *thd) const;
/** Get the length of the current token, in the raw buffer. */
- uint yyLength()
+ uint yyLength() const
{
/*
The assumption is that the lexical analyser is always 1 character ahead,
@@ -2643,31 +2644,31 @@ public:
End of file indicator for the query text to parse.
@return true if there are no more characters to parse
*/
- bool eof()
+ bool eof() const
{
return (m_ptr >= m_end_of_query);
}
/** Get the raw query buffer. */
- const char *get_buf()
+ const char *get_buf() const
{
return m_buf;
}
/** Get the pre-processed query buffer. */
- const char *get_cpp_buf()
+ const char *get_cpp_buf() const
{
return m_cpp_buf;
}
/** Get the end of the raw query buffer. */
- const char *get_end_of_query()
+ const char *get_end_of_query() const
{
return m_end_of_query;
}
/** Get the token start position, in the raw buffer. */
- const char *get_tok_start()
+ const char *get_tok_start() const
{
return has_lookahead() ? m_tok_start_prev : m_tok_start;
}
@@ -2678,25 +2679,25 @@ public:
}
/** Get the token end position, in the raw buffer. */
- const char *get_tok_end()
+ const char *get_tok_end() const
{
return m_tok_end;
}
/** Get the current stream pointer, in the raw buffer. */
- const char *get_ptr()
+ const char *get_ptr() const
{
return m_ptr;
}
/** Get the token start position, in the pre-processed buffer. */
- const char *get_cpp_tok_start()
+ const char *get_cpp_tok_start() const
{
return has_lookahead() ? m_cpp_tok_start_prev : m_cpp_tok_start;
}
/** Get the token end position, in the pre-processed buffer. */
- const char *get_cpp_tok_end()
+ const char *get_cpp_tok_end() const
{
return m_cpp_tok_end;
}
@@ -2705,7 +2706,7 @@ public:
Get the token end position in the pre-processed buffer,
with trailing spaces removed.
*/
- const char *get_cpp_tok_end_rtrim()
+ const char *get_cpp_tok_end_rtrim() const
{
const char *p;
for (p= m_cpp_tok_end;
@@ -2716,7 +2717,7 @@ public:
}
/** Get the current stream pointer, in the pre-processed buffer. */
- const char *get_cpp_ptr()
+ const char *get_cpp_ptr() const
{
return m_cpp_ptr;
}
@@ -2725,7 +2726,7 @@ public:
Get the current stream pointer, in the pre-processed buffer,
with traling spaces removed.
*/
- const char *get_cpp_ptr_rtrim()
+ const char *get_cpp_ptr_rtrim() const
{
const char *p;
for (p= m_cpp_ptr;
@@ -2735,13 +2736,13 @@ public:
return p;
}
/** Get the utf8-body string. */
- const char *get_body_utf8_str()
+ const char *get_body_utf8_str() const
{
return m_body_utf8;
}
/** Get the utf8-body length. */
- size_t get_body_utf8_length()
+ size_t get_body_utf8_length() const
{
return (size_t) (m_body_utf8_ptr - m_body_utf8);
}
@@ -2777,7 +2778,7 @@ private:
bool consume_comment(int remaining_recursions_permitted);
int lex_one_token(union YYSTYPE *yylval, THD *thd);
- int find_keyword(Lex_ident_cli_st *str, uint len, bool function);
+ int find_keyword(Lex_ident_cli_st *str, uint len, bool function) const;
LEX_CSTRING get_token(uint skip, uint length);
int scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str);
int scan_ident_start(THD *thd, Lex_ident_cli_st *str);
@@ -3658,7 +3659,7 @@ public:
bool can_be_merged();
bool can_use_merged();
- bool can_not_use_merged(bool no_update_or_delete);
+ bool can_not_use_merged();
bool only_view_structure();
bool need_correct_ident();
uint8 get_effective_with_check(TABLE_LIST *view);
@@ -4130,9 +4131,6 @@ public:
Item *create_item_query_expression(THD *thd, st_select_lex_unit *unit);
- Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
- Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
- Item *make_item_func_substr(THD *thd, Item *a, Item *b);
Item *make_item_func_sysdate(THD *thd, uint fsp);
Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db,
Lex_ident_cli_st *name, List<Item> *args);
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index a304cd39df7..76260ec51e7 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -234,6 +234,7 @@
#define OPTIMIZER_SWITCH_USE_ROWID_FILTER (1ULL << 33)
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING (1ULL << 34)
#define OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN (1ULL << 35)
+#define OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY (1ULL << 36)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
diff --git a/sql/sql_schema.cc b/sql/sql_schema.cc
index 0bf4a63c2f8..f08204d272d 100644
--- a/sql/sql_schema.cc
+++ b/sql/sql_schema.cc
@@ -32,6 +32,14 @@ public:
return thd->type_handler_for_datetime();
return src;
}
+
+ Item *make_item_func_replace(THD *thd,
+ Item *subj,
+ Item *find,
+ Item *replace) const;
+ Item *make_item_func_substr(THD *thd,
+ const Lex_substring_spec_st &spec) const;
+ Item *make_item_func_trim(THD *thd, const Lex_trim_st &spec) const;
};
@@ -78,3 +86,56 @@ Schema *Schema::find_implied(THD *thd)
return &maxdb_schema;
return &mariadb_schema;
}
+
+
+Item *Schema::make_item_func_replace(THD *thd,
+ Item *subj,
+ Item *find,
+ Item *replace) const
+{
+ return new (thd->mem_root) Item_func_replace(thd, subj, find, replace);
+}
+
+
+Item *Schema::make_item_func_substr(THD *thd,
+ const Lex_substring_spec_st &spec) const
+{
+ return spec.m_for ?
+ new (thd->mem_root) Item_func_substr(thd, spec.m_subject, spec.m_from,
+ spec.m_for) :
+ new (thd->mem_root) Item_func_substr(thd, spec.m_subject, spec.m_from);
+}
+
+
+Item *Schema::make_item_func_trim(THD *thd, const Lex_trim_st &spec) const
+{
+ return spec.make_item_func_trim_std(thd);
+}
+
+
+Item *Schema_oracle::make_item_func_replace(THD *thd,
+ Item *subj,
+ Item *find,
+ Item *replace) const
+{
+ return new (thd->mem_root) Item_func_replace_oracle(thd, subj, find, replace);
+}
+
+
+Item *Schema_oracle::make_item_func_substr(THD *thd,
+ const Lex_substring_spec_st &spec) const
+{
+ return spec.m_for ?
+ new (thd->mem_root) Item_func_substr_oracle(thd, spec.m_subject,
+ spec.m_from,
+ spec.m_for) :
+ new (thd->mem_root) Item_func_substr_oracle(thd, spec.m_subject,
+ spec.m_from);
+}
+
+
+Item *Schema_oracle::make_item_func_trim(THD *thd,
+ const Lex_trim_st &spec) const
+{
+ return spec.make_item_func_trim_oracle(thd);
+}
diff --git a/sql/sql_schema.h b/sql/sql_schema.h
index 37f8ceb7250..0258ff2dc97 100644
--- a/sql/sql_schema.h
+++ b/sql/sql_schema.h
@@ -33,6 +33,17 @@ public:
{
return src;
}
+
+ // Builders for native SQL function with a special syntax in sql_yacc.yy
+ virtual Item *make_item_func_replace(THD *thd,
+ Item *subj,
+ Item *find,
+ Item *replace) const;
+ virtual Item *make_item_func_substr(THD *thd,
+ const Lex_substring_spec_st &spec) const;
+
+ virtual Item *make_item_func_trim(THD *thd, const Lex_trim_st &spec) const;
+
/*
For now we have *hard-coded* compatibility schemas:
schema_mariadb, schema_oracle, schema_maxdb.
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index eda833b89b0..b00c7de9cbc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5814,7 +5814,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
/*
Perform range analysis if there are keys it could use (1).
Don't do range analysis for materialized subqueries (2).
- Don't do range analysis for materialized derived tables (3)
+ Don't do range analysis for materialized derived tables/views (3)
*/
if ((!s->const_keys.is_clear_all() ||
!bitmap_is_clear_all(&s->table->cond_set)) && // (1)
@@ -7573,6 +7573,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].records_read=1.0; /* This is a const table */
join->positions[idx].cond_selectivity= 1.0;
join->positions[idx].ref_depend_map= 0;
+ join->positions[idx].partial_join_cardinality= 1;
// join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
join->positions[idx].sj_strategy= SJ_OPT_NONE;
@@ -7590,6 +7591,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
}
join->best_ref[idx]=table;
join->positions[idx].spl_plan= 0;
+ join->positions[idx].spl_pd_boundary= 0;
}
@@ -7597,20 +7599,28 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
Estimate how many records we will get if we read just this table and apply
a part of WHERE that can be checked for it.
+ @param s Current JOIN_TAB
+ @param use_cond_selectivity Value of optimizer_use_condition_selectivity.
+ If > 1 then use table->cond_selecitivity.
+ @param force_estiamte Set to 1 if we should not call
+ use_found_constraint. To be deleted in 11.0
+ @return 0.0 No matching rows
+ @return >= 1.0 Number of expected matching rows
+
@detail
Estimate how many records we will get if we
- read the given table with its "independent" access method (either quick
select or full table/index scan),
- apply the part of WHERE that refers only to this table.
- @seealso
+ @see also
table_cond_selectivity() produces selectivity of condition that is checked
after joining rows from this table to rows from preceding tables.
*/
-inline
-double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
- uint use_cond_selectivity)
+static double apply_selectivity_for_table(JOIN_TAB *s,
+ uint use_cond_selectivity,
+ bool *force_estimate)
{
ha_rows records;
double dbl_records;
@@ -7621,34 +7631,47 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
double sel= table->cond_selectivity;
double table_records= rows2double(s->records);
dbl_records= table_records * sel;
+ *force_estimate= 1; // Don't call use_found_constraint()
return dbl_records;
}
records = s->found_records;
/*
- If there is a filtering condition on the table (i.e. ref analyzer found
- at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
- preceding this table in the join order we're now considering), then
- assume that 25% of the rows will be filtered out by this condition.
-
- This heuristic is supposed to force tables used in exprZ to be before
- this table in join order.
+ If applicable, get a more accurate estimate.
*/
- if (with_found_constraint)
- records-= records/4;
-
- /*
- If applicable, get a more accurate estimate. Don't use the two
- heuristics at once.
- */
+ DBUG_ASSERT(s->table->opt_range_condition_rows <= s->found_records);
if (s->table->opt_range_condition_rows != s->found_records)
+ {
+ *force_estimate= 1; // Don't call use_found_constraint()
records= s->table->opt_range_condition_rows;
+ }
dbl_records= (double)records;
return dbl_records;
}
+/*
+ Take into account that the table's WHERE clause has conditions on earlier
+ tables that can reduce the number of accepted rows.
+
+ @param records Number of original rows (after selectivity)
+
+ If there is a filtering condition on the table (i.e. ref analyzer found
+ at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
+ preceding this table in the join order we're now considering), then
+ assume that 25% of the rows will be filtered out by this condition.
+
+ This heuristic is supposed to force tables used in exprZ to be before
+ this table in join order.
+*/
+
+inline double use_found_constraint(double records)
+{
+ records-= records/4;
+ return records;
+}
+
/*
Calculate the cost of reading a set of rows trough an index
@@ -7705,6 +7728,92 @@ double adjust_quick_cost(double quick_cost, ha_rows records)
}
+/*
+ @brief
+ Compute the fanout of hash join operation using EITS data
+*/
+
+double hash_join_fanout(JOIN *join, JOIN_TAB *s, table_map remaining_tables,
+ double rnd_records, KEYUSE *hj_start_key,
+ bool *stats_found)
+{
+ THD *thd= join->thd;
+ /*
+ Before doing the hash join, we will scan the table and apply the local part
+ of the WHERE condition. This will produce rnd_records.
+
+ The EITS statistics describes the entire table. Calling
+
+ table->field[N]->get_avg_frequency()
+
+ produces average #rows in the table with some value.
+
+ What happens if we filter out rows so that rnd_records rows are left?
+ Something between the two outcomes:
+ A. filtering removes a fraction of rows for each value:
+ avg_frequency=avg_frequency * condition_selectivity
+
+ B. filtering removes entire groups of rows with the same value, but
+ the remaining groups remain of the same size.
+
+ We make pessimistic assumption and assume B.
+ We also handle an edge case: if rnd_records is less than avg_frequency,
+ assume we'll get rnd_records rows with the same value, and return
+ rnd_records as the fanout estimate.
+ */
+ double min_freq= rnd_records;
+
+ Json_writer_object trace_obj(thd, "hash_join_cardinality");
+ /*
+ There can be multiple KEYUSE referring to same or different columns
+
+ KEYUSE(tbl.col1 = ...)
+ KEYUSE(tbl.col1 = ...)
+ KEYUSE(tbl.col2 = ...)
+
+ Hash join code can use multiple columns: (col1, col2) for joining.
+ We need n_distinct({col1, col2}).
+
+ EITS only has statistics on individual columns: n_distinct(col1),
+ n_distinct(col2).
+
+ Our current solution is to be very conservative and use selectivity
+ of one column with the lowest avg_frequency.
+
+ In the future, we should an approach that cautiosly takes into account
+ multiple KEYUSEs either multiply by number of equalities or by sqrt
+ of the second most selective equality.
+ */
+ Json_writer_array trace_arr(thd, "hash_join_columns");
+ for (KEYUSE *keyuse= hj_start_key;
+ keyuse->table == s->table && is_hash_join_key_no(keyuse->key);
+ keyuse++)
+ {
+ if (!(remaining_tables & keyuse->used_tables) &&
+ (!keyuse->validity_ref || *keyuse->validity_ref) &&
+ s->access_from_tables_is_allowed(keyuse->used_tables,
+ join->sjm_lookup_tables))
+ {
+ Field *field= s->table->field[keyuse->keypart];
+ if (is_eits_usable(field))
+ {
+ double freq= field->read_stats->get_avg_frequency();
+
+ Json_writer_object trace_field(thd);
+ trace_field.add("field",field->field_name.str).
+ add("avg_frequency", freq);
+ if (freq < min_freq)
+ min_freq= freq;
+ *stats_found= 1;
+ }
+ }
+ }
+ trace_arr.end();
+ trace_obj.add("rows", min_freq);
+ return min_freq;
+}
+
+
/**
Find the best access path for an extension of a partial execution
plan and add this path to the plan.
@@ -7769,6 +7878,7 @@ best_access_path(JOIN *join,
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
+ table_map spl_pd_boundary= 0;
Range_rowid_filter_cost_info *filter= 0;
const char* cause= NULL;
enum join_type best_type= JT_UNKNOWN, type= JT_UNKNOWN;
@@ -7785,9 +7895,11 @@ best_access_path(JOIN *join,
loose_scan_opt.init(join, s, remaining_tables);
if (s->table->is_splittable())
- spl_plan= s->choose_best_splitting(record_count, remaining_tables);
- Json_writer_array trace_paths(thd, "considered_access_paths");
+ spl_plan= s->choose_best_splitting(idx,
+ remaining_tables,
+ &spl_pd_boundary);
+ Json_writer_array trace_paths(thd, "considered_access_paths");
if (s->keyuse)
{ /* Use key if possible */
KEYUSE *keyuse;
@@ -8399,12 +8511,46 @@ best_access_path(JOIN *join,
(!(s->table->map & join->outer_join) ||
join->allowed_outer_join_with_cache)) // (2)
{
+ double fanout;
Json_writer_object trace_access_hash(thd);
- double join_sel= 0.1;
+ trace_access_hash.add("type", "hash");
+ trace_access_hash.add("index", "hj-key");
+ double join_sel;
+ bool stats_found= 0, force_estimate= 0;
/* Estimate the cost of the hash join access to the table */
- double rnd_records= matching_candidates_in_table(s, found_constraint,
- use_cond_selectivity);
+ double rnd_records= apply_selectivity_for_table(s, use_cond_selectivity,
+ &force_estimate);
+
+ DBUG_ASSERT(hj_start_key);
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY))
+ {
+ /*
+ Starting from this point, rnd_records should not be used anymore.
+ Use "fanout" for an estimate of # matching records.
+ */
+ fanout= hash_join_fanout(join, s, remaining_tables, rnd_records,
+ hj_start_key, &stats_found);
+ join_sel= 1.0; // Don't do the "10% heuristic"
+ if (stats_found)
+ goto fanout_computed;
+ }
+
+ /*
+ No OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY or no field statistics
+ found.
+
+ Take into account if there is non constant constraints used with
+ earlier tables in the where expression.
+ If yes, this will set fanout to rnd_records/4.
+ We estimate that there will be HASH_FANOUT (10%)
+ hash matches / row.
+ */
+ if (found_constraint && !force_estimate)
+ rnd_records= use_found_constraint(rnd_records);
+ fanout= rnd_records;
+ join_sel= 0.1;
+ fanout_computed:
tmp= s->quick ? s->quick->read_time : s->scan_time();
double cmp_time= (s->records - rnd_records)/TIME_FOR_COMPARE;
tmp= COST_ADD(tmp, cmp_time);
@@ -8415,19 +8561,36 @@ best_access_path(JOIN *join,
record_count /
(double) thd->variables.join_buff_size));
tmp= COST_MULT(tmp, refills);
- best_time= COST_ADD(tmp,
- COST_MULT((record_count*join_sel) / TIME_FOR_COMPARE,
- rnd_records));
+
+ // Add cost of reading/writing the join buffer
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY))
+ {
+ /* Set it to be 1/10th of TIME_FOR_COMPARE */
+ double row_copy_cost= 1.0 / (10*TIME_FOR_COMPARE);
+ double join_buffer_operations=
+ COST_ADD(
+ COST_MULT(record_count, row_copy_cost),
+ COST_MULT(record_count, fanout * (idx - join->const_tables))
+ );
+ double jbuf_use_cost= row_copy_cost * join_buffer_operations;
+ trace_access_hash.add("jbuf_use_cost", jbuf_use_cost);
+ tmp= COST_ADD(tmp, jbuf_use_cost);
+ }
+
+ double where_cost= COST_MULT((fanout*join_sel) / TIME_FOR_COMPARE,
+ record_count);
+ trace_access_hash.add("extra_cond_check_cost", where_cost);
+
+ best_time= COST_ADD(tmp, where_cost);
+
best= tmp;
- records= rnd_records;
+ records= fanout;
best_key= hj_start_key;
best_ref_depends_map= 0;
best_uses_jbuf= TRUE;
best_filter= 0;
best_type= JT_HASH;
- trace_access_hash.add("type", "hash");
- trace_access_hash.add("index", "hj-key");
- trace_access_hash.add("cost", rnd_records);
+ trace_access_hash.add("records", records);
trace_access_hash.add("cost", best);
trace_access_hash.add("chosen", true);
}
@@ -8479,9 +8642,13 @@ best_access_path(JOIN *join,
!(s->table->force_index && best_key && !s->quick) && // (4)
!(best_key && s->table->pos_in_table_list->jtbm_subselect)) // (5)
{ // Check full join
- double rnd_records= matching_candidates_in_table(s, found_constraint,
- use_cond_selectivity);
-
+ bool force_estimate= 0;
+ double rnd_records= apply_selectivity_for_table(s,
+ use_cond_selectivity,
+ &force_estimate);
+ rnd_records= ((found_constraint && !force_estimate) ?
+ use_found_constraint(rnd_records) :
+ rnd_records);
/*
Range optimizer never proposes a RANGE if it isn't better
than FULL: so if RANGE is present, it's always preferred to FULL.
@@ -8615,8 +8782,9 @@ best_access_path(JOIN *join,
best_filter= filter;
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
- best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
- join->outer_join)));
+ best_uses_jbuf= MY_TEST(!disable_jbuf &&
+ (join->allowed_outer_join_with_cache ||
+ !(s->table->map & join->outer_join)));
spl_plan= 0;
best_type= type;
}
@@ -8639,6 +8807,7 @@ best_access_path(JOIN *join,
pos->loosescan_picker.loosescan_key= MAX_KEY;
pos->use_join_buffer= best_uses_jbuf;
pos->spl_plan= spl_plan;
+ pos->spl_pd_boundary= !spl_plan ? 0 : spl_pd_boundary;
pos->range_rowid_filter_info= best_filter;
pos->key_dependent= (best_type == JT_EQ_REF ? (table_map) 0 :
key_dependent & remaining_tables);
@@ -9172,6 +9341,9 @@ optimize_straight_join(JOIN *join, table_map remaining_tables)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
remaining_tables);
position->cond_selectivity= pushdown_cond_selectivity;
+ double partial_join_cardinality= record_count *
+ pushdown_cond_selectivity;
+ join->positions[idx].partial_join_cardinality= partial_join_cardinality;
++idx;
}
@@ -9698,7 +9870,7 @@ double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
with previous tables.
For quick selects and full table scans, selectivity of COND(this_table)
- is accounted for in matching_candidates_in_table(). Here, we only count
+ is accounted for in apply_selectivity_for_table(). Here, we only count
selectivity of COND(this_table, previous_tables).
For other access methods, we need to calculate selectivity of the whole
@@ -9900,7 +10072,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
/*
The table is accessed with full table scan, or quick select.
Selectivity of COND(table) is already accounted for in
- matching_candidates_in_table().
+ apply_selectivity_for_table().
*/
sel= 1;
}
@@ -10258,6 +10430,8 @@ best_extension_by_limited_search(JOIN *join,
}
}
+ join->positions[idx].partial_join_cardinality= partial_join_cardinality;
+
if ((search_depth > 1) && (remaining_tables & ~real_table_bit) &
allowed_tables)
{
@@ -13326,6 +13500,9 @@ uint check_join_cache_usage(JOIN_TAB *tab,
join->return_tab= 0;
+ if (tab->no_forced_join_cache)
+ goto no_join_cache;
+
/*
Don't use join cache if @@join_cache_level==0 or this table is the first
one join suborder (either at top level or inside a bush)
@@ -14294,7 +14471,8 @@ bool JOIN_TAB::preread_init()
DBUG_RETURN(TRUE);
if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) ||
- derived->is_nonrecursive_derived_with_rec_ref())
+ derived->is_nonrecursive_derived_with_rec_ref() ||
+ is_split_derived)
preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
@@ -17762,6 +17940,9 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
reopt_remaining_tables &
~real_table_bit);
}
+ double partial_join_cardinality= rec_count *
+ pushdown_cond_selectivity;
+ join->positions[i].partial_join_cardinality= partial_join_cardinality;
(*outer_rec_count) *= pushdown_cond_selectivity;
if (!rs->emb_sj_nest)
*outer_rec_count= COST_MULT(*outer_rec_count, pos.records_read);
@@ -21376,6 +21557,16 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
DBUG_ENTER("sub_select");
+ if (join_tab->split_derived_to_update && !end_of_records)
+ {
+ table_map tab_map= join_tab->split_derived_to_update;
+ for (uint i= 0; tab_map; i++, tab_map>>= 1)
+ {
+ if (tab_map & 1)
+ join->map2table[i]->preread_init_done= false;
+ }
+ }
+
if (join_tab->last_inner)
{
JOIN_TAB *last_inner_tab= join_tab->last_inner;
@@ -24906,7 +25097,7 @@ JOIN_TAB::remove_duplicates()
if (!(sortorder= (SORT_FIELD*) my_malloc(PSI_INSTRUMENT_ME,
(fields->elements+1) *
sizeof(SORT_FIELD),
- MYF(MY_WME))))
+ MYF(MY_WME | MY_ZEROFILL))))
DBUG_RETURN(TRUE);
/* Calculate how many saved fields there is in list */
@@ -24925,7 +25116,6 @@ JOIN_TAB::remove_duplicates()
else
{
/* Item is not stored in temporary table, remember it */
- sorder->field= 0; // Safety, not used
sorder->item= item;
/* Calculate sorder->length */
item->type_handler()->sort_length(thd, item, sorder);
@@ -28739,7 +28929,7 @@ void st_select_lex::print_item_list(THD *thd, String *str,
outer_select() can not be used here because it is for name resolution
and will return NULL at any end of name resolution chain (view/derived)
*/
- bool top_level= (get_master() == &thd->lex->unit);
+ bool top_level= is_query_topmost(thd);
List_iterator_fast<Item> it(item_list);
Item *item;
while ((item= it++))
@@ -28846,7 +29036,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
return;
}
- bool top_level= (get_master() == &thd->lex->unit);
+ bool top_level= is_query_topmost(thd);
enum explainable_cmd_type sel_type= SELECT_CMD;
if (top_level)
sel_type= get_explainable_cmd_type(thd);
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 043c414d016..88e6fd4e30f 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -398,6 +398,8 @@ typedef struct st_join_table {
*/
bool idx_cond_fact_out;
bool use_join_cache;
+ /* 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;
@@ -524,6 +526,16 @@ typedef struct st_join_table {
bool preread_init_done;
+ /* true <=> split optimization has been applied to this materialized table */
+ bool is_split_derived;
+
+ /*
+ Bitmap of split materialized derived tables that can be filled just before
+ this join table is to be joined. All parameters of the split derived tables
+ belong to tables preceding this join table.
+ */
+ table_map split_derived_to_update;
+
/*
Cost info to the range filter used when joining this join table
(Defined when the best join order has been already chosen)
@@ -686,9 +698,10 @@ typedef struct st_join_table {
void partial_cleanup();
void add_keyuses_for_splitting();
- SplM_plan_info *choose_best_splitting(double record_count,
- table_map remaining_tables);
- bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables,
+ SplM_plan_info *choose_best_splitting(uint idx,
+ table_map remaining_tables,
+ table_map *spl_pd_boundary);
+ bool fix_splitting(SplM_plan_info *spl_plan, table_map excluded_tables,
bool is_const_table);
} JOIN_TAB;
@@ -953,9 +966,21 @@ public:
*/
KEYUSE *key;
+ /* Cardinality of current partial join ending with this position */
+ double partial_join_cardinality;
+
/* Info on splitting plan used at this position */
SplM_plan_info *spl_plan;
+ /*
+ If spl_plan is NULL the value of spl_pd_boundary is 0. Otherwise
+ spl_pd_boundary contains the bitmap of the table from the current
+ partial join ending at this position that starts the sub-sequence of
+ tables S from which no conditions are allowed to be used in the plan
+ spl_plan for the split table joined at this position.
+ */
+ table_map spl_pd_boundary;
+
/* Cost info for the range filter used at this position */
Range_rowid_filter_cost_info *range_rowid_filter_info;
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index 60da595afd0..fdb9c647727 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -1011,10 +1011,19 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
else
table->file->print_error(error, MYF(0));
seq->write_unlock(table);
- if (trans_commit_stmt(thd))
- error= 1;
- if (trans_commit_implicit(thd))
- error= 1;
+ {
+ wait_for_commit* suspended_wfc= thd->suspend_subsequent_commits();
+ if (trans_commit_stmt(thd))
+ error= 1;
+ if (trans_commit_implicit(thd))
+ error= 1;
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("hold_worker_on_schedule",
+ {
+ /* delay binlogging of a parent trx in rpl_parallel_seq */
+ my_sleep(100000);
+ });
+ }
if (likely(!error))
error= write_bin_log(thd, 1, thd->query(), thd->query_length());
if (likely(!error))
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index be011adb60c..d0b6ac20d1d 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -3901,50 +3901,16 @@ double Histogram::point_selectivity(double pos, double avg_sel)
}
else
{
- /*
+ /*
The value 'pos' fits within one single histogram bucket.
- Histogram buckets have the same numbers of rows, but they cover
- different ranges of values.
-
- We assume that values are uniformly distributed across the [0..1] value
- range.
- */
-
- /*
- If all buckets covered value ranges of the same size, the width of
- value range would be:
+ We also have avg_sel which is per-table average selectivity of col=const.
+ If there are popular values, this may be larger than one bucket, so
+ cap the returned number by the selectivity of one bucket.
*/
double avg_bucket_width= 1.0 / (get_width() + 1);
-
- /*
- Let's see what is the width of value range that our bucket is covering.
- (min==max currently. they are kept in the formula just in case we
- will want to extend it to handle multi-bucket case)
- */
- double inv_prec_factor= (double) 1.0 / prec_factor();
- double current_bucket_width=
- (max + 1 == get_width() ? 1.0 : (get_value(max) * inv_prec_factor)) -
- (min == 0 ? 0.0 : (get_value(min-1) * inv_prec_factor));
-
- DBUG_ASSERT(current_bucket_width); /* We shouldn't get a one zero-width bucket */
-
- /*
- So:
- - each bucket has the same #rows
- - values are unformly distributed across the [min_value,max_value] domain.
- If a bucket has value range that's N times bigger then average, than
- each value will have to have N times fewer rows than average.
- */
- sel= avg_sel * avg_bucket_width / current_bucket_width;
-
- /*
- (Q: if we just follow this proportion we may end up in a situation
- where number of different values we expect to find in this bucket
- exceeds the number of rows that this histogram has in a bucket. Are
- we ok with this or we would want to have certain caps?)
- */
+ sel= MY_MIN(avg_bucket_width, avg_sel);
}
return sel;
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index e3bfe37b172..a4da5c48b6d 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1808,7 +1808,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
if (view_is_mergeable &&
(table->select_lex->master_unit() != &old_lex->unit ||
old_lex->can_use_merged()) &&
- !old_lex->can_not_use_merged(0))
+ !old_lex->can_not_use_merged())
{
/* lex should contain at least one table */
DBUG_ASSERT(view_main_select_tables != 0);
@@ -1841,8 +1841,11 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
*/
if (!table->select_lex->master_unit()->is_unit_op() &&
table->select_lex->order_list.elements == 0)
+ {
table->select_lex->order_list.
push_back(&lex->first_select_lex()->order_list);
+ lex->first_select_lex()->order_list.empty();
+ }
else
{
if (old_lex->sql_command == SQLCOM_SELECT &&
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index be1acd1f5ba..65aec5feb35 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -226,6 +226,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)()
Lex_for_loop_bounds_st for_loop_bounds;
Lex_trim_st trim;
Json_table_column::On_response json_on_response;
+ Lex_substring_spec_st substring_spec;
vers_history_point_t vers_history_point;
struct
{
@@ -636,7 +637,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> RELEASE_SYM /* SQL-2003-R */
%token <kwd> RENAME
%token <kwd> REPEAT_SYM /* MYSQL-FUNC */
-%token <kwd> REPLACE /* MYSQL-FUNC */
%token <kwd> REQUIRE_SYM
%token <kwd> RESIGNAL_SYM /* SQL-2003-R */
%token <kwd> RESTRICT
@@ -673,7 +673,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> STDDEV_SAMP_SYM /* SQL-2003-N */
%token <kwd> STD_SYM
%token <kwd> STRAIGHT_JOIN
-%token <kwd> SUBSTRING /* SQL-2003-N */
%token <kwd> SUM_SYM /* SQL-2003-N */
%token <kwd> SYSDATE
%token <kwd> TABLE_REF_PRIORITY
@@ -686,7 +685,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> TO_SYM /* SQL-2003-R */
%token <kwd> TRAILING /* SQL-2003-R */
%token <kwd> TRIGGER_SYM /* SQL-2003-R */
-%token <kwd> TRIM /* SQL-2003-N */
%token <kwd> TRUE_SYM /* SQL-2003-R */
%token <kwd> UNDO_SYM /* FUTURE-USE */
%token <kwd> UNION_SYM /* SQL-2003-R */
@@ -732,6 +730,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> ROWNUM_SYM /* Oracle-R */
/*
+ SQL functions with a special syntax
+*/
+%token <kwd> REPLACE /* MYSQL-FUNC */
+%token <kwd> SUBSTRING /* SQL-2003-N */
+%token <kwd> TRIM /* SQL-2003-N */
+
+
+/*
Non-reserved keywords
*/
@@ -1762,6 +1768,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <for_loop> sp_for_loop_index_and_bounds
%type <for_loop_bounds> sp_for_loop_bounds
%type <trim> trim_operands
+%type <substring_spec> substring_operands
%type <num> opt_sp_for_loop_direction
%type <spvar_mode> sp_parameter_type
%type <index_hint> index_hint_type
@@ -10276,7 +10283,8 @@ function_call_keyword:
}
| TRIM '(' trim_operands ')'
{
- if (unlikely(!($$= $3.make_item_func_trim(thd))))
+ if (unlikely(!($$= Schema::find_implied(thd)->
+ make_item_func_trim(thd, $3))))
MYSQL_YYABORT;
}
| USER_SYM '(' ')'
@@ -10295,6 +10303,26 @@ function_call_keyword:
}
;
+substring_operands:
+ expr ',' expr ',' expr
+ {
+ $$= Lex_substring_spec_st::init($1, $3, $5);
+ }
+ | expr ',' expr
+ {
+ $$= Lex_substring_spec_st::init($1, $3);
+ }
+ | expr FROM expr FOR_SYM expr
+ {
+ $$= Lex_substring_spec_st::init($1, $3, $5);
+ }
+ | expr FROM expr
+ {
+ $$= Lex_substring_spec_st::init($1, $3);
+ }
+ ;
+
+
/*
Function calls using non reserved keywords, with special syntaxic forms.
Dedicated grammar rules are needed because of the syntax,
@@ -10427,24 +10455,10 @@ function_call_nonkeyword:
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | SUBSTRING '(' expr ',' expr ',' expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr ',' expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr FROM expr ')'
+ | SUBSTRING '(' substring_operands ')'
{
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
+ if (unlikely(!($$= Schema::find_implied(thd)->
+ make_item_func_substr(thd, $3))))
MYSQL_YYABORT;
}
%ifdef ORACLE
@@ -10659,7 +10673,8 @@ function_call_conflict:
}
| REPLACE '(' expr ',' expr ',' expr ')'
{
- if (unlikely(!($$= Lex->make_item_func_replace(thd, $3, $5, $7))))
+ if (unlikely(!($$= Schema::find_implied(thd)->
+ make_item_func_replace(thd, $3, $5, $7))))
MYSQL_YYABORT;
}
| REVERSE_SYM '(' expr ')'
diff --git a/sql/structs.h b/sql/structs.h
index 42894fa4cc7..0e4ca6236fe 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -793,6 +793,11 @@ public:
}
Item *make_item_func_trim_std(THD *thd) const;
Item *make_item_func_trim_oracle(THD *thd) const;
+ /*
+ This method is still used to handle LTRIM and RTRIM,
+ while the special syntax TRIM(... BOTH|LEADING|TRAILING)
+ is now handled by Schema::make_item_func_trim().
+ */
Item *make_item_func_trim(THD *thd) const;
};
@@ -804,6 +809,25 @@ public:
};
+class Lex_substring_spec_st
+{
+public:
+ Item *m_subject;
+ Item *m_from;
+ Item *m_for;
+ static Lex_substring_spec_st init(Item *subject,
+ Item *from,
+ Item *xfor= NULL)
+ {
+ Lex_substring_spec_st res;
+ res.m_subject= subject;
+ res.m_from= from;
+ res.m_for= xfor;
+ return res;
+ }
+};
+
+
class st_select_lex;
class Lex_select_lock
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index ffa301df12b..29cd5809ea1 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -602,10 +602,9 @@ bool check_has_super(sys_var *self, THD *thd, set_var *var)
return false;
}
-static Sys_var_bit Sys_core_file("core_file", "write a core-file on crashes",
- READ_ONLY GLOBAL_VAR(test_flags), NO_CMD_LINE,
- TEST_CORE_ON_SIGNAL, DEFAULT(IF_WIN(TRUE,FALSE)), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- 0,0,0);
+static Sys_var_bit Sys_core_file("core_file", "Write core on crashes",
+ READ_ONLY GLOBAL_VAR(test_flags), CMD_LINE(OPT_ARG),
+ TEST_CORE_ON_SIGNAL, DEFAULT(IF_WIN(TRUE,FALSE)));
static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
{
@@ -2757,6 +2756,7 @@ export const char *optimizer_switch_names[]=
"rowid_filter",
"condition_pushdown_from_having",
"not_null_range_scan",
+ "hash_join_cardinality",
"default",
NullS
};
diff --git a/sql/table.cc b/sql/table.cc
index 26b13debc95..1a4875bde81 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3463,7 +3463,6 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
char *sql_copy;
handler *file;
LEX *old_lex;
- Query_arena *arena, backup;
LEX tmp_lex;
KEY *unused1;
uint unused2;
@@ -3490,12 +3489,6 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
old_lex= thd->lex;
thd->lex= &tmp_lex;
- arena= thd->stmt_arena;
- if (arena->is_conventional())
- arena= 0;
- else
- thd->set_n_backup_active_arena(arena, &backup);
-
thd->reset_db(&db);
lex_start(thd);
@@ -3530,8 +3523,6 @@ ret:
lex_end(thd->lex);
thd->reset_db(&db_backup);
thd->lex= old_lex;
- if (arena)
- thd->restore_active_arena(arena, &backup);
reenable_binlog(thd);
thd->variables.character_set_client= old_cs;
if (unlikely(thd->is_error() || error))
@@ -6743,7 +6734,7 @@ void TABLE_LIST::set_check_materialized()
DBUG_ENTER("TABLE_LIST::set_check_materialized");
SELECT_LEX_UNIT *derived= this->derived;
if (view)
- derived= &view->unit;
+ derived= this->derived= &view->unit;
DBUG_ASSERT(derived);
DBUG_ASSERT(!derived->is_excluded());
if (!derived->first_select()->exclude_from_table_unique_test)
@@ -9468,8 +9459,13 @@ void TABLE_LIST::wrap_into_nested_join(List<TABLE_LIST> &join_list)
static inline bool derived_table_optimization_done(TABLE_LIST *table)
{
- return table->derived &&
- (table->derived->is_excluded() ||
+ SELECT_LEX_UNIT *derived= (table->derived ?
+ table->derived :
+ (table->view ?
+ &table->view->unit:
+ NULL));
+ return derived &&
+ (derived->is_excluded() ||
table->is_materialized_derived());
}
@@ -9531,18 +9527,29 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
set_derived();
}
- if (is_view() ||
- !derived_table_optimization_done(this))
+ if (!derived_table_optimization_done(this))
{
/* A subquery might be forced to be materialized due to a side-effect. */
if (!is_materialized_derived() && unit->can_be_merged() &&
- (unit->outer_select() && !unit->outer_select()->with_rownum) &&
+ /*
+ Following is special case of
+ SELECT * FROM (<limited-select>) WHERE ROWNUM() <= nnn
+ */
+ (unit->outer_select() &&
+ !(unit->outer_select()->with_rownum &&
+ unit->outer_select()->table_list.elements == 1 &&
+ (thd->lex->sql_command == SQLCOM_SELECT ||
+ !unit->outer_select()->is_query_topmost(thd)) &&
+ !is_view())) &&
+
(!thd->lex->with_rownum ||
(!first_select->group_list.elements &&
!first_select->order_list.elements)) &&
(is_view() ||
- (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
- !thd->lex->can_not_use_merged(1))) &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)) &&
+ !thd->lex->can_not_use_merged() &&
+ !((thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI) && !is_view()) &&
!is_recursive_with_table())
set_merged_derived();
else
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
index f39588e41db..a19ea688fd6 100644
--- a/sql/wsrep_schema.cc
+++ b/sql/wsrep_schema.cc
@@ -1016,10 +1016,9 @@ int Wsrep_schema::append_fragment(THD* thd,
Wsrep_schema_impl::store(frag_table, 3, flags);
Wsrep_schema_impl::store(frag_table, 4, data.data(), data.size());
- int error;
- if ((error= Wsrep_schema_impl::insert(frag_table))) {
- WSREP_ERROR("Failed to write to frag table: %d", error);
+ if (Wsrep_schema_impl::insert(frag_table)) {
trans_rollback_stmt(thd);
+ close_thread_tables(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1);
}
diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
index 3d1bf3733a8..0ce612d6097 100644
--- a/sql/wsrep_thd.h
+++ b/sql/wsrep_thd.h
@@ -228,7 +228,14 @@ static inline void wsrep_override_error(THD* thd,
break;
case wsrep::e_append_fragment_error:
/* TODO: Figure out better error number */
- wsrep_override_error(thd, ER_ERROR_DURING_COMMIT, 0, status);
+ if (status)
+ wsrep_override_error(thd, ER_ERROR_DURING_COMMIT,
+ "Error while appending streaming replication fragment"
+ "(provider status: %s)",
+ wsrep::provider::to_string(status).c_str());
+ else
+ wsrep_override_error(thd, ER_ERROR_DURING_COMMIT,
+ "Error while appending streaming replication fragment");
break;
case wsrep::e_not_supported_error:
wsrep_override_error(thd, ER_NOT_SUPPORTED_YET);