summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2016-09-19 09:47:08 +0200
committerSergei Golubchik <serg@mariadb.org>2016-09-19 09:47:08 +0200
commitf9bdc7c01af52c04a05b5d0e890f86c77323d3b0 (patch)
tree09779236c1d7061fb1706524c2e3b357369eae8e /sql
parentf7be8cf2854fc0c2871c1537e60b1d7cb1931a61 (diff)
parentf566a4f83c8c255e0192afa525fdeb0897927167 (diff)
downloadmariadb-git-f9bdc7c01af52c04a05b5d0e890f86c77323d3b0.tar.gz
Merge branch '10.2' into bb-10.2-jan
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt4
-rw-r--r--sql/contributors.h21
-rw-r--r--sql/field.cc127
-rw-r--r--sql/field.h72
-rw-r--r--sql/handler.cc47
-rw-r--r--sql/handler.h1
-rw-r--r--sql/item.cc331
-rw-r--r--sql/item.h128
-rw-r--r--sql/item_cmpfunc.cc37
-rw-r--r--sql/item_cmpfunc.h104
-rw-r--r--sql/item_func.cc21
-rw-r--r--sql/item_func.h169
-rw-r--r--sql/item_geofunc.h69
-rw-r--r--sql/item_inetfunc.h16
-rw-r--r--sql/item_row.cc18
-rw-r--r--sql/item_row.h3
-rw-r--r--sql/item_strfunc.cc91
-rw-r--r--sql/item_strfunc.h193
-rw-r--r--sql/item_subselect.cc55
-rw-r--r--sql/item_subselect.h17
-rw-r--r--sql/item_sum.cc46
-rw-r--r--sql/item_sum.h61
-rw-r--r--sql/item_timefunc.cc40
-rw-r--r--sql/item_timefunc.h148
-rw-r--r--sql/item_windowfunc.cc43
-rw-r--r--sql/item_windowfunc.h112
-rw-r--r--sql/item_xmlfunc.cc34
-rw-r--r--sql/item_xmlfunc.h4
-rw-r--r--sql/lock.cc95
-rw-r--r--sql/log.cc57
-rw-r--r--sql/log.h1
-rw-r--r--sql/log_event.cc1
-rw-r--r--sql/mdl.cc141
-rw-r--r--sql/multi_range_read.cc2
-rw-r--r--sql/my_json_writer.cc2
-rw-r--r--sql/mysqld.cc173
-rw-r--r--sql/mysqld.h20
-rw-r--r--sql/net_serv.cc98
-rw-r--r--sql/opt_range.cc69
-rw-r--r--sql/opt_range_mrr.cc6
-rw-r--r--sql/opt_subselect.cc9
-rw-r--r--sql/partition_element.h2
-rw-r--r--sql/partition_info.cc158
-rw-r--r--sql/partition_info.h10
-rw-r--r--sql/procedure.h1
-rw-r--r--sql/protocol.cc134
-rw-r--r--sql/rpl_mi.cc7
-rw-r--r--sql/rpl_parallel.cc43
-rw-r--r--sql/rpl_rli.cc6
-rw-r--r--sql/rpl_rli.h7
-rw-r--r--sql/session_tracker.cc1710
-rw-r--r--sql/session_tracker.h304
-rw-r--r--sql/set_var.cc62
-rw-r--r--sql/set_var.h7
-rw-r--r--sql/share/charsets/Index.xml139
-rw-r--r--sql/share/errmsg-utf8.txt56
-rw-r--r--sql/signal_handler.cc24
-rw-r--r--sql/slave.cc168
-rw-r--r--sql/slave.h1
-rw-r--r--sql/sp_head.cc17
-rw-r--r--sql/sql_acl.cc17
-rw-r--r--sql/sql_admin.cc299
-rw-r--r--sql/sql_alter.h2
-rw-r--r--sql/sql_base.cc74
-rw-r--r--sql/sql_cache.cc29
-rw-r--r--sql/sql_class.cc104
-rw-r--r--sql/sql_class.h65
-rw-r--r--sql/sql_cte.cc952
-rw-r--r--sql/sql_cte.h334
-rw-r--r--sql/sql_db.cc14
-rw-r--r--sql/sql_delete.cc4
-rw-r--r--sql/sql_derived.cc316
-rw-r--r--sql/sql_derived.h8
-rw-r--r--sql/sql_explain.cc7
-rw-r--r--sql/sql_explain.h2
-rw-r--r--sql/sql_insert.cc6
-rw-r--r--sql/sql_lex.cc242
-rw-r--r--sql/sql_lex.h70
-rw-r--r--sql/sql_parse.cc130
-rw-r--r--sql/sql_partition.cc139
-rw-r--r--sql/sql_partition.h4
-rw-r--r--sql/sql_plugin.cc177
-rw-r--r--sql/sql_plugin.h8
-rw-r--r--sql/sql_prepare.cc4
-rw-r--r--sql/sql_priv.h4
-rw-r--r--sql/sql_reload.cc6
-rw-r--r--sql/sql_rename.cc34
-rw-r--r--sql/sql_repl.cc12
-rw-r--r--sql/sql_repl.h1
-rw-r--r--sql/sql_select.cc190
-rw-r--r--sql/sql_select.h16
-rw-r--r--sql/sql_show.cc282
-rw-r--r--sql/sql_show.h6
-rw-r--r--sql/sql_string.cc16
-rw-r--r--sql/sql_string.h12
-rw-r--r--sql/sql_table.cc375
-rw-r--r--sql/sql_table.h3
-rw-r--r--sql/sql_union.cc316
-rw-r--r--sql/sql_update.cc8
-rw-r--r--sql/sql_view.cc21
-rw-r--r--sql/sql_window.cc1440
-rw-r--r--sql/sql_window.h37
-rw-r--r--sql/sql_yacc.yy104
-rw-r--r--sql/sys_vars.cc170
-rw-r--r--sql/sys_vars.ic169
-rw-r--r--sql/table.cc572
-rw-r--r--sql/table.h35
-rw-r--r--sql/table_cache.cc374
-rw-r--r--sql/table_cache.h22
-rw-r--r--sql/threadpool_common.cc3
-rw-r--r--sql/transaction.cc95
-rw-r--r--sql/transaction.h2
-rw-r--r--sql/unireg.cc11
-rw-r--r--sql/wsrep_applier.cc10
-rw-r--r--sql/wsrep_binlog.h2
-rw-r--r--sql/wsrep_check_opts.cc2
-rw-r--r--sql/wsrep_hton.cc11
-rw-r--r--sql/wsrep_mysqld.cc248
-rw-r--r--sql/wsrep_mysqld.h10
-rw-r--r--sql/wsrep_notify.cc1
-rw-r--r--sql/wsrep_sst.cc37
-rw-r--r--sql/wsrep_sst.h5
-rw-r--r--sql/wsrep_thd.cc14
-rw-r--r--sql/wsrep_var.cc123
-rw-r--r--sql/wsrep_var.h5
125 files changed, 10600 insertions, 2753 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 089d793b2b0..a18294e5ae3 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -95,7 +95,9 @@ SET (SQL_SOURCE
../sql-common/client_plugin.c
opt_range.cc opt_range.h opt_sum.cc
../sql-common/pack.c parse_file.cc password.c procedure.cc
- protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc
+ protocol.cc records.cc repl_failsafe.cc rpl_filter.cc
+ session_tracker.cc
+ set_var.cc
slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc
sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h
diff --git a/sql/contributors.h b/sql/contributors.h
index 76674d654e5..f52d3243453 100644
--- a/sql/contributors.h
+++ b/sql/contributors.h
@@ -36,17 +36,16 @@ struct show_table_contributors_st {
*/
struct show_table_contributors_st show_table_contributors[]= {
- /* MariaDB foundation members, in contribution, size , time order */
- {"Booking.com", "http://www.booking.com", "Founding member of the MariaDB Foundation"},
- {"MariaDB Corporation", "https://mariadb.com", "Founding member of the MariaDB Foundation"},
- {"Auttomattic", "http://automattic.com", "Member of the MariaDB Foundation"},
- {"Visma", "http://visma.com", "Member of the MariaDB Foundation"},
- {"Nexedi", "http://www.nexedi.com", "Member of the MariaDB Foundation"},
- {"Acronis", "http://www.acronis.com", "Member of the MariaDB Foundation"},
-
- /* Smaller sponsors, newer per year */
- {"Verkkokauppa.com", "Finland", "Sponsor of the MariaDB Foundation"},
- {"Virtuozzo", "https://virtuozzo.com/", "Sponsor of the MariaDB Foundation"},
+ /* MariaDB foundation sponsors, in contribution, size , time order */
+ {"Booking.com", "http://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"},
+ {"MariaDB Corporation", "https://mariadb.com", "Founding member, Gold Sponsor of the MariaDB Foundation"},
+ {"Visma", "http://visma.com", "Gold Sponsor of the MariaDB Foundation"},
+ {"DBS", "http://dbs.com", "Gold Sponsor of the MariaDB Foundation"},
+ {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"},
+ {"Acronis", "http://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"},
+ {"Auttomattic", "https://automattic.com", "Bronze Sponsor of the MariaDB Foundation"},
+ {"Verkkokauppa.com", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"},
+ {"Virtuozzo", "https://virtuozzo.com/", "Bronze Sponsor of the MariaDB Foundation"},
/* Sponsors of important features */
{"Google", "USA", "Sponsoring encryption, parallel replication and GTID"},
diff --git a/sql/field.cc b/sql/field.cc
index bd7f7c3f689..ba0ebb253f4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4920,12 +4920,12 @@ void Field_double::sql_type(String &res) const
field has NOW() as default and is updated when row changes, else it is
field which has 0 as default value and is not automatically updated.
TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update
- automatically (TIMESTAMP DEFAULT NOW())
+ automatically (TIMESTAMP DEFAULT NOW()), not used in Field since 10.2.2
TIMESTAMP_UN_FIELD - field which is set on update automatically but has not
NOW() as default (but it may has 0 or some other const timestamp as
default) (TIMESTAMP ON UPDATE NOW()).
TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on
- update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW())
+ update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW()), not used in Field since 10.2.2
NONE - field which is not auto-set on update with some other than NOW()
default value (TIMESTAMP DEFAULT 0).
@@ -4956,8 +4956,8 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
this field will be automaticly updated on insert.
*/
flags|= TIMESTAMP_FLAG;
- if (unireg_check != TIMESTAMP_DN_FIELD)
- flags|= ON_UPDATE_NOW_FLAG;
+ flags|= ON_UPDATE_NOW_FLAG;
+ DBUG_ASSERT(unireg_check == TIMESTAMP_UN_FIELD);
}
}
@@ -9801,10 +9801,11 @@ bool check_expression(Virtual_column_info *vcol, const char *type,
ret= vcol->expr_item->walk(&Item::check_vcol_func_processor, 0, &res);
vcol->flags= res.errors;
- if (ret ||
- (res.errors &
- (VCOL_IMPOSSIBLE |
- (must_be_determinstic ? VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC: 0))))
+ uint filter= VCOL_IMPOSSIBLE;
+ if (must_be_determinstic)
+ filter|= VCOL_NOT_STRICTLY_DETERMINISTIC;
+
+ if (ret || (res.errors & filter))
{
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
type, name);
@@ -9862,59 +9863,43 @@ bool Column_definition::check(THD *thd)
}
}
}
+
if (default_value && (flags & AUTO_INCREMENT_FLAG))
{
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
DBUG_RETURN(1);
}
- if (default_value && !default_value->expr_item->basic_const_item())
+ if (default_value && !default_value->expr_item->basic_const_item() &&
+ mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME &&
+ default_value->expr_item->type() == Item::FUNC_ITEM)
{
- Item *def_expr= default_value->expr_item;
-
- unireg_check= Field::NONE;
/*
- NOW() for TIMESTAMP and DATETIME fields are handled as in MariaDB 10.1
- by marking them in unireg_check.
+ Special case: NOW() for TIMESTAMP and DATETIME fields are handled
+ as in MariaDB 10.1 by marking them in unireg_check.
*/
- if (def_expr->type() == Item::FUNC_ITEM &&
- (static_cast<Item_func*>(def_expr)->functype() ==
- Item_func::NOW_FUNC &&
- (mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME)))
+ Item_func *fn= static_cast<Item_func*>(default_value->expr_item);
+ if (fn->functype() == Item_func::NOW_FUNC &&
+ (fn->decimals == 0 || fn->decimals >= length))
{
- /*
- We are not checking the number of decimals for timestamps
- to allow one to write (for historical reasons)
- TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP
- Instead we are going to use the number of decimals specifed by the
- column.
- */
default_value= 0;
- unireg_check= (on_update ?
- Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
- Field::TIMESTAMP_DN_FIELD); // only for insertions.
+ unireg_check= Field::TIMESTAMP_DN_FIELD;
}
- else if (on_update)
- unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
- }
- else
- {
- /* No function default for insertions. Either NULL or a constant. */
- if (on_update)
- unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
- else
- unireg_check= ((flags & AUTO_INCREMENT_FLAG) ?
- Field::NEXT_NUMBER : // Automatic increment.
- Field::NONE);
}
- if (on_update &&
- (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME ||
- on_update->decimals < length))
+ if (on_update)
{
- my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
- DBUG_RETURN(TRUE);
+ if (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME ||
+ on_update->decimals < length)
+ {
+ my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
+ DBUG_RETURN(TRUE);
+ }
+ unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD
+ : Field::TIMESTAMP_DNUN_FIELD;
}
+ else if (flags & AUTO_INCREMENT_FLAG)
+ unireg_check= Field::NEXT_NUMBER;
sign_len= flags & UNSIGNED_FLAG ? 0 : 1;
@@ -10497,8 +10482,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
comment= old_field->comment;
decimals= old_field->decimals();
vcol_info= old_field->vcol_info;
- default_value= old_field->default_value;
- check_constraint= old_field->check_constraint;
+ default_value= orig_field ? orig_field->default_value : 0;
+ check_constraint= orig_field ? orig_field->check_constraint : 0;
option_list= old_field->option_list;
switch (sql_type) {
@@ -10576,40 +10561,24 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
- The column didn't have a default expression
*/
if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
- old_field->ptr != NULL &&
- orig_field != NULL &&
- !default_value)
+ old_field->ptr != NULL && orig_field != NULL)
{
- bool default_now= false;
- if (real_type_with_now_as_default(sql_type))
- {
- // The SQL type of the new field allows a function default:
- default_now= orig_field->has_insert_default_function();
- bool update_now= orig_field->has_update_default_function();
-
- if (default_now && update_now)
- unireg_check= Field::TIMESTAMP_DNUN_FIELD;
- else if (default_now)
- unireg_check= Field::TIMESTAMP_DN_FIELD;
- else if (update_now)
- unireg_check= Field::TIMESTAMP_UN_FIELD;
- }
- if (!default_now) // Give a constant default
+ if (orig_field->has_update_default_function())
+ unireg_check= Field::TIMESTAMP_UN_FIELD;
+
+ /* Get the value from default_values */
+ const uchar *dv= orig_field->table->s->default_values;
+ if (!default_value && !orig_field->is_null_in_record(dv))
{
- /* Get the value from default_values */
- const uchar *dv= orig_field->table->s->default_values;
- if (!orig_field->is_null_in_record(dv))
- {
- StringBuffer<MAX_FIELD_WIDTH> tmp(charset);
- String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv));
- char *pos= (char*) thd->strmake(res->ptr(), res->length());
- default_value= new (thd->mem_root) Virtual_column_info();
- default_value->expr_str.str= pos;
- default_value->expr_str.length= res->length();
- default_value->expr_item=
- new (thd->mem_root) Item_string(thd, pos, res->length(), charset);
- default_value->utf8= 0;
- }
+ StringBuffer<MAX_FIELD_WIDTH> tmp(charset);
+ String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv));
+ char *pos= (char*) thd->strmake(res->ptr(), res->length());
+ default_value= new (thd->mem_root) Virtual_column_info();
+ default_value->expr_str.str= pos;
+ default_value->expr_str.length= res->length();
+ default_value->expr_item=
+ new (thd->mem_root) Item_string(thd, pos, res->length(), charset);
+ default_value->utf8= 0;
}
}
}
diff --git a/sql/field.h b/sql/field.h
index 13a0e914845..f550dad1c6c 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -474,20 +474,6 @@ inline bool is_temporal_type_with_date(enum_field_types type)
/**
- Tests if a field real type can have "DEFAULT CURRENT_TIMESTAMP"
-
- @param type Field type, as returned by field->real_type().
- @retval true If field real type can have "DEFAULT CURRENT_TIMESTAMP".
- @retval false If field real type can not have "DEFAULT CURRENT_TIMESTAMP".
-*/
-inline bool real_type_with_now_as_default(enum_field_types type)
-{
- return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2 ||
- type == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_DATETIME2;
-}
-
-
-/**
Recognizer for concrete data type (called real_type for some reason),
returning true if it is one of the TIMESTAMP types.
*/
@@ -574,12 +560,19 @@ inline bool is_temporal_type_with_time(enum_field_types type)
}
}
-/* Bits for type of vcol expression */
-#define VCOL_DETERMINISTIC 0 /* Normal (no bit set) */
-#define VCOL_UNKNOWN 1 /* UDF used; Need fix_fields() to know */
+/*
+ Flags for Virtual_column_info. If none is set, the expression must be
+ a constant with no side-effects, so it's calculated at CREATE TABLE time,
+ stored in table->record[2], and not recalculated for every statement.
+*/
+#define VCOL_FIELD_REF 1
#define VCOL_NON_DETERMINISTIC 2
-#define VCOL_TIME_FUNC 4
-#define VCOL_IMPOSSIBLE 8
+#define VCOL_SESSION_FUNC 4 /* uses session data, e.g. USER or DAYNAME */
+#define VCOL_TIME_FUNC 8
+#define VCOL_IMPOSSIBLE 16
+
+#define VCOL_NOT_STRICTLY_DETERMINISTIC \
+ (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC)
/*
Virtual_column_info is the class to contain additional
@@ -921,16 +914,9 @@ public:
}
virtual void set_default();
- bool has_insert_default_function() const
- {
- return (unireg_check == TIMESTAMP_DN_FIELD ||
- unireg_check == TIMESTAMP_DNUN_FIELD);
- }
-
bool has_update_default_function() const
{
- return (unireg_check == TIMESTAMP_UN_FIELD ||
- unireg_check == TIMESTAMP_DNUN_FIELD);
+ return unireg_check == TIMESTAMP_UN_FIELD;
}
/*
@@ -2370,21 +2356,7 @@ public:
void sql_type(String &str) const;
bool zero_pack() const { return 0; }
virtual int set_time();
- virtual void set_default()
- {
- if (has_insert_default_function())
- set_time();
- else
- Field::set_default();
- }
virtual void set_explicit_default(Item *value);
- virtual int evaluate_insert_default_function()
- {
- int res= 0;
- if (has_insert_default_function())
- res= set_time();
- return res;
- }
virtual int evaluate_update_default_function()
{
int res= 0;
@@ -2814,20 +2786,6 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return Field_datetime::get_TIME(ltime, ptr, fuzzydate); }
virtual int set_time();
- virtual void set_default()
- {
- if (has_insert_default_function())
- set_time();
- else
- Field::set_default();
- }
- virtual int evaluate_insert_default_function()
- {
- int res= 0;
- if (has_insert_default_function())
- res= set_time();
- return res;
- }
virtual int evaluate_update_default_function()
{
int res= 0;
@@ -3806,9 +3764,7 @@ public:
bool has_default_function() const
{
- return (unireg_check == Field::TIMESTAMP_DN_FIELD ||
- unireg_check == Field::TIMESTAMP_DNUN_FIELD ||
- unireg_check == Field::TIMESTAMP_UN_FIELD ||
+ return (unireg_check == Field::TIMESTAMP_UN_FIELD ||
unireg_check == Field::NEXT_NUMBER);
}
diff --git a/sql/handler.cc b/sql/handler.cc
index 7307afb6cd2..d7481f8e8ea 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1359,7 +1359,8 @@ int ha_commit_trans(THD *thd, bool all)
uint rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
/* rw_trans is TRUE when we in a transaction changing data */
- bool rw_trans= is_real_trans && (rw_ha_count > 0);
+ bool rw_trans= is_real_trans &&
+ (rw_ha_count > !thd->is_current_stmt_binlog_disabled());
MDL_request mdl_request;
DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d",
is_real_trans, rw_trans, rw_ha_count));
@@ -2735,6 +2736,15 @@ int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
return result;
}
+
+bool handler::ha_was_semi_consistent_read()
+{
+ bool result= was_semi_consistent_read();
+ if (result)
+ increment_statistics(&SSV::ha_read_retry_count);
+ return result;
+}
+
/* Initialize handler for random reading, with error handling */
int handler::ha_rnd_init_with_error(bool scan)
@@ -5877,6 +5887,26 @@ int handler::ha_reset()
}
+static int check_wsrep_max_ws_rows()
+{
+#ifdef WITH_WSREP
+ if (wsrep_max_ws_rows)
+ {
+ THD *thd= current_thd;
+ thd->wsrep_affected_rows++;
+ if (thd->wsrep_exec_mode != REPL_RECV &&
+ thd->wsrep_affected_rows > wsrep_max_ws_rows)
+ {
+ trans_rollback_stmt(thd) || trans_rollback(thd);
+ my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0));
+ return ER_ERROR_DURING_COMMIT;
+ }
+ }
+#endif /* WITH_WSREP */
+ return 0;
+}
+
+
int handler::ha_write_row(uchar *buf)
{
int error;
@@ -5900,7 +5930,7 @@ int handler::ha_write_row(uchar *buf)
error= binlog_log_row(table, 0, buf, log_func);
}
DEBUG_SYNC_C("ha_write_row_end");
- DBUG_RETURN(error);
+ DBUG_RETURN(error ? error : check_wsrep_max_ws_rows());
}
@@ -5931,7 +5961,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
rows_changed++;
error= binlog_log_row(table, old_data, new_data, log_func);
}
- return error;
+ return error ? error : check_wsrep_max_ws_rows();
}
int handler::ha_delete_row(const uchar *buf)
@@ -5958,7 +5988,7 @@ int handler::ha_delete_row(const uchar *buf)
rows_changed++;
error= binlog_log_row(table, buf, 0, log_func);
}
- return error;
+ return error ? error : check_wsrep_max_ws_rows();
}
@@ -6089,7 +6119,10 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
DBUG_RETURN(0);
}
- THD_TRANS *trans= &victim_thd->transaction.all;
+ /* Try statement transaction if standard one is not set. */
+ THD_TRANS *trans= (victim_thd->transaction.all.ha_list) ?
+ &victim_thd->transaction.all : &victim_thd->transaction.stmt;
+
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
for (; ha_info; ha_info= ha_info_next)
@@ -6097,8 +6130,8 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
handlerton *hton= ha_info->ht();
if (!hton->abort_transaction)
{
- /* Skip warning for binlog SE */
- if (hton->db_type != DB_TYPE_BINLOG)
+ /* Skip warning for binlog & wsrep. */
+ if (hton->db_type != DB_TYPE_BINLOG && hton != wsrep_hton)
{
WSREP_WARN("Cannot abort transaction.");
}
diff --git a/sql/handler.h b/sql/handler.h
index dbf05759dfc..fbb620c696c 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -3233,6 +3233,7 @@ public:
If this method returns nonzero, it will also signal the storage
engine that the next read will be a locking re-read of the row.
*/
+ bool ha_was_semi_consistent_read();
virtual bool was_semi_consistent_read() { return 0; }
/**
Tell the engine whether it should avoid unnecessary lock waits.
diff --git a/sql/item.cc b/sql/item.cc
index 2fb507cd720..d461386a696 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -57,6 +57,17 @@ bool cmp_items(Item *a, Item *b)
}
+/**
+ Set max_sum_func_level if it is needed
+*/
+inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select)
+{
+ if (thd->lex->in_sum_func &&
+ thd->lex->in_sum_func->nest_level >= select->nest_level)
+ set_if_bigger(thd->lex->in_sum_func->max_sum_func_level,
+ select->nest_level - 1);
+}
+
/*****************************************************************************
** Item functions
*****************************************************************************/
@@ -488,6 +499,7 @@ Item::Item(THD *thd):
}
}
+
/**
Constructor used by Item_field, Item_ref & aggregate (sum)
functions.
@@ -956,8 +968,7 @@ bool Item_field::check_field_expression_processor(void *arg)
{
if (field->flags & NO_DEFAULT_VALUE_FLAG)
return 0;
- if ((field->default_value || field->has_insert_default_function() ||
- field->vcol_info))
+ if ((field->default_value && field->default_value->flags) || field->vcol_info)
{
Field *org_field= (Field*) arg;
if (field == org_field ||
@@ -2079,6 +2090,9 @@ bool DTCollation::aggregate(const DTCollation &dt, uint flags)
set(0, DERIVATION_NONE, 0);
return 1;
}
+ if (collation->state & MY_CS_BINSORT &&
+ dt.collation->state & MY_CS_BINSORT)
+ return 1;
if (collation->state & MY_CS_BINSORT)
return 0;
if (dt.collation->state & MY_CS_BINSORT)
@@ -2259,6 +2273,83 @@ bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
}
+/**
+ @brief
+ Building clone for Item_func_or_sum
+
+ @param thd thread handle
+ @param mem_root part of the memory for the clone
+
+ @details
+ This method gets copy of the current item and also
+ build clones for its referencies. For the referencies
+ build_copy is called again.
+
+ @retval
+ clone of the item
+ 0 if an error occured
+*/
+
+Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root)
+{
+ Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd, mem_root);
+ if (!copy)
+ return 0;
+ if (arg_count > 2)
+ {
+ copy->args=
+ (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count);
+ if (!copy->args)
+ return 0;
+ }
+ else if (arg_count > 0)
+ copy->args= copy->tmp_arg;
+
+
+ for (uint i= 0; i < arg_count; i++)
+ {
+ Item *arg_clone= args[i]->build_clone(thd, mem_root);
+ if (!arg_clone)
+ return 0;
+ copy->args[i]= arg_clone;
+ }
+ return copy;
+}
+
+
+/**
+ @brief
+ Building clone for Item_ref
+
+ @param thd thread handle
+ @param mem_root part of the memory for the clone
+
+ @details
+ This method gets copy of the current item and also
+ builds clone for its reference.
+
+ @retval
+ clone of the item
+ 0 if an error occured
+*/
+
+Item* Item_ref::build_clone(THD *thd, MEM_ROOT *mem_root)
+{
+ Item_ref *copy= (Item_ref *) get_copy(thd, mem_root);
+ if (!copy)
+ return 0;
+ copy->ref=
+ (Item**) alloc_root(mem_root, sizeof(Item*));
+ if (!copy->ref)
+ return 0;
+ Item *item_clone= (* ref)->build_clone(thd, mem_root);
+ if (!item_clone)
+ return 0;
+ *copy->ref= item_clone;
+ return copy;
+}
+
+
void Item_ident_for_show::make_field(THD *thd, Send_field *tmp_field)
{
tmp_field->table_name= tmp_field->org_table_name= table_name;
@@ -4961,6 +5052,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
+ /*
+ We can not "move" aggregate function in the place where
+ its arguments are not defined.
+ */
+ set_max_sum_func_level(thd, select);
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, rf,
rf);
@@ -4969,6 +5065,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
}
else
{
+ /*
+ We can not "move" aggregate function in the place where
+ its arguments are not defined.
+ */
+ set_max_sum_func_level(thd, select);
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex,
this, (Item_ident*)*reference);
@@ -5100,6 +5201,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
return(1);
}
+ /*
+ We can not "move" aggregate function in the place where
+ its arguments are not defined.
+ */
+ set_max_sum_func_level(thd, thd->lex->current_select);
set_field(new_field);
return 0;
}
@@ -5124,6 +5230,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
select->parsing_place == IN_GROUP_BY &&
alias_name_used ? *rf->ref : rf);
+ /*
+ We can not "move" aggregate function in the place where
+ its arguments are not defined.
+ */
+ set_max_sum_func_level(thd, thd->lex->current_select);
return FALSE;
}
}
@@ -5220,6 +5331,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
}
#endif
fixed= 1;
+ if (field->vcol_info)
+ fix_session_vcol_expr_for_read(thd, field, field->vcol_info);
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
!outer_fixed && !thd->lex->in_sum_func &&
thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS &&
@@ -6707,6 +6820,121 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg)
}
+Item *Item_field::derived_field_transformer_for_having(THD *thd, uchar *arg)
+{
+ st_select_lex *sl= (st_select_lex *)arg;
+ table_map map= sl->master_unit()->derived->table->map;
+ if (!((Item_field*)this)->item_equal)
+ {
+ if (used_tables() == map)
+ {
+ Item_ref *rf=
+ new (thd->mem_root) Item_ref(thd, &sl->context,
+ NullS, NullS,
+ ((Item_field*) this)->field_name);
+ if (!rf)
+ return 0;
+ return rf;
+ }
+ }
+ else
+ {
+ Item_equal *cond= (Item_equal *) ((Item_field*)this)->item_equal;
+ Item_equal_fields_iterator li(*cond);
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->used_tables() == map && item->real_item()->type() == FIELD_ITEM)
+ {
+ Item_ref *rf=
+ new (thd->mem_root) Item_ref(thd, &sl->context,
+ NullS, NullS,
+ ((Item_field*) (item->real_item()))->field_name);
+ if (!rf)
+ return 0;
+ return rf;
+ }
+ }
+ }
+ return this;
+}
+
+
+Item *Item_field::derived_field_transformer_for_where(THD *thd, uchar *arg)
+{
+ Item *producing_item;
+ st_select_lex *sl= (st_select_lex *)arg;
+ List_iterator_fast<Item> li(sl->item_list);
+ table_map map= sl->master_unit()->derived->table->map;
+ if (used_tables() == map)
+ {
+ uint field_no= ((Item_field*) this)->field->field_index;
+ for (uint i= 0; i <= field_no; i++)
+ producing_item= li++;
+ return producing_item->build_clone(thd, thd->mem_root);
+ }
+ else if (((Item_field*)this)->item_equal)
+ {
+ Item_equal *cond= (Item_equal *) ((Item_field*)this)->item_equal;
+ Item_equal_fields_iterator it(*cond);
+ Item *item;
+ while ((item=it++))
+ {
+ if (item->used_tables() == map && item->real_item()->type() == FIELD_ITEM)
+ {
+ Item_field *field_item= (Item_field *) (item->real_item());
+ li.rewind();
+ uint field_no= field_item->field->field_index;
+ for (uint i= 0; i <= field_no; i++)
+ producing_item= li++;
+ return producing_item->build_clone(thd, thd->mem_root);
+ }
+ }
+ }
+ return this;
+}
+
+
+Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd,
+ uchar *arg)
+{
+ st_select_lex *sl= (st_select_lex *)arg;
+ List_iterator<Grouping_tmp_field> li(sl->grouping_tmp_fields);
+ Grouping_tmp_field *field;
+ table_map map= sl->master_unit()->derived->table->map;
+ if (used_tables() == map)
+ {
+ while ((field=li++))
+ {
+ if (((Item_field*) this)->field == field->tmp_field)
+ return field->producing_item->build_clone(thd, thd->mem_root);
+ }
+ }
+ else if (((Item_field*)this)->item_equal)
+ {
+ Item_equal *cond= (Item_equal *) ((Item_field*)this)->item_equal;
+ Item_equal_fields_iterator it(*cond);
+ Item *item;
+ while ((item=it++))
+ {
+ if (item->used_tables() == map && item->real_item()->type() == FIELD_ITEM)
+ {
+ Item_field *field_item= (Item_field *) (item->real_item());
+ li.rewind();
+ while ((field=li++))
+ {
+ if (field_item->field == field->tmp_field)
+ {
+ return field->producing_item->build_clone(thd, thd->mem_root);
+ }
+ }
+ }
+ }
+ }
+ return this;
+}
+
+
void Item_field::print(String *str, enum_query_type query_type)
{
if (field && field->table->const_table &&
@@ -8232,7 +8460,8 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
set_field(def_field);
if (field->default_value)
{
- if (field->default_value->expr_item) // it's NULL during CREATE TABLE
+ fix_session_vcol_expr_for_read(thd, field, field->default_value);
+ if (thd->mark_used_columns != MARK_COLUMNS_NONE)
field->default_value->expr_item->walk(&Item::register_field_in_read_map, 1, 0);
IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED
}
@@ -8258,7 +8487,7 @@ void Item_default_value::print(String *str, enum_query_type query_type)
void Item_default_value::calculate()
{
- if (field->default_value || field->has_insert_default_function())
+ if (field->default_value)
field->set_default();
}
@@ -9907,7 +10136,7 @@ table_map Item_ref_null_helper::used_tables() const
#ifndef DBUG_OFF
/* Debugger help function */
-static char dbug_item_print_buf[256];
+static char dbug_item_print_buf[2048];
const char *dbug_print_item(Item *item)
{
@@ -9931,5 +10160,97 @@ const char *dbug_print_item(Item *item)
return "Couldn't fit into buffer";
}
+const char *dbug_print_select(SELECT_LEX *sl)
+{
+ char *buf= dbug_item_print_buf;
+ String str(buf, sizeof(dbug_item_print_buf), &my_charset_bin);
+ str.length(0);
+ if (!sl)
+ return "(SELECT_LEX*)NULL";
+
+ THD *thd= current_thd;
+ ulonglong save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
+
+ sl->print(thd, &str, QT_EXPLAIN);
+
+ thd->variables.option_bits= save_option_bits;
+
+ if (str.c_ptr() == buf)
+ return buf;
+ else
+ return "Couldn't fit into buffer";
+}
+
+const char *dbug_print_unit(SELECT_LEX_UNIT *un)
+{
+ char *buf= dbug_item_print_buf;
+ String str(buf, sizeof(dbug_item_print_buf), &my_charset_bin);
+ str.length(0);
+ if (!un)
+ return "(SELECT_LEX_UNIT*)NULL";
+
+ THD *thd= current_thd;
+ ulonglong save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
+
+ un->print(&str, QT_EXPLAIN);
+
+ thd->variables.option_bits= save_option_bits;
+
+ if (str.c_ptr() == buf)
+ return buf;
+ else
+ return "Couldn't fit into buffer";
+}
+
+
#endif /*DBUG_OFF*/
+bool Item_field::exclusive_dependence_on_table_processor(void *map)
+{
+ table_map tab_map= *((table_map *) map);
+ return !((used_tables() == tab_map ||
+ (item_equal && item_equal->used_tables() & tab_map)));
+}
+
+bool Item_field::exclusive_dependence_on_grouping_fields_processor(void *arg)
+{
+ st_select_lex *sl= (st_select_lex *)arg;
+ List_iterator<Grouping_tmp_field> li(sl->grouping_tmp_fields);
+ Grouping_tmp_field *field;
+ table_map map= sl->master_unit()->derived->table->map;
+ if (used_tables() == map)
+ {
+ while ((field=li++))
+ {
+ if (((Item_field*) this)->field == field->tmp_field)
+ return false;
+ }
+ }
+ else if (((Item_field*)this)->item_equal)
+ {
+ Item_equal *cond= (Item_equal *) ((Item_field*)this)->item_equal;
+ Item_equal_fields_iterator it(*cond);
+ Item *item;
+ while ((item=it++))
+ {
+ if (item->used_tables() == map && item->real_item()->type() == FIELD_ITEM)
+ {
+ li.rewind();
+ while ((field=li++))
+ {
+ if (((Item_field *)(item->real_item()))->field == field->tmp_field)
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void Item::register_in(THD *thd)
+{
+ next= thd->free_list;
+ thd->free_list= this;
+}
diff --git a/sql/item.h b/sql/item.h
index e43b4d50e46..5b8254837e8 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -32,6 +32,8 @@ C_MODE_START
#include <ma_dyncol.h>
C_MODE_END
+const char *dbug_print_item(Item *item);
+
class Protocol;
struct TABLE_LIST;
void item_init(void); /* Init item functions */
@@ -89,6 +91,10 @@ bool mark_unsupported_function(const char *w1, const char *w2,
#define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV)
#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE)
+#define NO_EXTRACTION_FL (1 << 6)
+#define FULL_EXTRACTION_FL (1 << 7)
+#define EXTRACTION_MASK (NO_EXTRACTION_FL | FULL_EXTRACTION_FL)
+
class DTCollation {
public:
CHARSET_INFO *collation;
@@ -591,7 +597,6 @@ class Item: public Value_source,
public Type_std_attributes,
public Type_handler
{
- Item(const Item &); /* Prevent use of these */
void operator=(Item &);
/**
The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached
@@ -1110,6 +1115,7 @@ public:
virtual bool basic_const_item() const { return 0; }
/* cloning of constant items (0 if it is not const) */
virtual Item *clone_item(THD *thd) { return 0; }
+ virtual Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return get_copy(thd, mem_root); }
virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const
{ return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
@@ -1475,6 +1481,12 @@ public:
virtual bool exists2in_processor(void *opt_arg) { return 0; }
virtual bool find_selective_predicates_list_processor(void *opt_arg)
{ return 0; }
+ virtual bool exclusive_dependence_on_table_processor(void *map)
+ { return 0; }
+ virtual bool exclusive_dependence_on_grouping_fields_processor(void *arg)
+ { return 0; }
+
+ virtual Item *get_copy(THD *thd, MEM_ROOT *mem_root)=0;
/* To call bool function for all arguments */
struct bool_func_call_args
@@ -1679,6 +1691,13 @@ public:
{ return this; }
virtual Item *expr_cache_insert_transformer(THD *thd, uchar *unused)
{ return this; }
+ virtual Item *derived_field_transformer_for_having(THD *thd, uchar *arg)
+ { return this; }
+ virtual Item *derived_field_transformer_for_where(THD *thd, uchar *arg)
+ { return this; }
+ virtual Item *derived_grouping_field_transformer_for_where(THD *thd,
+ uchar *arg)
+ { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs)
@@ -1836,9 +1855,35 @@ public:
*/
virtual void under_not(Item_func_not * upper
__attribute__((unused))) {};
+
+
+ void register_in(THD *thd);
+
+ bool depends_only_on(table_map view_map)
+ { return marker & FULL_EXTRACTION_FL; }
+ int get_extraction_flag()
+ { return marker & EXTRACTION_MASK; }
+ void set_extraction_flag(int flags)
+ {
+ marker &= ~EXTRACTION_MASK;
+ marker|= flags;
+ }
+ void clear_extraction_flag()
+ {
+ marker &= ~EXTRACTION_MASK;
+ }
};
+template <class T>
+inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item)
+{
+ Item *copy= new (mem_root) T(*item);
+ copy->register_in(thd);
+ return copy;
+}
+
+
/**
Compare two Items for List<Item>::add_unique()
*/
@@ -2119,6 +2164,8 @@ public:
{ return this; }
bool append_for_log(THD *thd, String *str);
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
/*****************************************************************************
@@ -2165,6 +2212,7 @@ public:
purposes.
*/
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
private:
uint m_case_expr_id;
@@ -2245,6 +2293,8 @@ public:
{
return mark_unsupported_function("name_const()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_name_const>(thd, mem_root, this); }
};
class Item_num: public Item_basic_constant
@@ -2377,6 +2427,8 @@ public:
CHARSET_INFO *charset_for_protocol(void) const
{ return field->charset_for_protocol(); }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ Item* get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_ident_for_show>(thd, mem_root, this); }
};
@@ -2516,8 +2568,8 @@ public:
bool update_table_bitmaps_processor(void *arg);
bool switch_to_nullable_fields_processor(void *arg);
bool check_vcol_func_processor(void *arg)
- { // may be, a special flag VCOL_FIELD ?
- return mark_unsupported_function(field_name, arg, VCOL_UNKNOWN);
+ {
+ return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF);
}
void cleanup();
Item_equal *get_item_equal() { return item_equal; }
@@ -2529,7 +2581,14 @@ public:
Item_field *field_for_view_update() { return this; }
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(THD *thd, uchar *select_arg);
+ Item *derived_field_transformer_for_having(THD *thd, uchar *arg);
+ Item *derived_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg);
virtual void print(String *str, enum_query_type query_type);
+ bool exclusive_dependence_on_table_processor(void *map);
+ bool exclusive_dependence_on_grouping_fields_processor(void *arg);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_field>(thd, mem_root, this); }
bool is_outer_field() const
{
DBUG_ASSERT(fixed);
@@ -2622,6 +2681,8 @@ public:
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool check_partition_func_processor(void *int_arg) {return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_null>(thd, mem_root, this); }
};
class Item_null_result :public Item_null
@@ -2783,6 +2844,8 @@ public:
bool append_for_log(THD *thd, String *str);
bool check_vcol_func_processor(void *int_arg) {return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
+
private:
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -2831,6 +2894,8 @@ public:
{ return (uint) (max_length - MY_TEST(value < 0)); }
bool eq(const Item *item, bool binary_cmp) const
{ return int_eq(value, item); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_int>(thd, mem_root, this); }
};
@@ -2847,6 +2912,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
Item *neg(THD *thd);
uint decimal_precision() const { return max_length; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_uint>(thd, mem_root, this); }
};
@@ -2893,6 +2960,8 @@ public:
uint decimal_precision() const { return decimal_value.precision(); }
bool eq(const Item *, bool binary_cmp) const;
void set_decimal_value(my_decimal *value_par);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_decimal>(thd, mem_root, this); }
};
@@ -2941,6 +3010,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *item, bool binary_cmp) const
{ return real_eq(value, item); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_float>(thd, mem_root, this); }
};
@@ -3130,6 +3201,9 @@ public:
}
return MYSQL_TYPE_STRING; // Not a temporal literal
}
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_string>(thd, mem_root, this); }
};
@@ -3358,6 +3432,8 @@ public:
}
enum Item_result cast_to_int_type() const { return INT_RESULT; }
void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_hex_hybrid>(thd, mem_root, this); }
};
@@ -3398,6 +3474,8 @@ public:
}
enum Item_result cast_to_int_type() const { return STRING_RESULT; }
void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_hex_string>(thd, mem_root, this); }
};
@@ -3479,6 +3557,8 @@ public:
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_date_literal>(thd, mem_root, this); }
};
@@ -3498,6 +3578,8 @@ public:
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_time_literal>(thd, mem_root, this); }
};
@@ -3519,6 +3601,8 @@ public:
void print(String *str, enum_query_type query_type);
Item *clone_item(THD *thd);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_datetime_literal>(thd, mem_root, this); }
};
@@ -3879,6 +3963,7 @@ public:
virtual void fix_length_and_dec()= 0;
bool const_item() const { return const_item_cache; }
table_map used_tables() const { return used_tables_cache; }
+ Item* build_clone(THD *thd, MEM_ROOT *mem_root);
};
@@ -4054,6 +4139,8 @@ public:
DBUG_ASSERT(ref);
return (*ref)->is_outer_field();
}
+
+ Item* build_clone(THD *thd, MEM_ROOT *mem_root);
/**
Checks if the item tree that ref points to contains a subquery.
@@ -4062,6 +4149,8 @@ public:
{
return (*ref)->has_subquery();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_ref>(thd, mem_root, this); }
};
@@ -4104,6 +4193,8 @@ public:
bool is_null();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual Ref_Type ref_type() { return DIRECT_REF; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_direct_ref>(thd, mem_root, this); }
};
@@ -4265,6 +4356,9 @@ public:
{
return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_wrapper>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
@@ -4516,6 +4610,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_ref_null_helper>(thd, mem_root, this); }
};
/*
@@ -4675,6 +4771,8 @@ public:
longlong val_int();
void copy();
int save_in_field(Field *field, bool no_conversions);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_copy_string>(thd, mem_root, this); }
};
@@ -4697,6 +4795,8 @@ public:
return null_value ? 0 : cached_value;
}
virtual void copy();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_copy_int>(thd, mem_root, this); }
};
@@ -4713,6 +4813,8 @@ public:
{
return null_value ? 0.0 : (double) (ulonglong) cached_value;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_copy_uint>(thd, mem_root, this); }
};
@@ -4739,6 +4841,8 @@ public:
cached_value= item->val_real();
null_value= item->null_value;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_copy_float>(thd, mem_root, this); }
};
@@ -4758,6 +4862,8 @@ public:
double val_real();
longlong val_int();
void copy();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_copy_decimal>(thd, mem_root, this); }
};
@@ -4894,6 +5000,7 @@ public:
bool send(Protocol *protocol, String *buffer);
int save_in_field(Field *field_arg, bool no_conversions);
table_map used_tables() const { return (table_map)0L; }
+ Item_field *field_for_view_update() { return 0; }
bool walk(Item_processor processor, bool walk_subquery, void *args)
{
@@ -5182,6 +5289,8 @@ public:
enum Item_result result_type() const { return INT_RESULT; }
bool cache_value();
int save_in_field(Field *field, bool no_conversions);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_int>(thd, mem_root, this); }
};
@@ -5206,6 +5315,8 @@ public:
Important when storing packed datetime values.
*/
Item *clone_item(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_temporal>(thd, mem_root, this); }
};
@@ -5222,6 +5333,8 @@ public:
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return REAL_RESULT; }
bool cache_value();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_real>(thd, mem_root, this); }
};
@@ -5238,6 +5351,8 @@ public:
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return DECIMAL_RESULT; }
bool cache_value();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_decimal>(thd, mem_root, this); }
};
@@ -5264,6 +5379,8 @@ public:
CHARSET_INFO *charset() const { return value->charset(); };
int save_in_field(Field *field, bool no_conversions);
bool cache_value();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_str>(thd, mem_root, this); }
};
@@ -5287,6 +5404,8 @@ public:
*/
return Item::safe_charset_converter(thd, tocs);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_str_for_nullif>(thd, mem_root, this); }
};
@@ -5358,6 +5477,8 @@ public:
}
bool cache_value();
virtual void set_null();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cache_row>(thd, mem_root, this); }
};
@@ -5413,6 +5534,7 @@ public:
static uint32 display_length(Item *item);
static enum_field_types get_real_type(Item *);
Field::geometry_type get_geometry_type() const { return geometry_type; };
+ Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index eafb6b1c91b..434442cffa9 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -4856,6 +4856,43 @@ void Item_cond::neg_arguments(THD *thd)
}
+/**
+ @brief
+ Building clone for Item_cond
+
+ @param thd thread handle
+ @param mem_root part of the memory for the clone
+
+ @details
+ This method gets copy of the current item and also
+ build clones for its elements. For this elements
+ build_copy is called again.
+
+ @retval
+ clone of the item
+ 0 if an error occured
+*/
+
+Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root)
+{
+ List_iterator_fast<Item> li(list);
+ Item *item;
+ Item_cond *copy= (Item_cond *) get_copy(thd, mem_root);
+ if (!copy)
+ return 0;
+ copy->list.empty();
+ while ((item= li++))
+ {
+ Item *arg_clone= item->build_clone(thd, mem_root);
+ if (!arg_clone)
+ return 0;
+ if (copy->list.push_back(arg_clone, mem_root))
+ return 0;
+ }
+ return copy;
+}
+
+
void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding)
{
List_iterator<Item> li(list);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f899775ce88..6d432bd97f3 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -26,6 +26,7 @@
#include "item_func.h" /* Item_int_func, Item_bool_func */
#define PCRE_STATIC 1 /* Important on Windows */
#include "pcre.h" /* pcre header file */
+#include "item.h"
extern Item_result item_cmp_type(Item_result a,Item_result b);
inline Item_result item_cmp_type(const Item *a, const Item *b)
@@ -124,6 +125,7 @@ public:
comparators= 0;
}
friend class Item_func;
+ friend class Item_bool_rowready_func2;
};
@@ -244,6 +246,8 @@ public:
Item_func_istrue(THD *thd, Item *a): Item_func_truth(thd, a, true, true) {}
~Item_func_istrue() {}
virtual const char* func_name() const { return "istrue"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_istrue>(thd, mem_root, this); }
};
@@ -258,6 +262,8 @@ public:
Item_func_truth(thd, a, true, false) {}
~Item_func_isnottrue() {}
virtual const char* func_name() const { return "isnottrue"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isnottrue>(thd, mem_root, this); }
};
@@ -271,6 +277,8 @@ public:
Item_func_isfalse(THD *thd, Item *a): Item_func_truth(thd, a, false, true) {}
~Item_func_isfalse() {}
virtual const char* func_name() const { return "isfalse"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isfalse>(thd, mem_root, this); }
};
@@ -285,6 +293,8 @@ public:
Item_func_truth(thd, a, false, false) {}
~Item_func_isnotfalse() {}
virtual const char* func_name() const { return "isnotfalse"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isnotfalse>(thd, mem_root, this); }
};
@@ -346,6 +356,8 @@ public:
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
bool invisible_mode();
void reset_cache() { cache= NULL; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_in_optimizer>(thd, mem_root, this); }
};
@@ -499,6 +511,17 @@ public:
return add_key_fields_optimize_op(join, key_fields, and_level,
usable_tables, sargables, false);
}
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+ {
+ Item_bool_rowready_func2 *clone=
+ (Item_bool_rowready_func2 *) Item_func::build_clone(thd, mem_root);
+ if (clone)
+ {
+ clone->cmp.comparators= 0;
+ }
+ return clone;
+ }
+
};
/**
@@ -521,6 +544,8 @@ public:
Item_args::propagate_equal_fields(thd, Context_boolean(), cond);
return this;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xor>(thd, mem_root, this); }
};
class Item_func_not :public Item_bool_func
@@ -537,6 +562,8 @@ public:
Item *neg_transformer(THD *thd);
bool fix_fields(THD *, Item **);
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_not>(thd, mem_root, this); }
};
class Item_maxmin_subselect;
@@ -584,6 +611,8 @@ public:
void add_key_fields(JOIN *join, KEY_FIELD **key_fields,
uint *and_level, table_map usable_tables,
SARGABLE_PARAM **sargables);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_trig_cond>(thd, mem_root, this); }
};
class Item_func_not_all :public Item_func_not
@@ -659,6 +688,8 @@ public:
uint in_equality_no;
virtual uint exists2in_reserved_items() { return 1; };
friend class Arg_comparator;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_eq>(thd, mem_root, this); }
};
class Item_func_equal :public Item_bool_rowready_func2
@@ -681,6 +712,8 @@ public:
return add_key_fields_optimize_op(join, key_fields, and_level,
usable_tables, sargables, true);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_equal>(thd, mem_root, this); }
};
@@ -695,6 +728,8 @@ public:
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return ">="; }
Item *negated_item(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ge>(thd, mem_root, this); }
};
@@ -709,6 +744,8 @@ public:
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return ">"; }
Item *negated_item(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_gt>(thd, mem_root, this); }
};
@@ -723,6 +760,8 @@ public:
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return "<="; }
Item *negated_item(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_le>(thd, mem_root, this); }
};
@@ -737,6 +776,8 @@ public:
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return "<"; }
Item *negated_item(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_lt>(thd, mem_root, this); }
};
@@ -760,6 +801,8 @@ public:
Item *negated_item(THD *thd);
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ne>(thd, mem_root, this); }
};
@@ -840,6 +883,8 @@ public:
cond);
return this;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_between>(thd, mem_root, this); }
};
@@ -858,6 +903,8 @@ public:
agg_arg_charsets_for_comparison(cmp_collation, args, 2);
fix_char_length(2); // returns "1" or "0" or "-1"
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_strcmp>(thd, mem_root, this); }
};
@@ -888,6 +935,8 @@ public:
str->append(func_name());
print_args(str, 0, query_type);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_interval>(thd, mem_root, this); }
};
@@ -910,6 +959,8 @@ public:
}
const char *func_name() const { return "coalesce"; }
table_map not_null_tables() const { return 0; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_coalesce>(thd, mem_root, this); }
};
@@ -959,6 +1010,8 @@ public:
{
return Item_func_case_abbreviation2::decimal_precision2(args);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ifnull>(thd, mem_root, this); }
};
@@ -982,6 +1035,8 @@ public:
const char *func_name() const { return "if"; }
bool eval_not_null_tables(void *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_if>(thd, mem_root, this); }
private:
void cache_type_info(Item *source);
};
@@ -1057,6 +1112,8 @@ public:
cond, &args[2]);
return this;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_nullif>(thd, mem_root, this); }
};
@@ -1240,7 +1297,6 @@ public:
item_dec->set_decimal_value(dec);
}
Item_result result_type() { return DECIMAL_RESULT; }
-
};
@@ -1499,6 +1555,19 @@ public:
void cleanup();
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond);
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_case>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+ {
+ Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root);
+ if (clone)
+ {
+ clone->case_item= 0;
+ clone->arg_buffer= 0;
+ bzero(&clone->cmp_items, sizeof(cmp_items));
+ }
+ return clone;
+ }
};
/*
@@ -1595,6 +1664,18 @@ public:
bool eval_not_null_tables(void *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
bool count_sargable_conds(void *arg);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_in>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+ {
+ Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd, mem_root);
+ if (clone)
+ {
+ clone->array= 0;
+ bzero(&clone->cmp_items, sizeof(cmp_items));
+ }
+ return clone;
+ }
};
class cmp_item_row :public cmp_item
@@ -1689,6 +1770,8 @@ public:
bool top_level);
table_map not_null_tables() const { return 0; }
Item *neg_transformer(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isnull>(thd, mem_root, this); }
};
/* Functions used by HAVING for rewriting IN subquery */
@@ -1734,6 +1817,8 @@ public:
Item *neg_transformer(THD *thd);
virtual void print(String *str, enum_query_type query_type);
void top_level_item() { abort_on_null=1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isnotnull>(thd, mem_root, this); }
};
@@ -1873,6 +1958,9 @@ public:
void cleanup();
bool find_selective_predicates_list_processor(void *arg);
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_like>(thd, mem_root, this); }
};
@@ -1978,6 +2066,8 @@ public:
longlong val_int();
void fix_length_and_dec();
const char *func_name() const { return "regexp"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_regex>(thd, mem_root, this); }
virtual inline void print(String *str, enum_query_type query_type)
{
@@ -2005,6 +2095,8 @@ public:
longlong val_int();
void fix_length_and_dec();
const char *func_name() const { return "regexp_instr"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_regexp_instr>(thd, mem_root, this); }
};
@@ -2082,6 +2174,7 @@ public:
Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(void *opt_arg);
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root);
};
template <template<class> class LI, class T> class Item_equal_iterator;
@@ -2254,6 +2347,7 @@ public:
void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
void set_link_equal_fields(bool flag) { link_equal_fields= flag; }
+ Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
friend class Item_equal_fields_iterator;
bool count_sargable_conds(void *arg);
friend class Item_equal_iterator<List_iterator_fast,Item>;
@@ -2398,6 +2492,8 @@ public:
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables);
SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cond_and>(thd, mem_root, this); }
};
inline bool is_cond_and(Item *item)
@@ -2422,6 +2518,8 @@ public:
table_map not_null_tables() const { return and_tables_cache; }
Item *copy_andor_structure(THD *thd);
Item *neg_transformer(THD *thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_cond_or>(thd, mem_root, this); }
};
class Item_func_dyncol_check :public Item_bool_func
@@ -2431,6 +2529,8 @@ public:
longlong val_int();
const char *func_name() const { return "column_check"; }
bool need_parentheses_in_default() { return false; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_check>(thd, mem_root, this); }
};
class Item_func_dyncol_exists :public Item_bool_func
@@ -2441,6 +2541,8 @@ public:
longlong val_int();
const char *func_name() const { return "column_exists"; }
bool need_parentheses_in_default() { return false; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_exists>(thd, mem_root, this); }
};
inline bool is_cond_or(Item *item)
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 7f8c89cc228..3b98dd0d345 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2273,15 +2273,6 @@ longlong Item_func_bit_neg::val_int()
// Conversion functions
-void Item_func_integer::fix_length_and_dec()
-{
- max_length=args[0]->max_length - args[0]->decimals+1;
- uint tmp=float_length(decimals);
- set_if_smaller(max_length,tmp);
- decimals=0;
-}
-
-
void Item_func_int_val::fix_length_and_dec()
{
DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
@@ -3885,7 +3876,7 @@ longlong Item_master_pos_wait::val_int()
longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ;
String connection_name_buff;
LEX_STRING connection_name;
- Master_info *mi;
+ Master_info *mi= NULL;
if (arg_count >= 4)
{
String *con;
@@ -3905,8 +3896,9 @@ longlong Item_master_pos_wait::val_int()
connection_name= thd->variables.default_master_connection;
mysql_mutex_lock(&LOCK_active_mi);
- mi= master_info_index->get_master_info(&connection_name,
- Sql_condition::WARN_LEVEL_WARN);
+ if (master_info_index) // master_info_index is set to NULL on shutdown.
+ mi= master_info_index->get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_WARN);
mysql_mutex_unlock(&LOCK_active_mi);
if (!mi)
goto err;
@@ -5854,7 +5846,7 @@ void Item_func_get_system_var::print(String *str, enum_query_type query_type)
bool Item_func_get_system_var::check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function("@@", var->name.str, arg, VCOL_NON_DETERMINISTIC);
+ return mark_unsupported_function("@@", var->name.str, arg, VCOL_SESSION_FUNC);
}
enum Item_result Item_func_get_system_var::result_type() const
@@ -6957,3 +6949,6 @@ void Item_func_last_value::fix_length_and_dec()
Type_std_attributes::set(last_value);
maybe_null= last_value->maybe_null;
}
+
+
+
diff --git a/sql/item_func.h b/sql/item_func.h
index a909d93dbaa..ca7c4819012 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -613,8 +613,10 @@ public:
longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_connection_id>(thd, mem_root, this); }
};
@@ -640,6 +642,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_signed>(thd, mem_root, this); }
};
@@ -658,6 +662,8 @@ public:
return value;
}
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_unsigned>(thd, mem_root, this); }
};
@@ -682,6 +688,8 @@ public:
const char *func_name() const { return "decimal_typecast"; }
virtual void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_decimal_typecast>(thd, mem_root, this); }
};
@@ -700,6 +708,8 @@ public:
const char *func_name() const { return "double_typecast"; }
virtual void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_double_typecast>(thd, mem_root, this); }
};
@@ -723,6 +733,8 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_plus>(thd, mem_root, this); }
};
class Item_func_minus :public Item_func_additive_op
@@ -735,6 +747,8 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_minus>(thd, mem_root, this); }
};
@@ -750,6 +764,8 @@ public:
void result_precision();
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_mul>(thd, mem_root, this); }
};
@@ -764,6 +780,8 @@ public:
const char *func_name() const { return "/"; }
void fix_length_and_dec();
void result_precision();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_div>(thd, mem_root, this); }
};
@@ -784,6 +802,8 @@ public:
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_int_div>(thd, mem_root, this); }
};
@@ -799,6 +819,8 @@ public:
void fix_length_and_dec();
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_mod>(thd, mem_root, this); }
};
@@ -816,6 +838,8 @@ public:
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_neg>(thd, mem_root, this); }
};
@@ -830,6 +854,8 @@ public:
void fix_length_and_dec();
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_abs>(thd, mem_root, this); }
};
// A class to handle logarithmic and trigonometric functions
@@ -852,6 +878,8 @@ public:
Item_func_exp(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "exp"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_exp>(thd, mem_root, this); }
};
@@ -861,6 +889,8 @@ public:
Item_func_ln(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "ln"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ln>(thd, mem_root, this); }
};
@@ -871,6 +901,8 @@ public:
Item_func_log(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "log"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_log>(thd, mem_root, this); }
};
@@ -880,6 +912,8 @@ public:
Item_func_log2(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "log2"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_log2>(thd, mem_root, this); }
};
@@ -889,6 +923,8 @@ public:
Item_func_log10(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "log10"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_log10>(thd, mem_root, this); }
};
@@ -898,6 +934,8 @@ public:
Item_func_sqrt(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "sqrt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sqrt>(thd, mem_root, this); }
};
@@ -907,6 +945,8 @@ public:
Item_func_pow(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "pow"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_pow>(thd, mem_root, this); }
};
@@ -916,6 +956,8 @@ public:
Item_func_acos(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "acos"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_acos>(thd, mem_root, this); }
};
class Item_func_asin :public Item_dec_func
@@ -924,6 +966,8 @@ public:
Item_func_asin(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "asin"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_asin>(thd, mem_root, this); }
};
class Item_func_atan :public Item_dec_func
@@ -933,6 +977,8 @@ public:
Item_func_atan(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "atan"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_atan>(thd, mem_root, this); }
};
class Item_func_cos :public Item_dec_func
@@ -941,6 +987,8 @@ public:
Item_func_cos(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "cos"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_cos>(thd, mem_root, this); }
};
class Item_func_sin :public Item_dec_func
@@ -949,6 +997,8 @@ public:
Item_func_sin(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "sin"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sin>(thd, mem_root, this); }
};
class Item_func_tan :public Item_dec_func
@@ -957,6 +1007,8 @@ public:
Item_func_tan(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "tan"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_tan>(thd, mem_root, this); }
};
class Item_func_cot :public Item_dec_func
@@ -965,13 +1017,8 @@ public:
Item_func_cot(THD *thd, Item *a): Item_dec_func(thd, a) {}
double val_real();
const char *func_name() const { return "cot"; }
-};
-
-class Item_func_integer :public Item_int_func
-{
-public:
- inline Item_func_integer(THD *thd, Item *a): Item_int_func(thd, a) {}
- void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_cot>(thd, mem_root, this); }
};
@@ -993,6 +1040,8 @@ public:
my_decimal *decimal_op(my_decimal *);
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ceiling>(thd, mem_root, this); }
};
@@ -1006,6 +1055,8 @@ public:
my_decimal *decimal_op(my_decimal *);
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_floor>(thd, mem_root, this); }
};
/* This handles round and truncate */
@@ -1021,6 +1072,8 @@ public:
longlong int_op();
my_decimal *decimal_op(my_decimal *);
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_round>(thd, mem_root, this); }
};
@@ -1042,6 +1095,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_rand>(thd, mem_root, this); }
private:
void seed_random (Item * val);
};
@@ -1053,6 +1108,8 @@ public:
Item_func_sign(THD *thd, Item *a): Item_int_func(thd, a) {}
const char *func_name() const { return "sign"; }
longlong val_int();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sign>(thd, mem_root, this); }
};
@@ -1068,6 +1125,8 @@ public:
const char *func_name() const { return name; }
void fix_length_and_dec()
{ decimals= NOT_FIXED_DEC; max_length= float_length(decimals); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_units>(thd, mem_root, this); }
};
@@ -1104,6 +1163,8 @@ class Item_func_min :public Item_func_min_max
public:
Item_func_min(THD *thd, List<Item> &list): Item_func_min_max(thd, list, 1) {}
const char *func_name() const { return "least"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_min>(thd, mem_root, this); }
};
class Item_func_max :public Item_func_min_max
@@ -1111,6 +1172,8 @@ class Item_func_max :public Item_func_min_max
public:
Item_func_max(THD *thd, List<Item> &list): Item_func_min_max(thd, list, -1) {}
const char *func_name() const { return "greatest"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_max>(thd, mem_root, this); }
};
@@ -1143,6 +1206,8 @@ public:
/* The item could be a NULL constant. */
null_value= args[0]->is_null();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_rollup_const>(thd, mem_root, this); }
};
@@ -1154,6 +1219,8 @@ public:
longlong val_int();
const char *func_name() const { return "length"; }
void fix_length_and_dec() { max_length=10; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_length>(thd, mem_root, this); }
};
class Item_func_bit_length :public Item_func_length
@@ -1163,6 +1230,8 @@ public:
longlong val_int()
{ DBUG_ASSERT(fixed == 1); return Item_func_length::val_int()*8; }
const char *func_name() const { return "bit_length"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_length>(thd, mem_root, this); }
};
class Item_func_char_length :public Item_int_func
@@ -1173,6 +1242,8 @@ public:
longlong val_int();
const char *func_name() const { return "char_length"; }
void fix_length_and_dec() { max_length=10; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_char_length>(thd, mem_root, this); }
};
class Item_func_coercibility :public Item_int_func
@@ -1186,6 +1257,8 @@ public:
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
bool const_item() const { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_coercibility>(thd, mem_root, this); }
};
class Item_func_locate :public Item_int_func
@@ -1199,6 +1272,8 @@ public:
longlong val_int();
void fix_length_and_dec();
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_locate>(thd, mem_root, this); }
};
@@ -1212,6 +1287,8 @@ public:
longlong val_int();
const char *func_name() const { return "field"; }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_field>(thd, mem_root, this); }
};
@@ -1223,6 +1300,8 @@ public:
longlong val_int();
const char *func_name() const { return "ascii"; }
void fix_length_and_dec() { max_length=3; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ascii>(thd, mem_root, this); }
};
class Item_func_ord :public Item_int_func
@@ -1232,6 +1311,8 @@ public:
Item_func_ord(THD *thd, Item *a): Item_int_func(thd, a) {}
longlong val_int();
const char *func_name() const { return "ord"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ord>(thd, mem_root, this); }
};
class Item_func_find_in_set :public Item_int_func
@@ -1246,6 +1327,8 @@ public:
longlong val_int();
const char *func_name() const { return "find_in_set"; }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_find_in_set>(thd, mem_root, this); }
};
/* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */
@@ -1270,6 +1353,8 @@ public:
Item_func_bit_or(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "|"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_or>(thd, mem_root, this); }
};
class Item_func_bit_and :public Item_func_bit
@@ -1278,6 +1363,8 @@ public:
Item_func_bit_and(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "&"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_and>(thd, mem_root, this); }
};
class Item_func_bit_count :public Item_int_func
@@ -1287,6 +1374,8 @@ public:
longlong val_int();
const char *func_name() const { return "bit_count"; }
void fix_length_and_dec() { max_length=2; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_count>(thd, mem_root, this); }
};
class Item_func_shift_left :public Item_func_bit
@@ -1295,6 +1384,8 @@ public:
Item_func_shift_left(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "<<"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_shift_left>(thd, mem_root, this); }
};
class Item_func_shift_right :public Item_func_bit
@@ -1303,6 +1394,8 @@ public:
Item_func_shift_right(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
longlong val_int();
const char *func_name() const { return ">>"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_shift_right>(thd, mem_root, this); }
};
class Item_func_bit_neg :public Item_func_bit
@@ -1316,6 +1409,8 @@ public:
{
Item_func::print(str, query_type);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_neg>(thd, mem_root, this); }
};
@@ -1338,6 +1433,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_last_insert_id>(thd, mem_root, this); }
};
@@ -1355,6 +1452,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_benchmark>(thd, mem_root, this); }
};
@@ -1377,6 +1476,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sleep>(thd, mem_root, this); }
};
@@ -1470,7 +1571,7 @@ public:
virtual void print(String *str, enum_query_type query_type);
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(func_name(), "()", arg, VCOL_UNKNOWN);
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
};
@@ -1500,6 +1601,8 @@ class Item_func_udf_float :public Item_udf_func
String *val_str(String *str);
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
void fix_length_and_dec() { fix_num_length_and_dec(); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_udf_float>(thd, mem_root, this); }
};
@@ -1517,6 +1620,8 @@ public:
enum Item_result result_type () const { return INT_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
void fix_length_and_dec() { decimals= 0; max_length= 21; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_udf_int>(thd, mem_root, this); }
};
@@ -1534,6 +1639,8 @@ public:
enum Item_result result_type () const { return DECIMAL_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
void fix_length_and_dec() { fix_num_length_and_dec(); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_udf_decimal>(thd, mem_root, this); }
};
@@ -1572,6 +1679,8 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return string_field_type(); }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_udf_str>(thd, mem_root, this); }
};
#else /* Dummy functions to get sql_yacc.cc compiled */
@@ -1647,6 +1756,8 @@ class Item_func_get_lock :public Item_int_func
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_get_lock>(thd, mem_root, this); }
};
class Item_func_release_lock :public Item_int_func
@@ -1667,6 +1778,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_release_lock>(thd, mem_root, this); }
};
/* replication functions */
@@ -1687,6 +1800,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_master_pos_wait>(thd, mem_root, this); }
};
@@ -1703,6 +1818,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_master_gtid_wait>(thd, mem_root, this); }
};
@@ -1816,6 +1933,8 @@ public:
bool register_field_in_bitmap(void *arg);
bool set_entry(THD *thd, bool create_if_not_exists);
void cleanup();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_set_user_var>(thd, mem_root, this); }
};
@@ -1842,6 +1961,8 @@ public:
table_map used_tables() const
{ return const_item() ? 0 : RAND_TABLE_BIT; }
bool eq(const Item *item, bool binary_cmp) const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_get_user_var>(thd, mem_root, this); }
private:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -1881,6 +2002,8 @@ public:
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_user_var_as_out_param>(thd, mem_root, this); }
};
@@ -1935,6 +2058,8 @@ public:
void cleanup();
bool check_vcol_func_processor(void *arg);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_get_system_var>(thd, mem_root, this); }
};
@@ -1984,6 +2109,9 @@ public:
{
return mark_unsupported_function("match ... against()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_match>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
private:
/**
Check whether storage engine for given table,
@@ -2027,6 +2155,8 @@ public:
Item_func_bit_xor(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
longlong val_int();
const char *func_name() const { return "^"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_bit_xor>(thd, mem_root, this); }
};
class Item_func_is_free_lock :public Item_int_func
@@ -2041,6 +2171,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_free_lock>(thd, mem_root, this); }
};
class Item_func_is_used_lock :public Item_int_func
@@ -2055,6 +2187,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_used_lock>(thd, mem_root, this); }
};
/* For type casts */
@@ -2105,6 +2239,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_row_count>(thd, mem_root, this); }
};
@@ -2232,6 +2368,15 @@ public:
{
return TRUE;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sp>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root)
+ {
+ Item_func_sp *clone= (Item_func_sp *) Item_func::build_clone(thd, mem_root);
+ if (clone)
+ clone->sp_result_field= NULL;
+ return clone;
+ }
};
@@ -2246,6 +2391,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_found_rows>(thd, mem_root, this); }
};
@@ -2264,6 +2411,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_uuid_short>(thd, mem_root, this); }
};
@@ -2289,6 +2438,8 @@ public:
Item_func::update_used_tables();
maybe_null= last_value->maybe_null;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_last_value>(thd, mem_root, this); }
};
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 58e1d0a78fb..f96e570915e 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -51,6 +51,8 @@ public:
Item_geometry_func(thd, a, srid) {}
const char *func_name() const { return "st_geometryfromtext"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_geometry_from_text>(thd, mem_root, this); }
};
class Item_func_geometry_from_wkb: public Item_geometry_func
@@ -61,6 +63,8 @@ public:
Item_geometry_func(thd, a, srid) {}
const char *func_name() const { return "st_geometryfromwkb"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_geometry_from_wkb>(thd, mem_root, this); }
};
class Item_func_as_wkt: public Item_str_ascii_func
@@ -70,6 +74,8 @@ public:
const char *func_name() const { return "st_astext"; }
String *val_str_ascii(String *);
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_as_wkt>(thd, mem_root, this); }
};
class Item_func_as_wkb: public Item_geometry_func
@@ -79,6 +85,8 @@ public:
const char *func_name() const { return "st_aswkb"; }
String *val_str(String *);
enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); }
};
class Item_func_geometry_type: public Item_str_ascii_func
@@ -93,6 +101,8 @@ public:
fix_length_and_charset(20, default_charset());
maybe_null= 1;
};
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_geometry_type>(thd, mem_root, this); }
};
@@ -125,6 +135,8 @@ public:
{}
const char *func_name() const { return "st_convexhull"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_convexhull>(thd, mem_root, this); }
};
@@ -135,6 +147,8 @@ public:
const char *func_name() const { return "st_centroid"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_centroid>(thd, mem_root, this); }
};
class Item_func_envelope: public Item_geometry_func
@@ -144,6 +158,8 @@ public:
const char *func_name() const { return "st_envelope"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_envelope>(thd, mem_root, this); }
};
@@ -175,6 +191,8 @@ public:
Item_func_boundary(THD *thd, Item *a): Item_geometry_func(thd, a) {}
const char *func_name() const { return "st_boundary"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_boundary>(thd, mem_root, this); }
};
@@ -187,6 +205,8 @@ public:
const char *func_name() const { return "point"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_point>(thd, mem_root, this); }
};
class Item_func_spatial_decomp: public Item_geometry_func
@@ -211,6 +231,8 @@ public:
}
}
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_decomp>(thd, mem_root, this); }
};
class Item_func_spatial_decomp_n: public Item_geometry_func
@@ -235,6 +257,8 @@ public:
}
}
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_decomp_n>(thd, mem_root, this); }
};
class Item_func_spatial_collection: public Item_geometry_func
@@ -268,6 +292,8 @@ public:
}
const char *func_name() const { return "st_multipoint"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_collection>(thd, mem_root, this); }
};
@@ -300,6 +326,7 @@ public:
usable_tables, sargables, false);
}
bool need_parentheses_in_default() { return false; }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
@@ -311,6 +338,8 @@ public:
{ }
longlong val_int();
const char *func_name() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_mbr_rel>(thd, mem_root, this); }
};
@@ -325,6 +354,8 @@ public:
{ }
longlong val_int();
const char *func_name() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_precise_rel>(thd, mem_root, this); }
};
@@ -341,6 +372,8 @@ public:
longlong val_int();
const char *func_name() const { return "st_relate"; }
bool need_parentheses_in_default() { return false; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_relate>(thd, mem_root, this); }
};
@@ -370,6 +403,8 @@ public:
{
Item_func::print(str, query_type);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_spatial_operation>(thd, mem_root, this); }
};
@@ -421,6 +456,8 @@ public:
Item_geometry_func(thd, obj, distance) {}
const char *func_name() const { return "st_buffer"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_buffer>(thd, mem_root, this); }
};
@@ -432,6 +469,8 @@ public:
const char *func_name() const { return "st_isempty"; }
void fix_length_and_dec() { maybe_null= 1; }
bool need_parentheses_in_default() { return false; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isempty>(thd, mem_root, this); }
};
class Item_func_issimple: public Item_int_func
@@ -446,6 +485,8 @@ public:
const char *func_name() const { return "st_issimple"; }
void fix_length_and_dec() { decimals=0; max_length=2; }
uint decimal_precision() const { return 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_issimple>(thd, mem_root, this); }
};
class Item_func_isclosed: public Item_int_func
@@ -456,6 +497,8 @@ public:
const char *func_name() const { return "st_isclosed"; }
void fix_length_and_dec() { decimals=0; max_length=2; }
uint decimal_precision() const { return 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isclosed>(thd, mem_root, this); }
};
class Item_func_isring: public Item_func_issimple
@@ -464,6 +507,8 @@ public:
Item_func_isring(THD *thd, Item *a): Item_func_issimple(thd, a) {}
longlong val_int();
const char *func_name() const { return "st_isring"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_isring>(thd, mem_root, this); }
};
class Item_func_dimension: public Item_int_func
@@ -474,6 +519,8 @@ public:
longlong val_int();
const char *func_name() const { return "st_dimension"; }
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dimension>(thd, mem_root, this); }
};
class Item_func_x: public Item_real_func
@@ -488,6 +535,8 @@ public:
Item_real_func::fix_length_and_dec();
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_x>(thd, mem_root, this); }
};
@@ -503,6 +552,8 @@ public:
Item_real_func::fix_length_and_dec();
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_y>(thd, mem_root, this); }
};
@@ -514,6 +565,8 @@ public:
longlong val_int();
const char *func_name() const { return "st_numgeometries"; }
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_numgeometries>(thd, mem_root, this); }
};
@@ -525,6 +578,8 @@ public:
longlong val_int();
const char *func_name() const { return "st_numinteriorrings"; }
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_numinteriorring>(thd, mem_root, this); }
};
@@ -536,6 +591,8 @@ public:
longlong val_int();
const char *func_name() const { return "st_numpoints"; }
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_numpoints>(thd, mem_root, this); }
};
@@ -551,6 +608,8 @@ public:
Item_real_func::fix_length_and_dec();
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_area>(thd, mem_root, this); }
};
@@ -566,6 +625,8 @@ public:
Item_real_func::fix_length_and_dec();
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_glength>(thd, mem_root, this); }
};
@@ -577,6 +638,8 @@ public:
longlong val_int();
const char *func_name() const { return "srid"; }
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_srid>(thd, mem_root, this); }
};
@@ -591,6 +654,8 @@ public:
Item_func_distance(THD *thd, Item *a, Item *b): Item_real_func(thd, a, b) {}
double val_real();
const char *func_name() const { return "st_distance"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_distance>(thd, mem_root, this); }
};
@@ -605,6 +670,8 @@ public:
const char *func_name() const { return "st_pointonsurface"; }
String *val_str(String *);
Field::geometry_type get_geometry_type() const;
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_pointonsurface>(thd, mem_root, this); }
};
@@ -620,6 +687,8 @@ class Item_func_gis_debug: public Item_int_func
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_gis_debug>(thd, mem_root, this); }
};
#endif
diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h
index f5a0596d860..741b9f7d997 100644
--- a/sql/item_inetfunc.h
+++ b/sql/item_inetfunc.h
@@ -37,6 +37,8 @@ public:
maybe_null= 1;
unsigned_flag= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_inet_aton>(thd, mem_root, this); }
};
@@ -57,6 +59,8 @@ public:
fix_length_and_charset(3 * 8 + 7, default_charset());
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_inet_ntoa>(thd, mem_root, this); }
};
@@ -124,6 +128,8 @@ public:
fix_length_and_charset(16, &my_charset_bin);
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_inet6_aton>(thd, mem_root, this); }
protected:
virtual bool calc_value(String *arg, String *buffer);
@@ -156,6 +162,8 @@ public:
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_inet6_ntoa>(thd, mem_root, this); }
protected:
virtual bool calc_value(String *arg, String *buffer);
@@ -176,6 +184,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_ipv4>(thd, mem_root, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -196,6 +206,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv6"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_ipv6>(thd, mem_root, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -216,6 +228,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4_compat"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_ipv4_compat>(thd, mem_root, this); }
protected:
virtual bool calc_value(const String *arg);
@@ -236,6 +250,8 @@ public:
public:
virtual const char *func_name() const
{ return "is_ipv4_mapped"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_is_ipv4_mapped>(thd, mem_root, this); }
protected:
virtual bool calc_value(const String *arg);
diff --git a/sql/item_row.cc b/sql/item_row.cc
index a17d2507547..ddbb0736d54 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -160,3 +160,21 @@ void Item_row::bring_value()
for (uint i= 0; i < arg_count; i++)
args[i]->bring_value();
}
+
+
+Item* Item_row::build_clone(THD *thd, MEM_ROOT *mem_root)
+{
+ Item_row *copy= (Item_row *) get_copy(thd, mem_root);
+ if (!copy)
+ return 0;
+ copy->args= (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ Item *arg_clone= args[i]->build_clone(thd, mem_root);
+ if (!arg_clone)
+ return 0;
+ copy->args[i]= arg_clone;
+ }
+ return copy;
+}
+
diff --git a/sql/item_row.h b/sql/item_row.h
index eb2686090e1..bbfebb56010 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -120,6 +120,9 @@ public:
bool null_inside() { return with_null; };
void bring_value();
bool check_vcol_func_processor(void *arg) {return FALSE; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_row>(thd, mem_root, this); }
+ Item *build_clone(THD *thd, MEM_ROOT *mem_root);
};
#endif /* ITEM_ROW_INCLUDED */
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 3f5f0505d4c..bff31ec7b26 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -54,7 +54,6 @@
#include <base64.h>
#include <my_md5.h>
#include "sha1.h"
-#include <zlib.h>
C_MODE_START
#include "../mysys/my_static.h" // For soundex_map
C_MODE_END
@@ -2344,18 +2343,6 @@ void Item_func_decode::crypto_transform(String *res)
sql_crypt.decode((char*) res->ptr(),res->length());
}
-Item *Item_func_sysconst::safe_charset_converter(THD *thd,
- CHARSET_INFO *tocs)
-{
- /*
- In default, virtual functions or constraint expressions, the value
- of a sysconst is not constant
- */
- if (thd->in_stored_expression)
- return Item_str_func::safe_charset_converter(thd, tocs);
- return const_charset_converter(thd, tocs, true, fully_qualified_func_name());
-}
-
String *Item_func_database::val_str(String *str)
{
@@ -2377,69 +2364,55 @@ String *Item_func_database::val_str(String *str)
BUG#28086) binlog_format=MIXED, but is incorrectly replicated to ''
if binlog_format=STATEMENT.
*/
-
-bool Item_func_user::init(THD *thd, const char *user, const char *host)
+bool Item_func_user::init(const char *user, const char *host)
{
- DBUG_ENTER("Item_func_user::init");
DBUG_ASSERT(fixed == 1);
- /* Check if we have already calculated the value for this thread */
- if (thd->query_id == last_query_id)
- DBUG_RETURN(FALSE);
- DBUG_PRINT("enter", ("user: '%s' host: '%s'", user,host));
-
- last_query_id= thd->query_id;
- null_value= 0;
-
// For system threads (e.g. replication SQL thread) user may be empty
if (user)
{
- CHARSET_INFO *cs= system_charset_info;
+ CHARSET_INFO *cs= str_value.charset();
size_t res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen;
- if (cached_value.alloc((uint) res_length))
+ if (str_value.alloc((uint) res_length))
{
null_value=1;
- DBUG_RETURN(TRUE);
+ return TRUE;
}
- cached_value.set_charset(cs);
- res_length=cs->cset->snprintf(cs, (char*)cached_value.ptr(),
- (uint) res_length,
+ res_length=cs->cset->snprintf(cs, (char*)str_value.ptr(), (uint) res_length,
"%s@%s", user, host);
- cached_value.length((uint) res_length);
- cached_value.mark_as_const();
+ str_value.length((uint) res_length);
+ str_value.mark_as_const();
}
- else
- cached_value.set("", 0, system_charset_info);
- DBUG_RETURN(FALSE);
+ return FALSE;
}
-String *Item_func_user::val_str(String *str)
-{
- THD *thd= current_thd;
- init(thd, thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip);
- return null_value ? 0 : &cached_value;
-}
-String *Item_func_current_user::val_str(String *str)
+bool Item_func_user::fix_fields(THD *thd, Item **ref)
{
- THD *thd= current_thd;
- Security_context *ctx= (context->security_ctx ?
- context->security_ctx : thd->security_ctx);
- init(thd, ctx->priv_user, ctx->priv_host);
- return null_value ? 0 : &cached_value;
+ return (Item_func_sysconst::fix_fields(thd, ref) ||
+ init(thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip));
}
-bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
+bool Item_func_current_user::fix_fields(THD *thd, Item **ref)
{
- return Item_func_sysconst::fix_fields(thd,ref) || init(thd);
+ if (Item_func_sysconst::fix_fields(thd, ref))
+ return TRUE;
+
+ Security_context *ctx= context && context->security_ctx
+ ? context->security_ctx : thd->security_ctx;
+ return init(ctx->priv_user, ctx->priv_host);
}
-bool Item_func_current_role::init(THD *thd)
+bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
{
- Security_context *ctx= context->security_ctx
+ if (Item_func_sysconst::fix_fields(thd, ref))
+ return 1;
+
+ Security_context *ctx= context && context->security_ctx
? context->security_ctx : thd->security_ctx;
if (ctx->priv_role[0])
@@ -2448,22 +2421,14 @@ bool Item_func_current_role::init(THD *thd)
system_charset_info))
return 1;
+ null_value= maybe_null= 0;
+ str_value.mark_as_const();
return 0;
}
- null_value= 1;
+ null_value= maybe_null= 1;
return 0;
}
-String *Item_func_current_role::val_str(String *)
-{
- return (null_value ? 0 : &str_value);
-}
-
-int Item_func_current_role::save_in_field(Field *field, bool no_conversions)
-{
- return save_str_value_in_field(field, &str_value);
-}
-
void Item_func_soundex::fix_length_and_dec()
{
uint32 char_length= args[0]->max_char_length();
@@ -4154,7 +4119,7 @@ longlong Item_func_crc32::val_int()
return 0; /* purecov: inspected */
}
null_value=0;
- return (longlong) crc32(0L, (uchar*)res->ptr(), res->length());
+ return (longlong) my_checksum(0L, (uchar*)res->ptr(), res->length());
}
#ifdef HAVE_COMPRESS
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index b0f5064a190..25b63ebe73d 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -105,6 +105,8 @@ public:
String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "md5"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_md5>(thd, mem_root, this); }
};
@@ -114,7 +116,9 @@ public:
Item_func_sha(THD *thd, Item *a): Item_str_ascii_func(thd, a) {}
String *val_str_ascii(String *);
void fix_length_and_dec();
- const char *func_name() const { return "sha"; }
+ const char *func_name() const { return "sha"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sha>(thd, mem_root, this); }
};
class Item_func_sha2 :public Item_str_ascii_func
@@ -124,6 +128,8 @@ public:
String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "sha2"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sha2>(thd, mem_root, this); }
};
class Item_func_to_base64 :public Item_str_ascii_func
@@ -134,6 +140,8 @@ public:
String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "to_base64"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_to_base64>(thd, mem_root, this); }
};
class Item_func_from_base64 :public Item_str_func
@@ -144,6 +152,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "from_base64"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_from_base64>(thd, mem_root, this); }
};
#include <my_crypt.h>
@@ -167,6 +177,8 @@ public:
Item_aes_crypt(thd, a, b) {}
void fix_length_and_dec();
const char *func_name() const { return "aes_encrypt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_aes_encrypt>(thd, mem_root, this); }
};
class Item_func_aes_decrypt :public Item_aes_crypt
@@ -176,6 +188,8 @@ public:
Item_aes_crypt(thd, a, b) {}
void fix_length_and_dec();
const char *func_name() const { return "aes_decrypt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_aes_decrypt>(thd, mem_root, this); }
};
@@ -188,6 +202,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "concat"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_concat>(thd, mem_root, this); }
};
class Item_func_decode_histogram :public Item_str_func
@@ -204,6 +220,8 @@ public:
maybe_null= 1;
}
const char *func_name() const { return "decode_histogram"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_decode_histogram>(thd, mem_root, this); }
};
class Item_func_concat_ws :public Item_str_func
@@ -215,6 +233,8 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "concat_ws"; }
table_map not_null_tables() const { return 0; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_concat_ws>(thd, mem_root, this); }
};
class Item_func_reverse :public Item_str_func
@@ -225,6 +245,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "reverse"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_reverse>(thd, mem_root, this); }
};
@@ -237,6 +259,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "replace"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_replace>(thd, mem_root, this); }
};
@@ -260,6 +284,8 @@ public:
String *val_str(String *str);
void fix_length_and_dec();
const char *func_name() const { return "regexp_replace"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_regexp_replace>(thd, mem_root, this); }
};
@@ -280,6 +306,8 @@ public:
String *val_str(String *str);
void fix_length_and_dec();
const char *func_name() const { return "regexp_substr"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_regexp_substr>(thd, mem_root, this); }
};
@@ -293,6 +321,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "insert"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_insert>(thd, mem_root, this); }
};
@@ -314,6 +344,8 @@ public:
Item_func_lcase(THD *thd, Item *item): Item_str_conv(thd, item) {}
const char *func_name() const { return "lcase"; }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_lcase>(thd, mem_root, this); }
};
class Item_func_ucase :public Item_str_conv
@@ -322,6 +354,8 @@ public:
Item_func_ucase(THD *thd, Item *item): Item_str_conv(thd, item) {}
const char *func_name() const { return "ucase"; }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ucase>(thd, mem_root, this); }
};
@@ -333,6 +367,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "left"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_left>(thd, mem_root, this); }
};
@@ -344,6 +380,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "right"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_right>(thd, mem_root, this); }
};
@@ -356,6 +394,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "substr"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_substr>(thd, mem_root, this); }
};
@@ -368,6 +408,9 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "substring_index"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_substr_index>(thd, mem_root, this); }
+
};
@@ -399,6 +442,8 @@ public:
const char *func_name() const { return "trim"; }
virtual void print(String *str, enum_query_type query_type);
virtual const char *mode_name() const { return "both"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_trim>(thd, mem_root, this); }
};
@@ -410,6 +455,8 @@ public:
String *val_str(String *);
const char *func_name() const { return "ltrim"; }
const char *mode_name() const { return "leading"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_ltrim>(thd, mem_root, this); }
};
@@ -421,6 +468,8 @@ public:
String *val_str(String *);
const char *func_name() const { return "rtrim"; }
const char *mode_name() const { return "trailing"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_rtrim>(thd, mem_root, this); }
};
@@ -458,6 +507,8 @@ public:
"password" : "old_password"); }
static char *alloc(THD *thd, const char *password, size_t pass_len,
enum PW_Alg al);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_password>(thd, mem_root, this); }
};
@@ -476,6 +527,8 @@ public:
max_length = args[0]->max_length + 9;
}
const char *func_name() const { return "des_encrypt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_des_encrypt>(thd, mem_root, this); }
};
class Item_func_des_decrypt :public Item_str_func
@@ -494,6 +547,8 @@ public:
max_length-= 9U;
}
const char *func_name() const { return "des_decrypt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_des_decrypt>(thd, mem_root, this); }
};
class Item_func_encrypt :public Item_str_func
@@ -521,6 +576,8 @@ public:
{
return FALSE;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_encrypt>(thd, mem_root, this); }
};
#include "sql_crypt.h"
@@ -539,6 +596,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "encode"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_encode>(thd, mem_root, this); }
protected:
virtual void crypto_transform(String *);
private:
@@ -552,6 +611,8 @@ class Item_func_decode :public Item_func_encode
public:
Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {}
const char *func_name() const { return "decode"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_decode>(thd, mem_root, this); }
protected:
void crypto_transform(String *);
};
@@ -562,7 +623,10 @@ class Item_func_sysconst :public Item_str_func
public:
Item_func_sysconst(THD *thd): Item_str_func(thd)
{ collation.set(system_charset_info,DERIVATION_SYSCONST); }
- Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
+ Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(thd, tocs, true, fully_qualified_func_name());
+ }
/*
Used to create correct Item name in new converted item in
safe_charset_converter, return string representation of this function
@@ -572,7 +636,7 @@ public:
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(fully_qualified_func_name(), arg,
- VCOL_NON_DETERMINISTIC);
+ VCOL_SESSION_FUNC);
}
};
@@ -589,21 +653,27 @@ public:
}
const char *func_name() const { return "database"; }
const char *fully_qualified_func_name() const { return "database()"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_database>(thd, mem_root, this); }
};
class Item_func_user :public Item_func_sysconst
{
protected:
- query_id_t last_query_id;
- String cached_value;
- bool init(THD *thd, const char *user, const char *host);
+ bool init (const char *user, const char *host);
public:
- Item_func_user(THD *thd): Item_func_sysconst(thd), last_query_id(0)
- {}
-
- String *val_str(String *);
+ Item_func_user(THD *thd): Item_func_sysconst(thd)
+ {
+ str_value.set("", 0, system_charset_info);
+ }
+ String *val_str(String *)
+ {
+ DBUG_ASSERT(fixed == 1);
+ return (null_value ? 0 : &str_value);
+ }
+ bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
{
max_length= (username_char_length +
@@ -611,6 +681,12 @@ public:
}
const char *func_name() const { return "user"; }
const char *fully_qualified_func_name() const { return "user()"; }
+ int save_in_field(Field *field, bool no_conversions)
+ {
+ return save_str_value_in_field(field, &str_value);
+ }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_user>(thd, mem_root, this); }
};
@@ -621,14 +697,14 @@ class Item_func_current_user :public Item_func_user
public:
Item_func_current_user(THD *thd, Name_resolution_context *context_arg):
Item_func_user(thd), context(context_arg) {}
- String *val_str(String *);
+ bool fix_fields(THD *thd, Item **ref);
const char *func_name() const { return "current_user"; }
const char *fully_qualified_func_name() const { return "current_user()"; }
- /* This is because of the stored Name_resolution_context */
bool check_vcol_func_processor(void *arg)
{
+ context= 0;
return mark_unsupported_function(fully_qualified_func_name(), arg,
- VCOL_IMPOSSIBLE);
+ VCOL_SESSION_FUNC);
}
};
@@ -642,21 +718,25 @@ public:
Item_func_sysconst(thd), context(context_arg) {}
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
- {
- max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN;
- maybe_null=1;
- }
- bool init(THD *thd);
- int save_in_field(Field *field, bool no_conversions);
+ { max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; }
+ int save_in_field(Field *field, bool no_conversions)
+ { return save_str_value_in_field(field, &str_value); }
const char *func_name() const { return "current_role"; }
const char *fully_qualified_func_name() const { return "current_role()"; }
- String *val_str(String *);
- /* This is because of the stored Name_resolution_context */
+ String *val_str(String *)
+ {
+ DBUG_ASSERT(fixed == 1);
+ return (null_value ? 0 : &str_value);
+ }
bool check_vcol_func_processor(void *arg)
{
+
+ context= 0;
return mark_unsupported_function(fully_qualified_func_name(), arg,
- VCOL_IMPOSSIBLE);
+ VCOL_SESSION_FUNC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_current_role>(thd, mem_root, this); }
};
@@ -668,6 +748,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "soundex"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_soundex>(thd, mem_root, this); }
};
@@ -680,6 +762,8 @@ public:
String *val_str(String *str);
void fix_length_and_dec();
const char *func_name() const { return "elt"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_elt>(thd, mem_root, this); }
};
@@ -692,6 +776,8 @@ public:
String *val_str(String *str);
void fix_length_and_dec();
const char *func_name() const { return "make_set"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_make_set>(thd, mem_root, this); }
};
@@ -710,6 +796,8 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "format"; }
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_format>(thd, mem_root, this); }
};
@@ -727,6 +815,8 @@ public:
max_length= arg_count * 4;
}
const char *func_name() const { return "char"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_char>(thd, mem_root, this); }
};
@@ -739,6 +829,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "repeat"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_repeat>(thd, mem_root, this); }
};
@@ -749,6 +841,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "space"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_space>(thd, mem_root, this); }
};
@@ -763,8 +857,11 @@ public:
const char *func_name() const { return "binlog_gtid_pos"; }
bool check_vcol_func_processor(void *arg)
{
+
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_binlog_gtid_pos>(thd, mem_root, this); }
};
@@ -777,6 +874,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "rpad"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_rpad>(thd, mem_root, this); }
};
@@ -789,6 +888,8 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "lpad"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_lpad>(thd, mem_root, this); }
};
@@ -805,6 +906,8 @@ public:
max_length=64;
maybe_null= 1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_conv>(thd, mem_root, this); }
};
@@ -822,6 +925,8 @@ public:
decimals=0;
fix_char_length(args[0]->max_length * 2);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_hex>(thd, mem_root, this); }
};
class Item_func_unhex :public Item_str_func
@@ -841,6 +946,8 @@ public:
decimals=0;
max_length=(1+args[0]->max_length)/2;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_unhex>(thd, mem_root, this); }
};
@@ -871,6 +978,8 @@ public:
Item_func_like_range_min(THD *thd, Item *a, Item *b):
Item_func_like_range(thd, a, b, true) { }
const char *func_name() const { return "like_range_min"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_like_range_min>(thd, mem_root, this); }
};
@@ -880,6 +989,8 @@ public:
Item_func_like_range_max(THD *thd, Item *a, Item *b):
Item_func_like_range(thd, a, b, false) { }
const char *func_name() const { return "like_range_max"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_like_range_max>(thd, mem_root, this); }
};
#endif
@@ -905,6 +1016,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
const char *func_name() const { return "cast_as_binary"; }
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_binary>(thd, mem_root, this); }
};
@@ -925,6 +1038,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_load_file>(thd, mem_root, this); }
};
@@ -940,6 +1055,8 @@ class Item_func_export_set: public Item_str_func
String *val_str(String *str);
void fix_length_and_dec();
const char *func_name() const { return "export_set"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_export_set>(thd, mem_root, this); }
};
@@ -957,6 +1074,8 @@ public:
2 * collation.collation->mbmaxlen;
max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_quote>(thd, mem_root, this); }
};
class Item_func_conv_charset :public Item_str_func
@@ -1039,6 +1158,8 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "convert"; }
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_conv_charset>(thd, mem_root, this); }
};
class Item_func_set_collation :public Item_str_func
@@ -1058,6 +1179,8 @@ public:
return args[0]->field_for_view_update();
}
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_set_collation>(thd, mem_root, this); }
};
@@ -1085,6 +1208,8 @@ public:
:Item_func_expr_str_metadata(thd, a) { }
String *val_str(String *);
const char *func_name() const { return "charset"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_charset>(thd, mem_root, this); }
};
@@ -1095,6 +1220,8 @@ public:
:Item_func_expr_str_metadata(thd, a) {}
String *val_str(String *);
const char *func_name() const { return "collation"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_collation>(thd, mem_root, this); }
};
@@ -1127,6 +1254,8 @@ public:
}
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_weight_string>(thd, mem_root, this); }
};
class Item_func_crc32 :public Item_int_func
@@ -1138,6 +1267,8 @@ public:
const char *func_name() const { return "crc32"; }
void fix_length_and_dec() { max_length=10; }
longlong val_int();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_crc32>(thd, mem_root, this); }
};
class Item_func_uncompressed_length : public Item_int_func
@@ -1148,6 +1279,8 @@ public:
const char *func_name() const{return "uncompressed_length";}
void fix_length_and_dec() { max_length=10; maybe_null= true; }
longlong val_int();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_uncompressed_length>(thd, mem_root, this); }
};
#ifdef HAVE_COMPRESS
@@ -1164,6 +1297,8 @@ public:
void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;}
const char *func_name() const{return "compress";}
String *val_str(String *) ZLIB_DEPENDED_FUNCTION
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_compress>(thd, mem_root, this); }
};
class Item_func_uncompress: public Item_str_func
@@ -1174,6 +1309,8 @@ public:
void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; }
const char *func_name() const{return "uncompress";}
String *val_str(String *) ZLIB_DEPENDED_FUNCTION
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_uncompress>(thd, mem_root, this); }
};
@@ -1193,6 +1330,8 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_uuid>(thd, mem_root, this); }
};
@@ -1214,6 +1353,8 @@ public:
String *val_str(String *);
virtual void print(String *str, enum_query_type query_type);
virtual enum Functype functype() const { return DYNCOL_FUNC; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_create>(thd, mem_root, this); }
};
@@ -1226,6 +1367,8 @@ public:
const char *func_name() const{ return "column_add"; }
String *val_str(String *);
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_add>(thd, mem_root, this); }
};
class Item_func_dyncol_json: public Item_str_func
@@ -1241,6 +1384,8 @@ public:
collation.set(&my_charset_bin);
decimals= 0;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_json>(thd, mem_root, this); }
};
/*
@@ -1281,6 +1426,8 @@ public:
bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_dyncol_get>(thd, mem_root, this); }
};
@@ -1291,6 +1438,8 @@ public:
void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; };
const char *func_name() const{ return "column_list"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dyncol_list>(thd, mem_root, this); }
};
#endif /* ITEM_STRFUNC_INCLUDED */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 8bb12ce0ac8..21c633333f1 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -54,7 +54,7 @@ Item_subselect::Item_subselect(THD *thd_arg):
have_to_be_excluded(0),
inside_first_fix_fields(0), done_first_fix_fields(FALSE),
expr_cache(0), forced_const(FALSE), substitution(0), engine(0), eliminated(FALSE),
- changed(0), is_correlated(FALSE)
+ changed(0), is_correlated(FALSE), with_recursive_reference(0)
{
DBUG_ENTER("Item_subselect::Item_subselect");
DBUG_PRINT("enter", ("this: 0x%lx", (ulong) this));
@@ -564,6 +564,21 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
bool Item_subselect::is_expensive()
{
double examined_rows= 0;
+ bool all_are_simple= true;
+
+ /* check extremely simple select */
+ if (!unit->first_select()->next_select()) // no union
+ {
+ /*
+ such single selects works even without optimization because
+ can not makes loops
+ */
+ SELECT_LEX *sl= unit->first_select();
+ JOIN *join = sl->join;
+ if (join && !join->tables_list && !sl->first_inner_unit())
+ return false;
+ }
+
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
{
@@ -573,23 +588,27 @@ bool Item_subselect::is_expensive()
if (!cur_join)
return true;
- /* very simple subquery */
- if (!cur_join->tables_list && !sl->first_inner_unit())
- return false;
-
/*
If the subquery is not optimised or in the process of optimization
it supposed to be expensive
*/
- if (!cur_join->optimized)
+ if (cur_join->optimization_state != JOIN::OPTIMIZATION_DONE)
return true;
+ if (!cur_join->tables_list && !sl->first_inner_unit())
+ continue;
+
/*
Subqueries whose result is known after optimization are not expensive.
Such subqueries have all tables optimized away, thus have no join plan.
*/
if ((cur_join->zero_result_cause || !cur_join->tables_list))
- return false;
+ continue;
+
+ /*
+ This is not simple SELECT in union so we can not go by simple condition
+ */
+ all_are_simple= false;
/*
If a subquery is not optimized we cannot estimate its cost. A subquery is
@@ -610,7 +629,8 @@ bool Item_subselect::is_expensive()
examined_rows+= cur_join->get_examined_rows();
}
- return (examined_rows > thd->variables.expensive_subquery_limit);
+ return !all_are_simple &&
+ (examined_rows > thd->variables.expensive_subquery_limit);
}
@@ -785,7 +805,8 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
engine->cols() == 1 &&
optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
- UNCACHEABLE_SIDEEFFECT)));
+ UNCACHEABLE_SIDEEFFECT)) &&
+ !with_recursive_reference);
}
@@ -824,7 +845,8 @@ bool Item_in_subselect::expr_cache_is_needed(THD *thd)
{
return (optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
- UNCACHEABLE_SIDEEFFECT)));
+ UNCACHEABLE_SIDEEFFECT)) &&
+ !with_recursive_reference);
}
@@ -3678,7 +3700,7 @@ int subselect_single_select_engine::exec()
SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= select_lex;
- if (!join->optimized)
+ if (join->optimization_state == JOIN::NOT_OPTIMIZED)
{
SELECT_LEX_UNIT *unit= select_lex->master_unit();
@@ -3828,7 +3850,7 @@ int subselect_uniquesubquery_engine::scan_table()
}
table->file->extra_opt(HA_EXTRA_CACHE,
- current_thd->variables.read_buff_size);
+ get_thd()->variables.read_buff_size);
table->null_row= 0;
for (;;)
{
@@ -4266,7 +4288,7 @@ table_map subselect_union_engine::upper_select_const_tables()
void subselect_single_select_engine::print(String *str,
enum_query_type query_type)
{
- select_lex->print(thd, str, query_type);
+ select_lex->print(get_thd(), str, query_type);
}
@@ -4797,6 +4819,7 @@ my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root)
bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
{
+ THD *thd= get_thd();
select_union *result_sink;
/* Options to create_tmp_table. */
ulonglong tmp_create_options= thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS;
@@ -5319,7 +5342,8 @@ int subselect_hash_sj_engine::exec()
*/
thd->lex->current_select= materialize_engine->select_lex;
/* The subquery should be optimized, and materialized only once. */
- DBUG_ASSERT(materialize_join->optimized && !is_materialized);
+ DBUG_ASSERT(materialize_join->optimization_state == JOIN::OPTIMIZATION_DONE &&
+ !is_materialized);
materialize_join->exec();
if ((res= MY_TEST(materialize_join->error || thd->is_fatal_error ||
thd->is_error())))
@@ -6030,6 +6054,7 @@ bool
subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
MY_BITMAP *partial_match_key_parts)
{
+ THD *thd= get_thd();
/* The length in bytes of the rowids (positions) of tmp_table. */
uint rowid_length= tmp_table->file->ref_length;
ha_rows row_count= tmp_table->file->stats.records;
@@ -6568,7 +6593,7 @@ bool subselect_table_scan_engine::partial_match()
}
tmp_table->file->extra_opt(HA_EXTRA_CACHE,
- current_thd->variables.read_buff_size);
+ get_thd()->variables.read_buff_size);
for (;;)
{
error= tmp_table->file->ha_rnd_next(tmp_table->record[0]);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index c39735384ed..83340573e8a 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -126,7 +126,14 @@ public:
bool changed;
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
- bool is_correlated;
+ bool is_correlated;
+
+ /*
+ TRUE <=> the subquery contains a recursive reference in the FROM list
+ of one of its selects. In this case some of subquery optimization
+ strategies cannot be applied for the subquery;
+ */
+ bool with_recursive_reference;
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS};
@@ -250,6 +257,9 @@ public:
}
void init_expr_cache_tracker(THD *thd);
+
+ Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; }
+ Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
friend class select_result_interceptor;
@@ -755,7 +765,8 @@ public:
ROWID_MERGE_ENGINE, TABLE_SCAN_ENGINE};
subselect_engine(Item_subselect *si,
- select_result_interceptor *res)
+ select_result_interceptor *res):
+ thd(NULL)
{
result= res;
item= si;
@@ -771,7 +782,7 @@ public:
Should be called before prepare().
*/
void set_thd(THD *thd_arg);
- THD * get_thd() { return thd; }
+ THD * get_thd() { return thd ? thd : current_thd; }
virtual int prepare(THD *)= 0;
virtual void fix_length_and_dec(Item_cache** row)= 0;
/*
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index cc7a76213f0..83378c2e994 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -381,6 +381,16 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl->master_unit()->item->with_sum_func= 1;
}
thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL);
+
+ if ((thd->lex->describe & DESCRIBE_EXTENDED) && aggr_sel)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_WARN_AGGFUNC_DEPENDENCE,
+ ER_THD(thd, ER_WARN_AGGFUNC_DEPENDENCE),
+ func_name(),
+ thd->lex->current_select->select_number,
+ aggr_sel->select_number);
+ }
return FALSE;
}
@@ -1342,10 +1352,14 @@ void Item_sum_sum::add_helper(bool perform_removal)
{
if (perform_removal)
{
- DBUG_ASSERT(count > 0);
- my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
- dec_buffs + curr_dec_buff, val);
- count--;
+ if (count > 0)
+ {
+ my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1),
+ dec_buffs + curr_dec_buff, val);
+ count--;
+ }
+ else
+ DBUG_VOID_RETURN;
}
else
{
@@ -1359,7 +1373,7 @@ void Item_sum_sum::add_helper(bool perform_removal)
}
else
{
- if (perform_removal)
+ if (perform_removal && count > 0)
sum-= aggr->arg_val_real();
else
sum+= aggr->arg_val_real();
@@ -1367,8 +1381,10 @@ void Item_sum_sum::add_helper(bool perform_removal)
{
if (perform_removal)
{
- DBUG_ASSERT(count > 0);
- count--;
+ if (count > 0)
+ {
+ count--;
+ }
}
else
count++;
@@ -1422,7 +1438,7 @@ my_decimal *Item_sum_sum::val_decimal(my_decimal *val)
if (aggr)
aggr->endup();
if (Item_sum_sum::result_type() == DECIMAL_RESULT)
- return (dec_buffs + curr_dec_buff);
+ return null_value ? NULL : (dec_buffs + curr_dec_buff);
return val_decimal_from_real(val);
}
@@ -1588,7 +1604,8 @@ void Item_sum_count::remove()
DBUG_ASSERT(aggr->Aggrtype() == Aggregator::SIMPLE_AGGREGATOR);
if (aggr->arg_is_null(false))
return;
- count--;
+ if (count > 0)
+ count--;
}
longlong Item_sum_count::val_int()
@@ -1692,8 +1709,8 @@ void Item_sum_avg::remove()
Item_sum_sum::remove();
if (!aggr->arg_is_null(true))
{
- DBUG_ASSERT(count > 0);
- count--;
+ if (count > 0)
+ count--;
}
}
@@ -1756,6 +1773,8 @@ double Item_sum_std::val_real()
{
DBUG_ASSERT(fixed == 1);
double nr= Item_sum_variance::val_real();
+ if (my_isinf(nr))
+ return DBL_MAX;
DBUG_ASSERT(nr >= 0.0);
return sqrt(nr);
}
@@ -2178,6 +2197,9 @@ bool Item_sum_bit::clear_as_window()
bool Item_sum_bit::remove_as_window(ulonglong value)
{
DBUG_ASSERT(as_window_function);
+ if (num_values_added == 0)
+ return 0; // Nothing to remove.
+
for (int i= 0; i < NUM_BIT_COUNTERS; i++)
{
if (!bit_counters[i])
@@ -2188,7 +2210,7 @@ bool Item_sum_bit::remove_as_window(ulonglong value)
}
bit_counters[i]-= (value & (1 << i)) ? 1 : 0;
}
- DBUG_ASSERT(num_values_added > 0);
+
// Prevent overflow;
num_values_added = std::min(num_values_added, num_values_added - 1);
set_bits_from_counters();
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 4cb5529e1ce..16835441125 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -543,7 +543,8 @@ public:
virtual void clear()= 0;
virtual bool add()= 0;
virtual bool setup(THD *thd) { return false; }
-
+
+ virtual bool supports_removal() const { return false; }
virtual void remove() { DBUG_ASSERT(0); }
virtual void cleanup();
@@ -768,6 +769,13 @@ public:
}
Item *copy_or_same(THD* thd);
void remove();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_sum>(thd, mem_root, this); }
+
+ bool supports_removal() const
+ {
+ return true;
+ }
private:
void add_helper(bool perform_removal);
@@ -825,6 +833,13 @@ class Item_sum_count :public Item_sum_int
return has_with_distinct() ? "count(distinct " : "count(";
}
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_count>(thd, mem_root, this); }
+
+ bool supports_removal() const
+ {
+ return true;
+ }
};
@@ -872,6 +887,13 @@ public:
count= 0;
Item_sum_sum::cleanup();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_avg>(thd, mem_root, this); }
+
+ bool supports_removal() const
+ {
+ return true;
+ }
};
@@ -930,6 +952,8 @@ public:
count= 0;
Item_sum_num::cleanup();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_variance>(thd, mem_root, this); }
};
/*
@@ -949,6 +973,8 @@ class Item_sum_std :public Item_sum_variance
Item *result_item(THD *thd, Field *field);
const char *func_name() const { return "std("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_std>(thd, mem_root, this); }
};
// This class is a string or number function depending on num_func
@@ -1014,6 +1040,8 @@ public:
bool add();
const char *func_name() const { return "min("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_min>(thd, mem_root, this); }
};
@@ -1027,6 +1055,8 @@ public:
bool add();
const char *func_name() const { return "max("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_max>(thd, mem_root, this); }
};
@@ -1075,6 +1105,11 @@ public:
DBUG_ASSERT(0);
}
+ bool supports_removal() const
+ {
+ return true;
+ }
+
protected:
static const int NUM_BIT_COUNTERS= 64;
ulonglong reset_bits,bits;
@@ -1101,6 +1136,8 @@ public:
bool add();
const char *func_name() const { return "bit_or("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_or>(thd, mem_root, this); }
private:
void set_bits_from_counters();
@@ -1116,6 +1153,8 @@ public:
bool add();
const char *func_name() const { return "bit_and("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_and>(thd, mem_root, this); }
private:
void set_bits_from_counters();
@@ -1129,6 +1168,8 @@ public:
bool add();
const char *func_name() const { return "bit_xor("; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_xor>(thd, mem_root, this); }
private:
void set_bits_from_counters();
@@ -1187,6 +1228,8 @@ public:
my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); }
String *val_str(String *str) { return val_string_from_real(str); }
double val_real();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_avg_field_double>(thd, mem_root, this); }
};
@@ -1206,6 +1249,8 @@ public:
longlong val_int() { return val_int_from_decimal(); }
String *val_str(String *str) { return val_string_from_decimal(str); }
my_decimal *val_decimal(my_decimal *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_avg_field_decimal>(thd, mem_root, this); }
};
@@ -1226,6 +1271,8 @@ public:
bool is_null() { update_null_value(); return null_value; }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
enum Item_result result_type () const { return REAL_RESULT; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_variance_field>(thd, mem_root, this); }
};
@@ -1237,6 +1284,8 @@ public:
{ }
enum Type type() const { return FIELD_STD_ITEM; }
double val_real();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_std_field>(thd, mem_root, this); }
};
@@ -1324,6 +1373,8 @@ class Item_sum_udf_float :public Item_udf_sum
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
void fix_length_and_dec() { fix_num_length_and_dec(); }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_udf_float>(thd, mem_root, this); }
};
@@ -1345,6 +1396,8 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
void fix_length_and_dec() { decimals=0; max_length=21; }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_udf_int>(thd, mem_root, this); }
};
@@ -1385,6 +1438,8 @@ public:
enum_field_types field_type() const { return string_field_type(); }
void fix_length_and_dec();
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_udf_str>(thd, mem_root, this); }
};
@@ -1405,6 +1460,8 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
void fix_length_and_dec() { fix_num_length_and_dec(); }
Item *copy_or_same(THD* thd);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_udf_decimal>(thd, mem_root, this); }
};
#else /* Dummy functions to get sql_yacc.cc compiled */
@@ -1598,6 +1655,8 @@ public:
virtual void print(String *str, enum_query_type query_type);
virtual bool change_context_processor(void *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_group_concat>(thd, mem_root, this); }
};
#endif /* ITEM_SUM_INCLUDED */
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a4a694fe5eb..41dc96717fe 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -2201,26 +2201,26 @@ void Item_extract::fix_length_and_dec()
{
maybe_null=1; // If wrong date
switch (int_type) {
- case INTERVAL_YEAR: max_length=4; date_value=1; break;
- case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
- case INTERVAL_QUARTER: max_length=2; date_value=1; break;
- case INTERVAL_MONTH: max_length=2; date_value=1; break;
- case INTERVAL_WEEK: max_length=2; date_value=1; break;
- case INTERVAL_DAY: max_length=2; date_value=1; break;
- case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break;
- case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break;
- case INTERVAL_DAY_SECOND: max_length=13; date_value=0; break;
- case INTERVAL_HOUR: max_length=2; date_value=0; break;
- case INTERVAL_HOUR_MINUTE: max_length=4; date_value=0; break;
- case INTERVAL_HOUR_SECOND: max_length=6; date_value=0; break;
- case INTERVAL_MINUTE: max_length=2; date_value=0; break;
- case INTERVAL_MINUTE_SECOND: max_length=4; date_value=0; break;
- case INTERVAL_SECOND: max_length=2; date_value=0; break;
- case INTERVAL_MICROSECOND: max_length=2; date_value=0; break;
- case INTERVAL_DAY_MICROSECOND: max_length=20; date_value=0; break;
- case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break;
- case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break;
- case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break;
+ case INTERVAL_YEAR: set_date_length(4); break; // YYYY
+ case INTERVAL_YEAR_MONTH: set_date_length(6); break; // YYYYMM
+ case INTERVAL_QUARTER: set_date_length(2); break; // 1..4
+ case INTERVAL_MONTH: set_date_length(2); break; // MM
+ case INTERVAL_WEEK: set_date_length(2); break; // 0..52
+ case INTERVAL_DAY: set_date_length(2); break; // DD
+ case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh
+ case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm
+ case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss
+ case INTERVAL_HOUR: set_time_length(2); break; // hh
+ case INTERVAL_HOUR_MINUTE: set_time_length(4); break; // hhmm
+ case INTERVAL_HOUR_SECOND: set_time_length(6); break; // hhmmss
+ case INTERVAL_MINUTE: set_time_length(2); break; // mm
+ case INTERVAL_MINUTE_SECOND: set_time_length(4); break; // mmss
+ case INTERVAL_SECOND: set_time_length(2); break; // ss
+ case INTERVAL_MICROSECOND: set_time_length(6); break; // ffffff
+ case INTERVAL_DAY_MICROSECOND: set_time_length(14); break; // DDhhmmssffffff
+ case INTERVAL_HOUR_MICROSECOND: set_time_length(12); break; // hhmmssffffff
+ case INTERVAL_MINUTE_MICROSECOND: set_time_length(10); break; // mmssffffff
+ case INTERVAL_SECOND_MICROSECOND: set_time_length(8); break; // ssffffff
case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */
}
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 80e7c1010d0..a853c63128d 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -53,6 +53,8 @@ public:
{
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_period_add>(thd, mem_root, this); }
};
@@ -67,6 +69,8 @@ public:
decimals=0;
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_period_diff>(thd, mem_root, this); }
};
@@ -90,6 +94,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_to_days>(thd, mem_root, this); }
};
@@ -123,6 +129,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_to_seconds>(thd, mem_root, this); }
};
@@ -144,6 +152,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dayofmonth>(thd, mem_root, this); }
};
@@ -178,6 +188,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_month>(thd, mem_root, this); }
};
@@ -196,8 +208,11 @@ public:
}
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
+
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_monthname>(thd, mem_root, this); }
};
@@ -219,6 +234,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_dayofyear>(thd, mem_root, this); }
};
@@ -240,6 +257,8 @@ public:
{
return !has_time_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_hour>(thd, mem_root, this); }
};
@@ -261,6 +280,8 @@ public:
{
return !has_time_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_minute>(thd, mem_root, this); }
};
@@ -282,6 +303,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_quarter>(thd, mem_root, this); }
};
@@ -303,6 +326,8 @@ public:
{
return !has_time_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_second>(thd, mem_root, this); }
};
@@ -318,6 +343,8 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_week>(thd, mem_root, this); }
};
class Item_func_yearweek :public Item_int_func
@@ -338,6 +365,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_yearweek>(thd, mem_root, this); }
};
@@ -361,6 +390,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_year>(thd, mem_root, this); }
};
@@ -396,6 +427,8 @@ public:
{
return !has_date_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_weekday>(thd, mem_root, this); }
};
class Item_func_dayname :public Item_func_weekday
@@ -411,7 +444,7 @@ class Item_func_dayname :public Item_func_weekday
bool check_partition_func_processor(void *int_arg) {return TRUE;}
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
};
@@ -468,6 +501,8 @@ public:
}
longlong int_op();
my_decimal *decimal_op(my_decimal* buf);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_unix_timestamp>(thd, mem_root, this); }
};
@@ -487,6 +522,8 @@ public:
}
longlong int_op();
my_decimal *decimal_op(my_decimal* buf);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_time_to_sec>(thd, mem_root, this); }
};
@@ -625,6 +662,8 @@ public:
Item_func_curtime_local(THD *thd, uint dec): Item_func_curtime(thd, dec) {}
const char *func_name() const { return "curtime"; }
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_curtime_local>(thd, mem_root, this); }
};
@@ -634,6 +673,8 @@ public:
Item_func_curtime_utc(THD *thd, uint dec): Item_func_curtime(thd, dec) {}
const char *func_name() const { return "utc_time"; }
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_curtime_utc>(thd, mem_root, this); }
};
@@ -661,6 +702,8 @@ public:
Item_func_curdate_local(THD *thd): Item_func_curdate(thd) {}
const char *func_name() const { return "curdate"; }
void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_curdate_local>(thd, mem_root, this); }
};
@@ -670,6 +713,8 @@ public:
Item_func_curdate_utc(THD *thd): Item_func_curdate(thd) {}
const char *func_name() const { return "utc_date"; }
void store_now_in_TIME(THD* thd, MYSQL_TIME *now_time);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_curdate_utc>(thd, mem_root, this); }
};
@@ -703,6 +748,8 @@ public:
const char *func_name() const { return "now"; }
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
virtual enum Functype functype() const { return NOW_FUNC; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_now_local>(thd, mem_root, this); }
};
@@ -718,7 +765,8 @@ public:
return mark_unsupported_function(func_name(), "()", arg,
VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC);
}
-
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_now_utc>(thd, mem_root, this); }
};
@@ -741,6 +789,8 @@ public:
VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC);
}
virtual enum Functype functype() const { return SYSDATE_FUNC; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sysdate_local>(thd, mem_root, this); }
};
@@ -756,6 +806,8 @@ public:
{
return has_date_args() || has_time_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_from_days>(thd, mem_root, this); }
};
@@ -776,8 +828,10 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
bool check_vcol_func_processor(void *arg)
{
- return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
+ return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_date_format>(thd, mem_root, this); }
};
@@ -789,6 +843,8 @@ class Item_func_from_unixtime :public Item_datetimefunc
const char *func_name() const { return "from_unixtime"; }
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_from_unixtime>(thd, mem_root, this); }
};
@@ -823,6 +879,8 @@ class Item_func_convert_tz :public Item_datetimefunc
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void cleanup();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_convert_tz>(thd, mem_root, this); }
};
@@ -837,6 +895,8 @@ public:
Item_timefunc::fix_length_and_dec();
}
const char *func_name() const { return "sec_to_time"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_sec_to_time>(thd, mem_root, this); }
};
@@ -855,16 +915,65 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_date_add_interval>(thd, mem_root, this); }
};
class Item_extract :public Item_int_func
{
bool date_value;
+ void set_date_length(uint32 length)
+ {
+ /*
+ Although DATE components (e.g. YEAR, YEAR_MONTH, QUARTER, MONTH, WEEK)
+ cannot have a sign, we should probably still add +1,
+ because all around the code we assume that max_length is sign inclusive.
+ Another options is to set unsigned_flag to "true".
+ */
+ max_length= length; //QQ: see above
+ date_value= true;
+ }
+ void set_time_length(uint32 length)
+ {
+ max_length= length + 1/*sign*/;
+ date_value= false;
+ }
public:
const interval_type int_type; // keep it public
Item_extract(THD *thd, interval_type type_arg, Item *a):
Item_int_func(thd, a), int_type(type_arg) {}
+ enum_field_types field_type() const
+ {
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ case INTERVAL_WEEK:
+ case INTERVAL_DAY:
+ case INTERVAL_DAY_HOUR:
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_HOUR:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_MINUTE:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_SECOND:
+ case INTERVAL_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ return MYSQL_TYPE_LONG;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ return MYSQL_TYPE_LONGLONG;
+ case INTERVAL_LAST:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return MYSQL_TYPE_LONGLONG;
+ }
longlong val_int();
enum Functype functype() const { return EXTRACT_FUNC; }
const char *func_name() const { return "extract"; }
@@ -909,6 +1018,11 @@ class Item_extract :public Item_int_func
}
return true;
}
+ Field *create_field_for_create_select(TABLE *table)
+ { return tmp_table_field_from_field_type(table, false, false); }
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_extract>(thd, mem_root, this); }
};
@@ -933,6 +1047,8 @@ public:
void fix_length_and_dec();
void print(String *str, enum_query_type query_type);
bool need_parentheses_in_default() { return true; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_char_typecast>(thd, mem_root, this); }
};
@@ -958,6 +1074,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *cast_type() const { return "date"; }
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_date_typecast>(thd, mem_root, this); }
};
@@ -970,6 +1088,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *cast_type() const { return "time"; }
enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_time_typecast>(thd, mem_root, this); }
};
@@ -982,6 +1102,8 @@ public:
const char *cast_type() const { return "datetime"; }
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_datetime_typecast>(thd, mem_root, this); }
};
@@ -993,6 +1115,8 @@ public:
const char *func_name() const { return "makedate"; }
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_makedate>(thd, mem_root, this); }
};
@@ -1009,6 +1133,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
void print(String *str, enum_query_type query_type);
const char *func_name() const { return "add_time"; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_add_time>(thd, mem_root, this); }
};
class Item_func_timediff :public Item_timefunc
@@ -1023,6 +1149,8 @@ public:
Item_timefunc::fix_length_and_dec();
}
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_timediff>(thd, mem_root, this); }
};
class Item_func_maketime :public Item_timefunc
@@ -1038,6 +1166,8 @@ public:
}
const char *func_name() const { return "maketime"; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_maketime>(thd, mem_root, this); }
};
@@ -1058,6 +1188,8 @@ public:
{
return !has_time_args();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_microsecond>(thd, mem_root, this); }
};
@@ -1075,6 +1207,8 @@ public:
maybe_null=1;
}
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_timestamp_diff>(thd, mem_root, this); }
};
@@ -1099,6 +1233,8 @@ public:
fix_length_and_charset(17, default_charset());
}
virtual void print(String *str, enum_query_type query_type);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_get_format>(thd, mem_root, this); }
};
@@ -1117,6 +1253,8 @@ public:
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *func_name() const { return "str_to_date"; }
void fix_length_and_dec();
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_str_to_date>(thd, mem_root, this); }
};
@@ -1126,6 +1264,8 @@ public:
Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "last_day"; }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_last_day>(thd, mem_root, this); }
};
#endif /* ITEM_TIMEFUNC_INCLUDED */
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index d157d545dad..c8ea979900c 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -41,7 +41,7 @@ Item_window_func::resolve_window_name(THD *thd)
return true;
}
- return false;
+ return false;
}
@@ -154,7 +154,7 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
/*
- This must be called before advance_window() can be called.
+ This must be called before attempting to compute the window function values.
@detail
If we attempt to do it in fix_fields(), partition_fields will refer
@@ -162,30 +162,25 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
We need it to refer to temp.table columns.
*/
-void Item_window_func::setup_partition_border_check(THD *thd)
-{
- partition_tracker.init(thd, window_spec->partition_list);
- window_func()->setup_window_func(thd, window_spec);
-}
-
-
void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: move this into Item_window_func? */
- peer_tracker.init(thd, window_spec->order_list);
+ peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
+ peer_tracker->init();
clear();
}
void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: consider moving this && Item_sum_rank's implementation */
- peer_tracker.init(thd, window_spec->order_list);
+ peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
+ peer_tracker->init();
clear();
}
bool Item_sum_dense_rank::add()
{
- if (peer_tracker.check_if_next_group() || first_add)
+ if (peer_tracker->check_if_next_group() || first_add)
{
first_add= false;
dense_rank++;
@@ -198,7 +193,7 @@ bool Item_sum_dense_rank::add()
bool Item_sum_rank::add()
{
row_number++;
- if (peer_tracker.check_if_next_group())
+ if (peer_tracker->check_if_next_group())
{
/* Row value changed */
cur_rank= row_number;
@@ -206,25 +201,10 @@ bool Item_sum_rank::add()
return false;
}
-bool Item_window_func::check_if_partition_changed()
-{
- return partition_tracker.check_if_next_group();
-}
-
-void Item_window_func::advance_window()
-{
- if (check_if_partition_changed())
- {
- /* Next partition */
- window_func()->clear();
- }
- window_func()->add();
-}
-
bool Item_sum_percent_rank::add()
{
row_number++;
- if (peer_tracker.check_if_next_group())
+ if (peer_tracker->check_if_next_group())
{
/* Row value changed. */
cur_rank= row_number;
@@ -235,8 +215,7 @@ bool Item_sum_percent_rank::add()
void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: move this into Item_window_func? */
- peer_tracker.init(thd, window_spec->order_list);
+ peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
+ peer_tracker->init();
clear();
}
-
-
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index 9d2fa135e29..433b3f6c4fd 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -12,25 +12,19 @@ int test_if_group_changed(List<Cached_item> &list);
/* A wrapper around test_if_group_changed */
class Group_bound_tracker
{
- List<Cached_item> group_fields;
- /*
- During the first check_if_next_group, the list of cached_items is not
- initialized. The compare function will return that the items match if
- the field's value is the same as the Cached_item's default value (0).
- This flag makes sure that we always return true during the first check.
-
- XXX This is better to be implemented within test_if_group_changed, but
- since it is used in other parts of the codebase, we keep it here for now.
- */
- bool first_check;
public:
- void init(THD *thd, SQL_I_List<ORDER> *list)
+
+ Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list)
{
for (ORDER *curr = list->first; curr; curr=curr->next)
{
Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
group_fields.push_back(tmp);
}
+ }
+
+ void init()
+ {
first_check= true;
}
@@ -76,6 +70,19 @@ public:
}
return 0;
}
+
+private:
+ List<Cached_item> group_fields;
+ /*
+ During the first check_if_next_group, the list of cached_items is not
+ initialized. The compare function will return that the items match if
+ the field's value is the same as the Cached_item's default value (0).
+ This flag makes sure that we always return true during the first check.
+
+ XXX This is better to be implemented within test_if_group_changed, but
+ since it is used in other parts of the codebase, we keep it here for now.
+ */
+ bool first_check;
};
/*
@@ -92,19 +99,22 @@ class Item_sum_row_number: public Item_sum_int
longlong count;
public:
+
+ Item_sum_row_number(THD *thd)
+ : Item_sum_int(thd), count(0) {}
+
void clear()
{
count= 0;
}
- bool add()
+
+ bool add()
{
count++;
- return false;
+ return false;
}
- void update_field() {}
- Item_sum_row_number(THD *thd)
- : Item_sum_int(thd), count(0) {}
+ void update_field() {}
enum Sumfunctype sum_func() const
{
@@ -119,7 +129,9 @@ public:
{
return "row_number(";
}
-
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_row_number>(thd, mem_root, this); }
};
@@ -145,9 +157,12 @@ class Item_sum_rank: public Item_sum_int
protected:
longlong row_number; // just ROW_NUMBER()
longlong cur_rank; // current value
-
- Group_bound_tracker peer_tracker;
+
+ Group_bound_tracker *peer_tracker;
public:
+
+ Item_sum_rank(THD *thd) : Item_sum_int(thd), peer_tracker(NULL) {}
+
void clear()
{
/* This is called on partition start */
@@ -168,10 +183,6 @@ public:
TODO: ^^ what does this do ? It is not called ever?
*/
-public:
- Item_sum_rank(THD *thd)
- : Item_sum_int(thd) {}
-
enum Sumfunctype sum_func () const
{
return RANK_FUNC;
@@ -183,11 +194,19 @@ public:
}
void setup_window_func(THD *thd, Window_spec *window_spec);
+
void cleanup()
{
- peer_tracker.cleanup();
+ if (peer_tracker)
+ {
+ peer_tracker->cleanup();
+ delete peer_tracker;
+ peer_tracker= NULL;
+ }
Item_sum_int::cleanup();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_rank>(thd, mem_root, this); }
};
@@ -214,7 +233,7 @@ class Item_sum_dense_rank: public Item_sum_int
{
longlong dense_rank;
bool first_add;
- Group_bound_tracker peer_tracker;
+ Group_bound_tracker *peer_tracker;
public:
/*
XXX(cvicentiu) This class could potentially be implemented in the rank
@@ -233,7 +252,7 @@ class Item_sum_dense_rank: public Item_sum_int
}
Item_sum_dense_rank(THD *thd)
- : Item_sum_int(thd), dense_rank(0), first_add(true) {}
+ : Item_sum_int(thd), dense_rank(0), first_add(true), peer_tracker(NULL) {}
enum Sumfunctype sum_func () const
{
return DENSE_RANK_FUNC;
@@ -248,9 +267,16 @@ class Item_sum_dense_rank: public Item_sum_int
void cleanup()
{
- peer_tracker.cleanup();
+ if (peer_tracker)
+ {
+ peer_tracker->cleanup();
+ delete peer_tracker;
+ peer_tracker= NULL;
+ }
Item_sum_int::cleanup();
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_dense_rank>(thd, mem_root, this); }
};
/*
@@ -289,7 +315,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
{
public:
Item_sum_percent_rank(THD *thd)
- : Item_sum_window_with_row_count(thd), cur_rank(1) {}
+ : Item_sum_window_with_row_count(thd), cur_rank(1), peer_tracker(NULL) {}
longlong val_int()
{
@@ -342,16 +368,23 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
}
void setup_window_func(THD *thd, Window_spec *window_spec);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_percent_rank>(thd, mem_root, this); }
private:
longlong cur_rank; // Current rank of the current row.
longlong row_number; // Value if this were ROW_NUMBER() function.
- Group_bound_tracker peer_tracker;
+ Group_bound_tracker *peer_tracker;
void cleanup()
{
- peer_tracker.cleanup();
+ if (peer_tracker)
+ {
+ peer_tracker->cleanup();
+ delete peer_tracker;
+ peer_tracker= NULL;
+ }
Item_sum_num::cleanup();
}
};
@@ -419,6 +452,9 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count
decimals = 10; // TODO-cvicentiu find out how many decimals the standard
// requires.
}
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_cume_dist>(thd, mem_root, this); }
private:
ulonglong current_row_count_;
@@ -487,6 +523,9 @@ class Item_sum_ntile : public Item_sum_window_with_row_count
enum Item_result result_type () const { return INT_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_sum_ntile>(thd, mem_root, this); }
private:
longlong get_num_quantiles() { return args[0]->val_int(); }
@@ -502,12 +541,6 @@ public:
public:
Window_spec *window_spec;
- /*
- This stores the data about the partition we're currently in.
- advance_window() uses this to tell when we've left one partition and
- entered another
- */
- Group_bound_tracker partition_tracker;
public:
Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)
: Item_func_or_sum(thd, (Item *) win_func),
@@ -600,9 +633,6 @@ public:
*/
void setup_partition_border_check(THD *thd);
- void advance_window();
- bool check_if_partition_changed();
-
enum_field_types field_type() const
{
return ((Item_sum *) args[0])->field_type();
@@ -751,6 +781,8 @@ public:
bool fix_fields(THD *thd, Item **ref);
bool resolve_window_name(THD *thd);
+
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index bf5abc882f2..cbbdeea0205 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -250,6 +250,8 @@ public:
Item_nodeset_func(thd, pxml) {}
const char *func_name() const { return "xpath_rootelement"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_rootelement>(thd, mem_root, this); }
};
@@ -261,6 +263,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_union"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_union>(thd, mem_root, this); }
};
@@ -293,6 +297,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_selfbyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_selfbyname>(thd, mem_root, this); }
};
@@ -305,6 +311,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_childbyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_childbyname>(thd, mem_root, this); }
};
@@ -319,6 +327,8 @@ public:
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_descendantbyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, mem_root, this); }
};
@@ -333,6 +343,8 @@ public:
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_ancestorbyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, mem_root, this); }
};
@@ -345,6 +357,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_parentbyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_parentbyname>(thd, mem_root, this); }
};
@@ -357,6 +371,8 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_attributebyname"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_attributebyname>(thd, mem_root, this); }
};
@@ -372,6 +388,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_predicate"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_predicate>(thd, mem_root, this); }
};
@@ -383,6 +401,8 @@ public:
Item_nodeset_func(thd, a, b, pxml) { }
const char *func_name() const { return "xpath_elementbyindex"; }
String *val_nodeset(String *nodeset);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, mem_root, this); }
};
@@ -422,6 +442,8 @@ public:
}
return args[0]->val_real() ? 1 : 0;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_xpath_cast_bool>(thd, mem_root, this); }
};
@@ -434,6 +456,8 @@ public:
Item_xpath_cast_number(THD *thd, Item *a): Item_real_func(thd, a) {}
const char *func_name() const { return "xpath_cast_number"; }
virtual double val_real() { return args[0]->val_real(); }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_xpath_cast_number>(thd, mem_root, this); }
};
@@ -449,6 +473,8 @@ public:
String *val_nodeset(String *res)
{ return string_cache; }
void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; }
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_context_cache>(thd, mem_root, this); }
};
@@ -468,6 +494,8 @@ public:
return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
return 0;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xpath_position>(thd, mem_root, this); }
};
@@ -489,6 +517,8 @@ public:
return predicate_supplied_context_size;
return res->length() / sizeof(MY_XPATH_FLT);
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xpath_count>(thd, mem_root, this); }
};
@@ -532,6 +562,8 @@ public:
}
return sum;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xpath_sum>(thd, mem_root, this); }
};
@@ -608,6 +640,8 @@ public:
}
return 0;
}
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_nodeset_to_const_comparator>(thd, mem_root, this); }
};
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index ed12793742e..3c58955c96a 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -101,6 +101,8 @@ public:
Item_xml_str_func(thd, a, b) {}
const char *func_name() const { return "extractvalue"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xml_extractvalue>(thd, mem_root, this); }
};
@@ -115,6 +117,8 @@ public:
Item_xml_str_func(thd, a, b, c) {}
const char *func_name() const { return "updatexml"; }
String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_xml_update>(thd, mem_root, this); }
};
#endif /* ITEM_XMLFUNC_INCLUDED */
diff --git a/sql/lock.cc b/sql/lock.cc
index 2e44786d6fe..8aebc1f30d9 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -90,6 +90,7 @@ extern HASH open_cache;
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
+
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
{ 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
@@ -244,6 +245,39 @@ void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
/**
+ Scan array of tables for access types; update transaction tracker
+ accordingly.
+
+ @param thd The current thread.
+ @param tables An array of pointers to the tables to lock.
+ @param count The number of tables to lock.
+*/
+
+#ifndef EMBEDDED_LIBRARY
+static void track_table_access(THD *thd, TABLE **tables, size_t count)
+{
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ {
+ Transaction_state_tracker *tst= (Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
+
+ while (count--)
+ {
+ TABLE *t= tables[count];
+
+ if (t)
+ tst->add_trx_state(thd, t->reginfo.lock_type,
+ t->file->has_transactions());
+ }
+ }
+}
+#else
+#define track_table_access(A,B,C)
+#endif //EMBEDDED_LIBRARY
+
+
+
+/**
Lock tables.
@param thd The current thread.
@@ -280,6 +314,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
my_free(sql_lock);
sql_lock= 0;
}
+
+ track_table_access(thd, tables, count);
+
DBUG_RETURN(sql_lock);
}
@@ -1036,10 +1073,21 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
m_mdl_blocks_commits_lock= NULL;
#ifdef WITH_WSREP
- if (WSREP_ON)
+ if (WSREP(thd) || wsrep_node_is_donor())
{
wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
wsrep->resume(wsrep);
+ /* resync here only if we did implicit desync earlier */
+ if (!wsrep_desync && wsrep_node_is_synced())
+ {
+ int ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"), thd->query());
+ DBUG_VOID_RETURN;
+ }
+ }
}
#endif /* WITH_WSREP */
}
@@ -1079,14 +1127,11 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
DBUG_RETURN(0);
#ifdef WITH_WSREP
- if (WSREP_ON && m_mdl_blocks_commits_lock)
+ if (WSREP(thd) && m_mdl_blocks_commits_lock)
{
WSREP_DEBUG("GRL was in block commit mode when entering "
"make_global_read_lock_block_commit");
- thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
- m_mdl_blocks_commits_lock= NULL;
- wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
- wsrep->resume(wsrep);
+ DBUG_RETURN(FALSE);
}
#endif /* WITH_WSREP */
@@ -1100,7 +1145,43 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
#ifdef WITH_WSREP
- if (WSREP_ON)
+ /* Native threads should bail out before wsrep oprations to follow.
+ Donor servicing thread is an exception, it should pause provider but not desync,
+ as it is already desynced in donor state
+ */
+ if (!WSREP(thd) && !wsrep_node_is_donor())
+ {
+ DBUG_RETURN(FALSE);
+ }
+
+ /* if already desynced or donor, avoid double desyncing
+ if not in PC and synced, desyncing is not possible either
+ */
+ if (wsrep_desync || !wsrep_node_is_synced())
+ {
+ WSREP_DEBUG("desync set upfont, skipping implicit desync for FTWRL: %d",
+ wsrep_desync);
+ }
+ else
+ {
+ int rcode;
+ WSREP_DEBUG("running implicit desync for node");
+ rcode = wsrep->desync(wsrep);
+ if (rcode != WSREP_OK)
+ {
+ WSREP_WARN("FTWRL desync failed %d for schema: %s, query: %s",
+ rcode, (thd->db ? thd->db : "(null)"), thd->query());
+ my_message(ER_LOCK_DEADLOCK, "wsrep desync failed for FTWRL", MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ long long ret = wsrep->pause(wsrep);
+ if (ret >= 0)
+ {
+ wsrep_locked_seqno= ret;
+ }
+ else if (ret != -ENOSYS) /* -ENOSYS - no provider */
{
long long ret = wsrep->pause(wsrep);
if (ret >= 0)
diff --git a/sql/log.cc b/sql/log.cc
index fa8f10e5464..569942ac485 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3144,6 +3144,22 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
}
+/*
+ Print some additional information about addition/removal of
+ XID list entries.
+ TODO: Remove once MDEV-9510 is fixed.
+*/
+#ifdef WITH_WSREP
+#define WSREP_XID_LIST_ENTRY(X, Y) \
+ if (wsrep_debug) \
+ { \
+ char buf[FN_REFLEN]; \
+ strmake(buf, Y->binlog_name, Y->binlog_name_len); \
+ WSREP_DEBUG(X, buf, Y->binlog_id); \
+ }
+#else
+#define WSREP_XID_LIST_ENTRY(X, Y) do { } while(0)
+#endif
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
:reset_master_pending(0), mark_xid_done_waiting(0),
@@ -3212,6 +3228,8 @@ void MYSQL_BIN_LOG::cleanup()
*/
DBUG_ASSERT(b->xid_count == 0);
DBUG_ASSERT(!binlog_xid_count_list.head());
+ WSREP_XID_LIST_ENTRY("MYSQL_BIN_LOG::cleanup(): Removing xid_list_entry "
+ "for %s (%lu)", b);
my_free(b);
}
@@ -3702,7 +3720,14 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
new_xid_list_entry->binlog_id= current_binlog_id;
/* Remove any initial entries with no pending XIDs. */
while ((b= binlog_xid_count_list.head()) && b->xid_count == 0)
+ {
+ WSREP_XID_LIST_ENTRY("MYSQL_BIN_LOG::open(): Removing xid_list_entry for "
+ "%s (%lu)", b);
my_free(binlog_xid_count_list.get());
+ }
+ mysql_cond_broadcast(&COND_xid_list);
+ WSREP_XID_LIST_ENTRY("MYSQL_BIN_LOG::open(): Adding new xid_list_entry for "
+ "%s (%lu)", new_xid_list_entry);
binlog_xid_count_list.push_back(new_xid_list_entry);
mysql_mutex_unlock(&LOCK_xid_list);
@@ -4237,8 +4262,11 @@ err:
if (b->binlog_id == current_binlog_id)
break;
DBUG_ASSERT(b->xid_count == 0);
+ WSREP_XID_LIST_ENTRY("MYSQL_BIN_LOG::reset_logs(): Removing "
+ "xid_list_entry for %s (%lu)", b);
my_free(binlog_xid_count_list.get());
}
+ mysql_cond_broadcast(&COND_xid_list);
reset_master_pending--;
mysql_mutex_unlock(&LOCK_xid_list);
}
@@ -4249,6 +4277,26 @@ err:
}
+void MYSQL_BIN_LOG::wait_for_last_checkpoint_event()
+{
+ mysql_mutex_lock(&LOCK_xid_list);
+ for (;;)
+ {
+ if (binlog_xid_count_list.is_last(binlog_xid_count_list.head()))
+ break;
+ mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
+ }
+ mysql_mutex_unlock(&LOCK_xid_list);
+
+ /*
+ LOCK_xid_list and LOCK_log are chained, so the LOCK_log will only be
+ obtained after mark_xid_done() has written the last checkpoint event.
+ */
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
+}
+
+
/**
Delete relay log files prior to rli->group_relay_log_name
(i.e. all logs which are not involved in a non-finished group
@@ -7699,7 +7747,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
last= current->next == NULL;
if (!current->error &&
RUN_HOOK(binlog_storage, after_sync,
- (current->thd, log_file_name,
+ (current->thd, current->cache_mngr->last_commit_pos_file,
current->cache_mngr->last_commit_pos_offset,
first, last)))
{
@@ -9405,7 +9453,7 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
*/
if (unlikely(reset_master_pending))
{
- mysql_cond_signal(&COND_xid_list);
+ mysql_cond_broadcast(&COND_xid_list);
mysql_mutex_unlock(&LOCK_xid_list);
DBUG_VOID_RETURN;
}
@@ -9443,8 +9491,7 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
mysql_mutex_lock(&LOCK_log);
mysql_mutex_lock(&LOCK_xid_list);
--mark_xid_done_waiting;
- if (unlikely(reset_master_pending))
- mysql_cond_signal(&COND_xid_list);
+ mysql_cond_broadcast(&COND_xid_list);
/* We need to reload current_binlog_id due to release/re-take of lock. */
current= current_binlog_id;
@@ -9459,6 +9506,8 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
DBUG_ASSERT(b);
if (b->binlog_id == current || b->xid_count > 0)
break;
+ WSREP_XID_LIST_ENTRY("TC_LOG_BINLOG::mark_xid_done(): Removing "
+ "xid_list_entry for %s (%lu)", b);
my_free(binlog_xid_count_list.get());
}
diff --git a/sql/log.h b/sql/log.h
index a74a01f9c67..8ddf8641cfa 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -791,6 +791,7 @@ public:
bool reset_logs(THD* thd, bool create_new_log,
rpl_gtid *init_state, uint32 init_state_len,
ulong next_log_number);
+ void wait_for_last_checkpoint_event();
void close(uint exiting);
void clear_inuse_flag_when_closing(File file);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index b1cf6a9024a..9515e5c04a7 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -6025,7 +6025,6 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
thd->set_db(new_db.str, new_db.length);
DBUG_ASSERT(thd->query() == 0);
- thd->reset_query_inner(); // Should not be needed
thd->is_slave_error= 0;
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
diff --git a/sql/mdl.cc b/sql/mdl.cc
index cb616c30b1e..1d6b4f6ffc3 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -17,6 +17,7 @@
#include "sql_class.h"
#include "debug_sync.h"
#include "sql_array.h"
+#include "rpl_rli.h"
#include <lf.h>
#include <mysqld_error.h>
#include <mysql/plugin.h>
@@ -506,6 +507,10 @@ public:
bitmap_t hog_lock_types_bitmap() const
{ return m_strategy->hog_lock_types_bitmap(); }
+#ifndef DBUG_OFF
+ bool check_if_conflicting_replication_locks(MDL_context *ctx);
+#endif
+
/** List of granted tickets for this lock. */
Ticket_list m_granted;
/** Tickets for contexts waiting to acquire a lock. */
@@ -1977,6 +1982,55 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
/**
+ Check if there is any conflicting lock that could cause this thread
+ to wait for another thread which is not ready to commit.
+ This is always an error, as the upper level of parallel replication
+ should not allow a scheduling of a conflicting DDL until all earlier
+ transactions has commited.
+
+ This function is only called for a slave using parallel replication
+ and trying to get an exclusive lock for the table.
+*/
+
+#ifndef DBUG_OFF
+bool MDL_lock::check_if_conflicting_replication_locks(MDL_context *ctx)
+{
+ Ticket_iterator it(m_granted);
+ MDL_ticket *conflicting_ticket;
+ rpl_group_info *rgi_slave= ctx->get_thd()->rgi_slave;
+
+ if (!rgi_slave->gtid_sub_id)
+ return 0;
+
+ while ((conflicting_ticket= it++))
+ {
+ if (conflicting_ticket->get_ctx() != ctx)
+ {
+ MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+ rpl_group_info *conflicting_rgi_slave;
+ conflicting_rgi_slave= conflicting_ctx->get_thd()->rgi_slave;
+
+ /*
+ If the conflicting thread is another parallel replication
+ thread for the same master and it's not in commit stage, then
+ the current transaction has started too early and something is
+ seriously wrong.
+ */
+ if (conflicting_rgi_slave &&
+ conflicting_rgi_slave->gtid_sub_id &&
+ conflicting_rgi_slave->rli == rgi_slave->rli &&
+ conflicting_rgi_slave->current_gtid.domain_id ==
+ rgi_slave->current_gtid.domain_id &&
+ !conflicting_rgi_slave->did_mark_start_commit)
+ return 1; // Fatal error
+ }
+ }
+ return 0;
+}
+#endif
+
+
+/**
Acquire one lock with waiting for conflicting locks to go away if needed.
@param mdl_request [in/out] Lock request object for lock to be acquired
@@ -2036,6 +2090,19 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
if (lock->needs_notification(ticket) && lock_wait_timeout)
lock->notify_conflicting_locks(this);
+ /*
+ Ensure that if we are trying to get an exclusive lock for a slave
+ running parallel replication, then we are not blocked by another
+ parallel slave thread that is not committed. This should never happen as
+ the parallel replication scheduler should never schedule a DDL while
+ DML's are still running.
+ */
+ DBUG_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE &&
+ mdl_request->type != MDL_EXCLUSIVE) ||
+ !(get_thd()->rgi_slave &&
+ get_thd()->rgi_slave->is_parallel_exec &&
+ lock->check_if_conflicting_replication_locks(this)));
+
mysql_prlock_unlock(&lock->m_rwlock);
will_wait_for(ticket);
@@ -2929,34 +2996,54 @@ bool MDL_context::has_explicit_locks()
}
#ifdef WITH_WSREP
-void MDL_ticket::wsrep_report(bool debug)
+static
+const char *wsrep_get_mdl_type_name(enum_mdl_type type)
{
- if (debug)
+ switch (type)
{
- const PSI_stage_info *psi_stage = m_lock->key.get_wait_state_name();
-
- WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s (%s)",
- (get_type() == MDL_INTENTION_EXCLUSIVE) ? "intention exclusive" :
- ((get_type() == MDL_SHARED) ? "shared" :
- ((get_type() == MDL_SHARED_HIGH_PRIO ? "shared high prio" :
- ((get_type() == MDL_SHARED_READ) ? "shared read" :
- ((get_type() == MDL_SHARED_WRITE) ? "shared write" :
- ((get_type() == MDL_SHARED_NO_WRITE) ? "shared no write" :
- ((get_type() == MDL_SHARED_NO_READ_WRITE) ? "shared no read write" :
- ((get_type() == MDL_EXCLUSIVE) ? "exclusive" :
- "UNKNOWN")))))))),
- (m_lock->key.mdl_namespace() == MDL_key::GLOBAL) ? "GLOBAL" :
- ((m_lock->key.mdl_namespace() == MDL_key::SCHEMA) ? "SCHEMA" :
- ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TABLE" :
- ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "FUNCTION" :
- ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "PROCEDURE" :
- ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "TRIGGER" :
- ((m_lock->key.mdl_namespace() == MDL_key::TABLE) ? "EVENT" :
- ((m_lock->key.mdl_namespace() == MDL_key::COMMIT) ? "COMMIT" :
- (char *)"UNKNOWN"))))))),
- m_lock->key.db_name(),
- m_lock->key.name(),
- psi_stage->m_name);
- }
+ case MDL_INTENTION_EXCLUSIVE : return "intention exclusive";
+ case MDL_SHARED : return "shared";
+ case MDL_SHARED_HIGH_PRIO : return "shared high prio";
+ case MDL_SHARED_READ : return "shared read";
+ case MDL_SHARED_WRITE : return "shared write";
+ case MDL_SHARED_UPGRADABLE : return "shared upgradable";
+ case MDL_SHARED_NO_WRITE : return "shared no write";
+ case MDL_SHARED_NO_READ_WRITE : return "shared no read write";
+ case MDL_EXCLUSIVE : return "exclusive";
+ default: break;
+ }
+ return "UNKNOWN";
+}
+
+static
+const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
+{
+ switch (ns)
+ {
+ case MDL_key::GLOBAL : return "GLOBAL";
+ case MDL_key::SCHEMA : return "SCHEMA";
+ case MDL_key::TABLE : return "TABLE";
+ case MDL_key::FUNCTION : return "FUNCTION";
+ case MDL_key::PROCEDURE : return "PROCEDURE";
+ case MDL_key::TRIGGER : return "TRIGGER";
+ case MDL_key::EVENT : return "EVENT";
+ case MDL_key::COMMIT : return "COMMIT";
+ case MDL_key::USER_LOCK : return "USER_LOCK";
+ default: break;
+ }
+ return "UNKNOWN";
+}
+
+void MDL_ticket::wsrep_report(bool debug)
+{
+ if (!debug) return;
+
+ const PSI_stage_info *psi_stage= m_lock->key.get_wait_state_name();
+ WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s (%s)",
+ wsrep_get_mdl_type_name(get_type()),
+ wsrep_get_mdl_namespace_name(m_lock->key.mdl_namespace()),
+ m_lock->key.db_name(),
+ m_lock->key.name(),
+ psi_stage->m_name);
}
#endif /* WITH_WSREP */
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 6e8428f1aad..5de9b4a9eec 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -261,7 +261,7 @@ int handler::multi_range_read_next(range_id_t *range_info)
}
else
{
- if (was_semi_consistent_read())
+ if (ha_was_semi_consistent_read())
{
/*
The following assignment is redundant, but for extra safety and to
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index e97db210da7..d36fdd1192a 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -330,6 +330,8 @@ void Single_line_formatting_helper::flush_on_one_line()
ptr++;
}
owner->output.append(']');
+ /* We've printed out the contents of the buffer, mark it as empty */
+ buf_ptr= buffer;
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index fa8f143335d..18d8d807d90 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -383,7 +383,7 @@ static bool binlog_format_used= false;
LEX_STRING opt_init_connect, opt_init_slave;
mysql_cond_t COND_thread_cache;
static mysql_cond_t COND_flush_thread_cache;
-mysql_cond_t COND_slave_init;
+mysql_cond_t COND_slave_background;
static DYNAMIC_ARRAY all_options;
/* Global variables */
@@ -400,7 +400,6 @@ bool opt_error_log= IF_WIN(1,0);
bool opt_disable_networking=0, opt_skip_show_db=0;
bool opt_skip_name_resolve=0;
my_bool opt_character_set_client_handshake= 1;
-bool server_id_supplied = 0;
bool opt_endinfo, using_udf_functions;
my_bool locked_in_memory;
bool opt_using_transactions;
@@ -530,7 +529,7 @@ ulong extra_max_connections;
uint max_digest_length= 0;
ulong slave_retried_transactions;
ulonglong slave_skipped_errors;
-ulong feature_files_opened_with_delayed_keys;
+ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0;
ulonglong denied_connections;
my_decimal decimal_zero;
@@ -691,6 +690,14 @@ THD *next_global_thread(THD *thd)
}
struct system_variables global_system_variables;
+/**
+ Following is just for options parsing, used with a difference against
+ global_system_variables.
+
+ TODO: something should be done to get rid of following variables
+*/
+const char *current_dbug_option="";
+
struct system_variables max_system_variables;
struct system_status_var global_status_var;
@@ -747,7 +754,7 @@ mysql_mutex_t
LOCK_crypt,
LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
- LOCK_connection_count, LOCK_error_messages, LOCK_slave_init;
+ LOCK_connection_count, LOCK_error_messages, LOCK_slave_background;
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
@@ -930,7 +937,7 @@ PSI_mutex_key key_LOCK_gtid_waiting;
PSI_mutex_key key_LOCK_after_binlog_sync;
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered,
- key_LOCK_slave_init;
+ key_LOCK_slave_background;
PSI_mutex_key key_TABLE_SHARE_LOCK_share;
static PSI_mutex_info all_server_mutexes[]=
@@ -996,7 +1003,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
{ &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL},
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
- { &key_LOCK_slave_init, "LOCK_slave_init", PSI_FLAG_GLOBAL},
+ { &key_LOCK_slave_background, "LOCK_slave_background", PSI_FLAG_GLOBAL},
{ &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
{ &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
{ &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
@@ -1053,7 +1060,7 @@ PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread,
key_COND_rpl_thread_stop, key_COND_rpl_thread_pool,
key_COND_parallel_entry, key_COND_group_commit_orderer,
- key_COND_prepare_ordered, key_COND_slave_init;
+ key_COND_prepare_ordered, key_COND_slave_background;
PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
static PSI_cond_info all_server_conds[]=
@@ -1103,7 +1110,7 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_parallel_entry, "COND_parallel_entry", 0},
{ &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0},
{ &key_COND_prepare_ordered, "COND_prepare_ordered", 0},
- { &key_COND_slave_init, "COND_slave_init", 0},
+ { &key_COND_slave_background, "COND_slave_background", 0},
{ &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL},
{ &key_COND_wait_gtid, "COND_wait_gtid", 0},
{ &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
@@ -1112,7 +1119,7 @@ static PSI_cond_info all_server_conds[]=
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_main,
key_thread_one_connection, key_thread_signal_hand,
- key_thread_slave_init, key_rpl_parallel_thread;
+ key_thread_slave_background, key_rpl_parallel_thread;
static PSI_thread_info all_server_threads[]=
{
@@ -1138,7 +1145,7 @@ static PSI_thread_info all_server_threads[]=
{ &key_thread_main, "main", PSI_FLAG_GLOBAL},
{ &key_thread_one_connection, "one_connection", 0},
{ &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL},
- { &key_thread_slave_init, "slave_init", PSI_FLAG_GLOBAL},
+ { &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL},
{ &key_rpl_parallel_thread, "rpl_parallel_thread", 0}
};
@@ -1464,7 +1471,6 @@ my_bool plugins_are_initialized= FALSE;
#ifndef DBUG_OFF
static const char* default_dbug_option;
#endif
-const char *current_dbug_option="";
#ifdef HAVE_LIBWRAP
const char *libwrapName= NULL;
int allow_severity = LOG_INFO;
@@ -2345,8 +2351,8 @@ static void clean_up_mutexes()
mysql_cond_destroy(&COND_prepare_ordered);
mysql_mutex_destroy(&LOCK_after_binlog_sync);
mysql_mutex_destroy(&LOCK_commit_ordered);
- mysql_mutex_destroy(&LOCK_slave_init);
- mysql_cond_destroy(&COND_slave_init);
+ mysql_mutex_destroy(&LOCK_slave_background);
+ mysql_cond_destroy(&COND_slave_background);
DBUG_VOID_RETURN;
}
@@ -2755,26 +2761,17 @@ static void network_init(void)
saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor;
saPipeSecurity.bInheritHandle = FALSE;
if ((hPipe= CreateNamedPipe(pipe_name,
- PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE |
- PIPE_READMODE_BYTE |
- PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- (int) global_system_variables.net_buffer_length,
- (int) global_system_variables.net_buffer_length,
- NMPWAIT_USE_DEFAULT_WAIT,
- &saPipeSecurity)) == INVALID_HANDLE_VALUE)
- {
- LPVOID lpMsgBuf;
- int error=GetLastError();
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf, 0, NULL );
- sql_perror((char *)lpMsgBuf);
- LocalFree(lpMsgBuf);
- unireg_abort(1);
- }
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int) global_system_variables.net_buffer_length,
+ (int) global_system_variables.net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &saPipeSecurity)) == INVALID_HANDLE_VALUE)
+ {
+ sql_perror("Create named pipe failed");
+ unireg_abort(1);
+ }
}
#endif
@@ -4334,11 +4331,23 @@ static int init_common_variables()
if (get_options(&remaining_argc, &remaining_argv))
return 1;
- set_server_version();
+ if (IS_SYSVAR_AUTOSIZE(&server_version_ptr))
+ set_server_version(server_version, sizeof(server_version));
if (!opt_abort)
- sql_print_information("%s (mysqld %s) starting as process %lu ...",
- my_progname, server_version, (ulong) getpid());
+ {
+ if (IS_SYSVAR_AUTOSIZE(&server_version_ptr))
+ sql_print_information("%s (mysqld %s) starting as process %lu ...",
+ my_progname, server_version, (ulong) getpid());
+ else
+ {
+ char real_server_version[SERVER_VERSION_LENGTH];
+ set_server_version(real_server_version, sizeof(real_server_version));
+ sql_print_information("%s (mysqld %s as %s) starting as process %lu ...",
+ my_progname, real_server_version, server_version,
+ (ulong) getpid());
+ }
+ }
#ifndef EMBEDDED_LIBRARY
if (opt_abort && !opt_verbose)
@@ -4729,9 +4738,9 @@ static int init_thread_environment()
MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered,
MY_MUTEX_INIT_SLOW);
- mysql_mutex_init(key_LOCK_slave_init, &LOCK_slave_init,
+ mysql_mutex_init(key_LOCK_slave_background, &LOCK_slave_background,
MY_MUTEX_INIT_SLOW);
- mysql_cond_init(key_COND_slave_init, &COND_slave_init, NULL);
+ mysql_cond_init(key_COND_slave_background, &COND_slave_background, NULL);
#ifdef HAVE_OPENSSL
mysql_mutex_init(key_LOCK_des_key_file,
@@ -4965,8 +4974,7 @@ static int init_server_components()
all things are initialized so that unireg_abort() doesn't fail
*/
mdl_init();
- tdc_init();
- if (hostname_cache_init())
+ if (tdc_init() || hostname_cache_init())
unireg_abort(1);
query_cache_set_min_res_unit(query_cache_min_res_unit);
@@ -5279,6 +5287,17 @@ static int init_server_components()
}
plugins_are_initialized= TRUE; /* Don't separate from init function */
+#ifndef EMBEDDED_LIBRARY
+ {
+ if (Session_tracker::server_boot_verify(system_charset_info))
+ {
+ sql_print_error("The variable session_track_system_variables has "
+ "invalid values.");
+ unireg_abort(1);
+ }
+ }
+#endif //EMBEDDED_LIBRARY
+
/* we do want to exit if there are any other unknown options */
if (remaining_argc > 1)
{
@@ -5831,17 +5850,6 @@ int mysqld_main(int argc, char **argv)
if (WSREP_ON && wsrep_check_opts())
global_system_variables.wsrep_on= 0;
- if (opt_bin_log && !global_system_variables.server_id)
- {
- SYSVAR_AUTOSIZE(global_system_variables.server_id, ::server_id= 1);
-#ifdef EXTRA_DEBUG
- sql_print_warning("You have enabled the binary log, but you haven't set "
- "server-id to a non-zero value: we force server id to 1; "
- "updates will be logged to the binary log, but "
- "connections from slaves will not be accepted.");
-#endif
- }
-
/*
The subsequent calls may take a long time : e.g. innodb log read.
Thus set the long running service control manager timeout
@@ -6288,7 +6296,8 @@ int mysqld_main(int argc, char **argv)
/**
Execute all commands from a file. Used by the mysql_install_db script to
- create MySQL privilege tables without having to start a full MySQL server.
+ create MySQL privilege tables without having to start a full MySQL server
+ and by read_init_file() if mysqld was started with the option --init-file.
*/
static void bootstrap(MYSQL_FILE *file)
@@ -7633,7 +7642,6 @@ struct my_option my_long_options[]=
MYSQL_TO_BE_IMPLEMENTED_OPTION("eq-range-index-dive-limit"),
MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
- MYSQL_COMPATIBILITY_OPTION("table-open-cache-instances"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-allow-batching"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-period"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-group"), // HAVE_REPLICATION
@@ -7753,7 +7761,10 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
var->value= buff;
mysql_mutex_lock(&LOCK_active_mi);
- *((longlong *)buff)= master_info_index->any_slave_sql_running();
+ if (master_info_index)
+ *((longlong *)buff)= master_info_index->any_slave_sql_running();
+ else
+ *((longlong *)buff)= 0;
mysql_mutex_unlock(&LOCK_active_mi);
return 0;
@@ -8387,9 +8398,11 @@ SHOW_VAR status_vars[]= {
{"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG},
{"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
+ {"Delete_scan", (char*) offsetof(STATUS_VAR, delete_scan_count), SHOW_LONG_STATUS},
{"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
{"Executed_events", (char*) &executed_events, SHOW_LONG_NOFLUSH },
{"Executed_triggers", (char*) offsetof(STATUS_VAR, executed_triggers), SHOW_LONG_STATUS},
+ {"Feature_check_constraint", (char*) &feature_check_constraint, SHOW_LONG },
{"Feature_delay_key_write", (char*) &feature_files_opened_with_delayed_keys, SHOW_LONG },
{"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS},
{"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS},
@@ -8415,6 +8428,7 @@ SHOW_VAR status_vars[]= {
{"Handler_read_last", (char*) offsetof(STATUS_VAR, ha_read_last_count), SHOW_LONG_STATUS},
{"Handler_read_next", (char*) offsetof(STATUS_VAR, ha_read_next_count), SHOW_LONG_STATUS},
{"Handler_read_prev", (char*) offsetof(STATUS_VAR, ha_read_prev_count), SHOW_LONG_STATUS},
+ {"Handler_read_retry", (char*) offsetof(STATUS_VAR, ha_read_retry_count), SHOW_LONG_STATUS},
{"Handler_read_rnd", (char*) offsetof(STATUS_VAR, ha_read_rnd_count), SHOW_LONG_STATUS},
{"Handler_read_rnd_deleted", (char*) offsetof(STATUS_VAR, ha_read_rnd_deleted_count), SHOW_LONG_STATUS},
{"Handler_read_rnd_next", (char*) offsetof(STATUS_VAR, ha_read_rnd_next_count), SHOW_LONG_STATUS},
@@ -8536,6 +8550,7 @@ SHOW_VAR status_vars[]= {
{"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
{"Threads_running", (char*) &thread_running, SHOW_INT},
+ {"Update_scan", (char*) offsetof(STATUS_VAR, update_scan_count), SHOW_LONG_STATUS},
{"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC},
#ifdef ENABLED_PROFILING
{"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_SIMPLE_FUNC},
@@ -8564,7 +8579,8 @@ static bool add_many_options(DYNAMIC_ARRAY *options, my_option *list,
#ifndef EMBEDDED_LIBRARY
static void print_version(void)
{
- set_server_version();
+ if (IS_SYSVAR_AUTOSIZE(&server_version_ptr))
+ set_server_version(server_version, sizeof(server_version));
printf("%s Ver %s for %s on %s (%s)\n",my_progname,
server_version,SYSTEM_TYPE,MACHINE_TYPE, MYSQL_COMPILATION_COMMENT);
@@ -8706,7 +8722,6 @@ static int mysql_init_variables(void)
mqh_used= 0;
kill_in_progress= 0;
cleanup_done= 0;
- server_id_supplied= 0;
test_flags= select_errors= dropping_tables= ha_open_options=0;
thread_count= thread_running= kill_cached_threads= wake_thread= 0;
service_thread_count= 0;
@@ -9175,7 +9190,6 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
opt_noacl=opt_bootstrap=1;
break;
case OPT_SERVER_ID:
- server_id_supplied = 1;
::server_id= global_system_variables.server_id;
break;
case OPT_LOWER_CASE_TABLE_NAMES:
@@ -9675,24 +9689,17 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
(MYSQL_SERVER_SUFFIX is set by the compilation environment)
*/
-void set_server_version(void)
+void set_server_version(char *buf, size_t size)
{
- if (!IS_SYSVAR_AUTOSIZE(&server_version_ptr))
- return;
- char *version_end= server_version+sizeof(server_version)-1;
- char *end= strxnmov(server_version, sizeof(server_version)-1,
- MYSQL_SERVER_VERSION,
- MYSQL_SERVER_SUFFIX_STR, NullS);
-#ifdef EMBEDDED_LIBRARY
- end= strnmov(end, "-embedded", (version_end-end));
-#endif
-#ifndef DBUG_OFF
- if (!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug"))
- end= strnmov(end, "-debug", (version_end-end));
-#endif
- if (opt_log || global_system_variables.sql_log_slow || opt_bin_log)
- strnmov(end, "-log", (version_end-end)); // This may slow down system
- *end= 0;
+ bool is_log= opt_log || global_system_variables.sql_log_slow || opt_bin_log;
+ bool is_debug= IF_DBUG(!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug"), 0);
+ strxnmov(buf, size - 1,
+ MYSQL_SERVER_VERSION,
+ MYSQL_SERVER_SUFFIX_STR,
+ IF_EMBEDDED("-embedded", ""),
+ is_debug ? "-debug" : "",
+ is_log ? "-log" : "",
+ NullS);
}
@@ -10150,11 +10157,6 @@ PSI_stage_info stage_waiting_for_the_next_event_in_relay_log= { 0, "Waiting for
PSI_stage_info stage_waiting_for_the_slave_thread_to_advance_position= { 0, "Waiting for the slave SQL thread to advance position", 0};
PSI_stage_info stage_waiting_to_finalize_termination= { 0, "Waiting to finalize termination", 0};
PSI_stage_info stage_waiting_to_get_readlock= { 0, "Waiting to get readlock", 0};
-PSI_stage_info stage_slave_waiting_workers_to_exit= { 0, "Waiting for workers to exit", 0};
-PSI_stage_info stage_slave_waiting_worker_to_release_partition= { 0, "Waiting for Slave Worker to release partition", 0};
-PSI_stage_info stage_slave_waiting_worker_to_free_events= { 0, "Waiting for Slave Workers to free pending events", 0};
-PSI_stage_info stage_slave_waiting_worker_queue= { 0, "Waiting for Slave Worker queue", 0};
-PSI_stage_info stage_slave_waiting_event_from_coordinator= { 0, "Waiting for an event from Coordinator", 0};
PSI_stage_info stage_binlog_waiting_background_tasks= { 0, "Waiting for background binlog tasks", 0};
PSI_stage_info stage_binlog_processing_checkpoint_notify= { 0, "Processing binlog checkpoint notification", 0};
PSI_stage_info stage_binlog_stopping_background_thread= { 0, "Stopping binlog background thread", 0};
@@ -10169,6 +10171,9 @@ PSI_stage_info stage_waiting_for_rpl_thread_pool= { 0, "Waiting while replicatio
PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0};
PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0};
PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process GTID received on multiple master connections", 0};
+PSI_stage_info stage_slave_background_process_request= { 0, "Processing requests", 0};
+PSI_stage_info stage_slave_background_wait_request= { 0, "Waiting for requests", 0};
+PSI_stage_info stage_waiting_for_deadlock_kill= { 0, "Waiting for parallel replication deadlock handling to complete", 0};
#ifdef HAVE_PSI_INTERFACE
@@ -10250,11 +10255,6 @@ PSI_stage_info *all_server_stages[]=
& stage_setup,
& stage_show_explain,
& stage_slave_has_read_all_relay_log,
- & stage_slave_waiting_event_from_coordinator,
- & stage_slave_waiting_worker_queue,
- & stage_slave_waiting_worker_to_free_events,
- & stage_slave_waiting_worker_to_release_partition,
- & stage_slave_waiting_workers_to_exit,
& stage_sorting,
& stage_sorting_for_group,
& stage_sorting_for_order,
@@ -10298,7 +10298,10 @@ PSI_stage_info *all_server_stages[]=
& stage_waiting_to_get_readlock,
& stage_master_gtid_wait_primary,
& stage_master_gtid_wait,
- & stage_gtid_wait_other_connection
+ & stage_gtid_wait_other_connection,
+ & stage_slave_background_process_request,
+ & stage_slave_background_wait_request,
+ & stage_waiting_for_deadlock_kill
};
PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 6783eeff810..83d723c6ab7 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -107,7 +107,7 @@ extern CHARSET_INFO *error_message_charset_info;
extern CHARSET_INFO *character_set_filesystem;
extern MY_BITMAP temp_pool;
-extern bool opt_large_files, server_id_supplied;
+extern bool opt_large_files;
extern bool opt_update_log, opt_bin_log, opt_error_log;
extern my_bool opt_log, opt_bootstrap;
extern my_bool opt_backup_history_log;
@@ -342,8 +342,8 @@ extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
- key_thread_one_connection, key_thread_signal_hand, key_thread_slave_init,
- key_rpl_parallel_thread;
+ key_thread_one_connection, key_thread_signal_hand,
+ key_thread_slave_background, key_rpl_parallel_thread;
extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
@@ -475,11 +475,6 @@ extern PSI_stage_info stage_waiting_for_the_next_event_in_relay_log;
extern PSI_stage_info stage_waiting_for_the_slave_thread_to_advance_position;
extern PSI_stage_info stage_waiting_to_finalize_termination;
extern PSI_stage_info stage_waiting_to_get_readlock;
-extern PSI_stage_info stage_slave_waiting_worker_to_release_partition;
-extern PSI_stage_info stage_slave_waiting_worker_to_free_events;
-extern PSI_stage_info stage_slave_waiting_worker_queue;
-extern PSI_stage_info stage_slave_waiting_event_from_coordinator;
-extern PSI_stage_info stage_slave_waiting_workers_to_exit;
extern PSI_stage_info stage_binlog_waiting_background_tasks;
extern PSI_stage_info stage_binlog_processing_checkpoint_notify;
extern PSI_stage_info stage_binlog_stopping_background_thread;
@@ -494,6 +489,9 @@ extern PSI_stage_info stage_waiting_for_rpl_thread_pool;
extern PSI_stage_info stage_master_gtid_wait_primary;
extern PSI_stage_info stage_master_gtid_wait;
extern PSI_stage_info stage_gtid_wait_other_connection;
+extern PSI_stage_info stage_slave_background_process_request;
+extern PSI_stage_info stage_slave_background_wait_request;
+extern PSI_stage_info stage_waiting_for_deadlock_kill;
#ifdef HAVE_PSI_STATEMENT_INTERFACE
/**
@@ -563,7 +561,7 @@ extern mysql_mutex_t
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
- LOCK_slave_init;
+ LOCK_slave_background;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
extern mysql_mutex_t LOCK_start_thread;
#ifdef HAVE_OPENSSL
@@ -576,7 +574,7 @@ extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_system_variables_hash;
extern mysql_cond_t COND_thread_count, COND_start_thread;
extern mysql_cond_t COND_manager;
-extern mysql_cond_t COND_slave_init;
+extern mysql_cond_t COND_slave_background;
extern int32 thread_running;
extern int32 thread_count, service_thread_count;
@@ -774,7 +772,7 @@ inline void dec_thread_running()
thread_safe_decrement32(&thread_running);
}
-extern void set_server_version(void);
+extern void set_server_version(char *buf, size_t size);
#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
extern "C" THD *_current_thd_noinline();
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index f0284462206..fccc947f3f1 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -117,7 +117,6 @@ extern my_bool thd_net_is_killed();
#endif
#define TEST_BLOCKING 8
-#define MAX_PACKET_LENGTH (256L*256L*256L-1)
static my_bool net_write_buff(NET *, const uchar *, ulong);
@@ -553,7 +552,7 @@ net_write_buff(NET *net, const uchar *packet, ulong len)
left_length= (ulong) (net->buff_end - net->write_pos);
#ifdef DEBUG_DATA_PACKETS
- DBUG_DUMP("data", packet, len);
+ DBUG_DUMP("data_written", packet, len);
#endif
if (len > left_length)
{
@@ -642,7 +641,8 @@ net_real_write(NET *net,const uchar *packet, size_t len)
}
memcpy(b+header_length,packet,len);
- if (my_compress(b+header_length, &len, &complen))
+ /* Don't compress error packets (compress == 2) */
+ if (net->compress == 2 || my_compress(b+header_length, &len, &complen))
complen=0;
int3store(&b[NET_HEADER_SIZE],complen);
int3store(b,len);
@@ -653,7 +653,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
#endif /* HAVE_COMPRESS */
#ifdef DEBUG_DATA_PACKETS
- DBUG_DUMP("data", packet, len);
+ DBUG_DUMP("data_written", packet, len);
#endif
#ifndef NO_ALARM
@@ -843,6 +843,7 @@ my_real_read(NET *net, size_t *complen,
size_t length;
uint i,retry_count=0;
ulong len=packet_error;
+ my_bool expect_error_packet __attribute__((unused))= 0;
thr_alarm_t alarmed;
#ifndef NO_ALARM
ALARM alarm_buff;
@@ -891,6 +892,7 @@ my_real_read(NET *net, size_t *complen,
if (i== 0 && thd_net_is_killed())
{
+ DBUG_PRINT("info", ("thd is killed"));
len= packet_error;
net->error= 0;
net->last_errno= ER_CONNECTION_KILLED;
@@ -960,39 +962,34 @@ my_real_read(NET *net, size_t *complen,
pos+= length;
update_statistics(thd_increment_bytes_received(net->thd, length));
}
+
+#ifdef DEBUG_DATA_PACKETS
+ DBUG_DUMP("data_read", net->buff+net->where_b, length);
+#endif
if (i == 0)
{ /* First parts is packet length */
ulong helping;
+#ifndef DEBUG_DATA_PACKETS
DBUG_DUMP("packet_header", net->buff+net->where_b,
NET_HEADER_SIZE);
+#endif
if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
- {
- if (net->buff[net->where_b] != (uchar) 255)
- {
- DBUG_PRINT("error",
- ("Packets out of order (Found: %d, expected %u)",
- (int) net->buff[net->where_b + 3],
- net->pkt_nr));
- /*
- We don't make noise server side, since the client is expected
- to break the protocol for e.g. --send LOAD DATA .. LOCAL where
- the server expects the client to send a file, but the client
- may reply with a new command instead.
- */
+ {
#ifndef MYSQL_SERVER
- EXTRA_DEBUG_fflush(stdout);
- EXTRA_DEBUG_fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n",
- (int) net->buff[net->where_b + 3],
- (uint) (uchar) net->pkt_nr);
- EXTRA_DEBUG_fflush(stderr);
+ if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1))
+ {
+ /*
+ If the server was killed then the server may have missed the
+ last sent client packet and the packet numbering may be one off.
+ */
+ DBUG_PRINT("warning", ("Found possible out of order packets"));
+ expect_error_packet= 1;
+ }
+ else
#endif
- }
- len= packet_error;
- /* Not a NET error on the client. XXX: why? */
- MYSQL_SERVER_my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
- goto end;
- }
- net->compress_pkt_nr= ++net->pkt_nr;
+ goto packets_out_of_order;
+ }
+ net->compress_pkt_nr= ++net->pkt_nr;
#ifdef HAVE_COMPRESS
if (net->compress)
{
@@ -1040,6 +1037,21 @@ my_real_read(NET *net, size_t *complen,
}
#endif
}
+#ifndef MYSQL_SERVER
+ else if (expect_error_packet)
+ {
+ /*
+ This check is safe both for compressed and not compressed protocol
+ as for the compressed protocol errors are not compressed anymore.
+ */
+ if (net->buff[net->where_b] != (uchar) 255)
+ {
+ /* Restore pkt_nr to original value */
+ net->pkt_nr--;
+ goto packets_out_of_order;
+ }
+ }
+#endif
}
end:
@@ -1053,7 +1065,7 @@ end:
net->reading_or_writing=0;
#ifdef DEBUG_DATA_PACKETS
if (len != packet_error)
- DBUG_DUMP("data", net->buff+net->where_b, len);
+ DBUG_DUMP("data_read", net->buff+net->where_b, len);
#endif
#ifdef MYSQL_SERVER
if (server_extension != NULL)
@@ -1064,9 +1076,35 @@ end:
}
#endif
return(len);
+
+packets_out_of_order:
+ {
+ DBUG_PRINT("error",
+ ("Packets out of order (Found: %d, expected %u)",
+ (int) net->buff[net->where_b + 3],
+ net->pkt_nr));
+ DBUG_ASSERT(0);
+ /*
+ We don't make noise server side, since the client is expected
+ to break the protocol for e.g. --send LOAD DATA .. LOCAL where
+ the server expects the client to send a file, but the client
+ may reply with a new command instead.
+ */
+#ifndef MYSQL_SERVER
+ EXTRA_DEBUG_fflush(stdout);
+ EXTRA_DEBUG_fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n",
+ (int) net->buff[net->where_b + 3],
+ (uint) (uchar) net->pkt_nr);
+ EXTRA_DEBUG_fflush(stderr);
+#endif
+ len= packet_error;
+ MYSQL_SERVER_my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
+ goto end;
+ }
}
+
/* Old interface. See my_net_read_packet() for function description */
#undef my_net_read
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index bd191f715e0..3ea9f4e5db9 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -2428,8 +2428,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
scan_time= read_time= DBL_MAX;
if (limit < records)
read_time= (double) records + scan_time + 1; // Force to use index
- else if (read_time <= 2.0 && !force_quick_range)
- DBUG_RETURN(0); /* No need for quick select */
possible_keys.clear_all();
@@ -2699,7 +2697,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
thd->no_errors=0;
}
-
DBUG_EXECUTE("info", print_quick(quick, &needed_reg););
/*
@@ -10377,8 +10374,10 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
KEY *table_key=quick->head->key_info+quick->index;
flag=EQ_RANGE;
if ((table_key->flags & HA_NOSAME) &&
+ min_part == key_tree->part &&
key_tree->part == table_key->user_defined_key_parts-1)
{
+ DBUG_ASSERT(min_part == max_part);
if ((table_key->flags & HA_NULL_PART_KEY) &&
null_part_in_key(key,
param->min_key,
@@ -11904,8 +11903,6 @@ void QUICK_ROR_UNION_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
*******************************************************************************/
static inline uint get_field_keypart(KEY *index, Field *field);
-static inline SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree,
- PARAM *param, uint *param_idx);
static bool get_sel_arg_for_keypart(Field *field, SEL_ARG *index_range_tree,
SEL_ARG **cur_range);
static bool get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
@@ -12179,8 +12176,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
(GA1,GA2) are all TRUE. If there is more than one such index, select the
first one. Here we set the variables: group_prefix_len and index_info.
*/
- KEY *cur_index_info= table->key_info;
- KEY *cur_index_info_end= cur_index_info + table->s->keys;
/* Cost-related variables for the best index so far. */
double best_read_cost= DBL_MAX;
ha_rows best_records= 0;
@@ -12192,11 +12187,12 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
uint max_key_part;
SEL_ARG *cur_index_tree= NULL;
ha_rows cur_quick_prefix_records= 0;
- uint cur_param_idx=MAX_KEY;
- for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
- cur_index_info++, cur_index++)
+ // We go through allowed indexes
+ for (uint cur_param_idx= 0; cur_param_idx < param->keys ; ++cur_param_idx)
{
+ const uint cur_index= param->real_keynr[cur_param_idx];
+ KEY *const cur_index_info= &table->key_info[cur_index];
KEY_PART_INFO *cur_part;
KEY_PART_INFO *end_part; /* Last part for loops. */
/* Last index part. */
@@ -12219,7 +12215,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
(was also: "Exclude UNIQUE indexes ..." but this was removed because
there are cases Loose Scan over a multi-part index is useful).
*/
- if (!table->covering_keys.is_set(cur_index))
+ if (!table->covering_keys.is_set(cur_index) ||
+ !table->keys_in_use_for_group_by.is_set(cur_index))
continue;
/*
@@ -12398,9 +12395,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
{
if (tree)
{
- uint dummy;
- SEL_ARG *index_range_tree= get_index_range_tree(cur_index, tree, param,
- &dummy);
+ SEL_ARG *index_range_tree= tree->keys[cur_param_idx];
if (!get_constant_key_infix(cur_index_info, index_range_tree,
first_non_group_part, min_max_arg_part,
last_part, thd, cur_key_infix,
@@ -12464,9 +12459,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
*/
if (tree && min_max_arg_item)
{
- uint dummy;
- SEL_ARG *index_range_tree= get_index_range_tree(cur_index, tree, param,
- &dummy);
+ SEL_ARG *index_range_tree= tree->keys[cur_param_idx];
SEL_ARG *cur_range= NULL;
if (get_sel_arg_for_keypart(min_max_arg_part->field,
index_range_tree, &cur_range) ||
@@ -12484,9 +12477,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
/* Compute the cost of using this index. */
if (tree)
{
- /* Find the SEL_ARG sub-tree that corresponds to the chosen index. */
- cur_index_tree= get_index_range_tree(cur_index, tree, param,
- &cur_param_idx);
+ cur_index_tree= tree->keys[cur_param_idx];
/* Check if this range tree can be used for prefix retrieval. */
Cost_estimate dummy_cost;
uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
@@ -13020,44 +13011,6 @@ get_field_keypart(KEY *index, Field *field)
/*
- Find the SEL_ARG sub-tree that corresponds to the chosen index.
-
- SYNOPSIS
- get_index_range_tree()
- index [in] The ID of the index being looked for
- range_tree[in] Tree of ranges being searched
- param [in] PARAM from SQL_SELECT::test_quick_select
- param_idx [out] Index in the array PARAM::key that corresponds to 'index'
-
- DESCRIPTION
-
- A SEL_TREE contains range trees for all usable indexes. This procedure
- finds the SEL_ARG sub-tree for 'index'. The members of a SEL_TREE are
- ordered in the same way as the members of PARAM::key, thus we first find
- the corresponding index in the array PARAM::key. This index is returned
- through the variable param_idx, to be used later as argument of
- check_quick_select().
-
- RETURN
- Pointer to the SEL_ARG subtree that corresponds to index.
-*/
-
-SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree, PARAM *param,
- uint *param_idx)
-{
- uint idx= 0; /* Index nr in param->key_parts */
- while (idx < param->keys)
- {
- if (index == param->real_keynr[idx])
- break;
- idx++;
- }
- *param_idx= idx;
- return(range_tree->keys[idx]);
-}
-
-
-/*
Compute the cost of a quick_group_min_max_select for a particular index.
SYNOPSIS
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index 729c491a6f1..fbccb7c4e1d 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -278,14 +278,14 @@ walk_up_n_right:
(1) - range analysis is used for estimating condition selectivity
(2) - This is a unique key, and we have conditions for all its
user-defined key parts.
- (3) - The table uses extended keys, and we have conditions for
- all key parts.
+ (3) - The table uses extended keys, this key covers all components,
+ and we have conditions for all key parts.
*/
if (!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
(!key_info || // (1)
((uint)key_tree->part+1 == key_info->user_defined_key_parts && // (2)
key_info->flags & HA_NOSAME) || // (2)
- (seq->param->table->s->use_ext_keys && // (3)
+ ((key_info->flags & HA_EXT_NOSAME) && // (3)
(uint)key_tree->part+1 == key_info->ext_key_parts) // (3)
) &&
range->start_key.length == range->end_key.length &&
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index a835c5824ff..a09826ac2f0 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -512,6 +512,7 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
(Subquery is correlated to the immediate outer query &&
Subquery !contains {GROUP BY, ORDER BY [LIMIT],
aggregate functions}) && subquery predicate is not under "NOT IN"))
+ 5. Subquery does not contain recursive references
A note about prepared statements: we want the if-branch to be taken on
PREPARE and each EXECUTE. The rewrites are only done once, but we need
@@ -528,7 +529,8 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3
optimizer_flag(thd,
OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3
- !in_subs->is_correlated) //4
+ !in_subs->is_correlated && //4
+ !in_subs->with_recursive_reference) //5
{
return TRUE;
}
@@ -1558,7 +1560,12 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
{
tl->set_tablenr(table_no);
if (tl->is_jtbm())
+ {
tl->jtbm_table_no= table_no;
+ Item *dummy= tl->jtbm_subselect;
+ tl->jtbm_subselect->fix_after_pullout(parent_lex, &dummy);
+ DBUG_ASSERT(dummy == tl->jtbm_subselect);
+ }
SELECT_LEX *old_sl= tl->select_lex;
tl->select_lex= parent_join->select_lex;
for (TABLE_LIST *emb= tl->embedding;
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 308a4d6ddd2..b979b7a58e6 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -65,7 +65,7 @@ typedef struct p_column_list_val
Item* item_expression;
partition_info *part_info;
uint partition_id;
- bool max_value;
+ bool max_value; // MAXVALUE for RANGE type or DEFAULT value for LIST type
bool null_value;
char fixed;
} part_column_list_val;
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 4f297c630ad..9d31667a6a8 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1472,6 +1472,8 @@ bool partition_info::check_list_constants(THD *thd)
List_iterator<partition_element> list_func_it(partitions);
DBUG_ENTER("partition_info::check_list_constants");
+ DBUG_ASSERT(part_type == LIST_PARTITION);
+
num_list_values= 0;
/*
We begin by calculating the number of list values that have been
@@ -1503,21 +1505,15 @@ bool partition_info::check_list_constants(THD *thd)
has_null_part_id= i;
found_null= TRUE;
}
- List_iterator<part_elem_value> list_val_it1(part_def->list_val_list);
- while (list_val_it1++)
- num_list_values++;
+ num_list_values+= part_def->list_val_list.elements;
} while (++i < num_parts);
list_func_it.rewind();
num_column_values= part_field_list.elements;
size_entries= column_list ?
(num_column_values * sizeof(part_column_list_val)) :
sizeof(LIST_PART_ENTRY);
- ptr= thd->calloc((num_list_values+1) * size_entries);
- if (unlikely(ptr == NULL))
- {
- mem_alloc_error(num_list_values * size_entries);
+ if (unlikely(!(ptr= thd->calloc((num_list_values+1) * size_entries))))
goto end;
- }
if (column_list)
{
part_column_list_val *loc_list_col_array;
@@ -1528,6 +1524,13 @@ bool partition_info::check_list_constants(THD *thd)
do
{
part_def= list_func_it++;
+ if (part_def->max_value)
+ {
+ // DEFAULT is not a real value so let's exclude it from sorting.
+ DBUG_ASSERT(num_list_values);
+ num_list_values--;
+ continue;
+ }
List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
while ((list_value= list_val_it2++))
{
@@ -1557,6 +1560,13 @@ bool partition_info::check_list_constants(THD *thd)
do
{
part_def= list_func_it++;
+ if (part_def->max_value && part_type == LIST_PARTITION)
+ {
+ // DEFAULT is not a real value so let's exclude it from sorting.
+ DBUG_ASSERT(num_list_values);
+ num_list_values--;
+ continue;
+ }
List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
while ((list_value= list_val_it2++))
{
@@ -2287,11 +2297,19 @@ int partition_info::add_max_value(THD *thd)
DBUG_ENTER("partition_info::add_max_value");
part_column_list_val *col_val;
- if (!(col_val= add_column_value(thd)))
+ /*
+ Makes for LIST COLUMNS 'num_columns' DEFAULT tuples, 1 tuple for RANGEs
+ */
+ uint max_val= (num_columns && part_type == LIST_PARTITION) ?
+ num_columns : 1;
+ for (uint i= 0; i < max_val; i++)
{
- DBUG_RETURN(TRUE);
+ if (!(col_val= add_column_value(thd)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ col_val->max_value= TRUE;
}
- col_val->max_value= TRUE;
DBUG_RETURN(FALSE);
}
@@ -2566,8 +2584,7 @@ int partition_info::reorganize_into_single_field_col_val(THD *thd)
*/
int partition_info::fix_partition_values(THD *thd,
part_elem_value *val,
- partition_element *part_elem,
- uint part_id)
+ partition_element *part_elem)
{
part_column_list_val *col_val= val->col_val_array;
DBUG_ENTER("partition_info::fix_partition_values");
@@ -2576,59 +2593,31 @@ int partition_info::fix_partition_values(THD *thd,
{
DBUG_RETURN(FALSE);
}
- if (val->added_items != 1)
- {
- my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- if (col_val->max_value)
+
+ Item *item_expr= col_val->item_expression;
+ if ((val->null_value= item_expr->null_value))
{
- /* The parser ensures we're not LIST partitioned here */
- DBUG_ASSERT(part_type == RANGE_PARTITION);
- if (defined_max_value)
- {
- my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- if (part_id == (num_parts - 1))
+ if (part_elem->has_null_value)
{
- defined_max_value= TRUE;
- part_elem->max_value= TRUE;
- part_elem->range_value= LONGLONG_MAX;
- }
- else
- {
- my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
+ part_elem->has_null_value= TRUE;
}
- else
+ else if (item_expr->result_type() != INT_RESULT)
{
- Item *item_expr= col_val->item_expression;
- if ((val->null_value= item_expr->null_value))
- {
- if (part_elem->has_null_value)
- {
- my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- part_elem->has_null_value= TRUE;
- }
- else if (item_expr->result_type() != INT_RESULT)
+ my_error(ER_VALUES_IS_NOT_INT_TYPE_ERROR, MYF(0),
+ part_elem->partition_name);
+ DBUG_RETURN(TRUE);
+ }
+ if (part_type == RANGE_PARTITION)
+ {
+ if (part_elem->has_null_value)
{
- my_error(ER_VALUES_IS_NOT_INT_TYPE_ERROR, MYF(0),
- part_elem->partition_name);
+ my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
DBUG_RETURN(TRUE);
}
- if (part_type == RANGE_PARTITION)
- {
- if (part_elem->has_null_value)
- {
- my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
- DBUG_RETURN(TRUE);
- }
- part_elem->range_value= val->value;
- }
+ part_elem->range_value= val->value;
}
col_val->fixed= 2;
DBUG_RETURN(FALSE);
@@ -2828,6 +2817,7 @@ bool partition_info::fix_parser_data(THD *thd)
key_algorithm == KEY_ALGORITHM_NONE)
key_algorithm= KEY_ALGORITHM_55;
}
+ defined_max_value= FALSE; // in case it already set (CREATE TABLE LIKE)
do
{
part_elem= it++;
@@ -2835,16 +2825,60 @@ bool partition_info::fix_parser_data(THD *thd)
num_elements= part_elem->list_val_list.elements;
DBUG_ASSERT(part_type == RANGE_PARTITION ?
num_elements == 1U : TRUE);
+
for (j= 0; j < num_elements; j++)
{
part_elem_value *val= list_val_it++;
- if (column_list)
+
+ if (val->added_items != (column_list ? num_columns : 1))
+ {
+ my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Check the last MAX_VALUE for range partitions and DEFAULT value
+ for LIST partitions.
+ Both values are marked with defined_max_value and
+ default_partition_id.
+
+ This is a max_value/default is max_value is set and this is
+ a normal RANGE (no column list) or if it's a LIST partition:
+
+ PARTITION p3 VALUES LESS THAN MAXVALUE
+ or
+ PARTITION p3 VALUES DEFAULT
+ */
+ if (val->added_items && val->col_val_array[0].max_value &&
+ (!column_list || part_type == LIST_PARTITION))
{
- if (val->added_items != num_columns)
+ DBUG_ASSERT(part_type == RANGE_PARTITION ||
+ part_type == LIST_PARTITION);
+ if (defined_max_value)
+ {
+ my_error((part_type == RANGE_PARTITION) ?
+ ER_PARTITION_MAXVALUE_ERROR :
+ ER_PARTITION_DEFAULT_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* For RANGE PARTITION MAX_VALUE must be last */
+ if (i != (num_parts - 1) &&
+ part_type != LIST_PARTITION)
{
- my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
+
+ defined_max_value= TRUE;
+ default_partition_id= i;
+ part_elem->max_value= TRUE;
+ part_elem->range_value= LONGLONG_MAX;
+ continue;
+ }
+
+ if (column_list)
+ {
for (k= 0; k < num_columns; k++)
{
part_column_list_val *col_val= &val->col_val_array[k];
@@ -2857,10 +2891,8 @@ bool partition_info::fix_parser_data(THD *thd)
}
else
{
- if (fix_partition_values(thd, val, part_elem, i))
- {
+ if (fix_partition_values(thd, val, part_elem))
DBUG_RETURN(TRUE);
- }
if (val->null_value)
{
/*
diff --git a/sql/partition_info.h b/sql/partition_info.h
index 5181e19d568..66579be6384 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -202,6 +202,7 @@ public:
uint num_full_part_fields;
uint has_null_part_id;
+ uint32 default_partition_id;
/*
This variable is used to calculate the partition id when using
LINEAR KEY/HASH. This functionality is kept in the MySQL Server
@@ -230,6 +231,10 @@ public:
bool use_default_num_subpartitions;
bool default_partitions_setup;
bool defined_max_value;
+ inline bool has_default_partititon()
+ {
+ return (part_type == LIST_PARTITION && defined_max_value);
+ }
bool list_of_part_fields; // KEY or COLUMNS PARTITIONING
bool list_of_subpart_fields; // KEY SUBPARTITIONING
bool linear_hash_ind; // LINEAR HASH/KEY
@@ -323,8 +328,7 @@ public:
Item* get_column_item(Item *item, Field *field);
int fix_partition_values(THD *thd,
part_elem_value *val,
- partition_element *part_elem,
- uint part_id);
+ partition_element *part_elem);
bool fix_column_value_functions(THD *thd,
part_elem_value *val,
uint part_id);
@@ -399,6 +403,7 @@ static inline void init_single_partition_iterator(uint32 part_id,
part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
part_iter->part_nums.end= part_id+1;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range;
}
@@ -410,6 +415,7 @@ void init_all_partitions_iterator(partition_info *part_info,
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
part_iter->part_nums.end= part_info->num_parts;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range;
}
diff --git a/sql/procedure.h b/sql/procedure.h
index be631675d5c..b9d5066bb3d 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -58,6 +58,7 @@ public:
DBUG_ASSERT(0); // impossible
return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE);
}
+ Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
class Item_proc_real :public Item_proc
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 608ec553da0..be73c94c9b2 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -35,7 +35,8 @@ static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
/* Declared non-static only because of the embedded library. */
bool net_send_error_packet(THD *, uint, const char *, const char *);
/* Declared non-static only because of the embedded library. */
-bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, bool);
+bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
+ bool, bool);
/* Declared non-static only because of the embedded library. */
bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
#ifndef EMBEDDED_LIBRARY
@@ -197,7 +198,8 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
@param affected_rows Number of rows changed by statement
@param id Auto_increment id for first row (if used)
@param message Message to send to the client (Used by mysql_status)
-
+ @param is_eof this called instead of old EOF packet
+
@return
@retval FALSE The message was successfully sent
@retval TRUE An error occurred and the messages wasn't sent properly
@@ -209,10 +211,14 @@ bool
net_send_ok(THD *thd,
uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong id, const char *message,
+ bool is_eof,
bool skip_flush)
{
NET *net= &thd->net;
- uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
+ StringBuffer<MYSQL_ERRMSG_SIZE + 10> store;
+
+ bool state_changed= false;
+
bool error= FALSE;
DBUG_ENTER("net_send_ok");
@@ -222,38 +228,67 @@ net_send_ok(THD *thd,
DBUG_RETURN(FALSE);
}
- buff[0]=0; // No fields
- pos=net_store_length(buff+1,affected_rows);
- pos=net_store_length(pos, id);
+ /*
+ OK send instead of EOF still require 0xFE header, but OK packet content.
+ */
+ if (is_eof)
+ {
+ DBUG_ASSERT(thd->client_capabilities & CLIENT_DEPRECATE_EOF);
+ store.q_append((char)254);
+ }
+ else
+ store.q_append('\0');
+
+ /* affected rows */
+ store.q_net_store_length(affected_rows);
+
+ /* last insert id */
+ store.q_net_store_length(id);
+
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
DBUG_PRINT("info",
("affected_rows: %lu id: %lu status: %u warning_count: %u",
- (ulong) affected_rows,
+ (ulong) affected_rows,
(ulong) id,
(uint) (server_status & 0xffff),
(uint) statement_warn_count));
- int2store(pos, server_status);
- pos+=2;
+ store.q_append2b(server_status);
/* We can only return up to 65535 warnings in two bytes */
uint tmp= MY_MIN(statement_warn_count, 65535);
- int2store(pos, tmp);
- pos+= 2;
+ store.q_append2b(tmp);
}
else if (net->return_status) // For 4.0 protocol
{
- int2store(pos, server_status);
- pos+=2;
+ store.q_append2b(server_status);
}
thd->get_stmt_da()->set_overwrite_status(true);
- if (message && message[0])
- pos= net_store_data(pos, (uchar*) message, strlen(message));
- error= my_net_write(net, buff, (size_t) (pos-buff));
- if (!error && !skip_flush)
+ state_changed=
+ (thd->client_capabilities & CLIENT_SESSION_TRACK) &&
+ (server_status & SERVER_SESSION_STATE_CHANGED);
+
+ if (state_changed || (message && message[0]))
+ {
+ DBUG_ASSERT(safe_strlen(message) <= MYSQL_ERRMSG_SIZE);
+ store.q_net_store_data((uchar*) safe_str(message), safe_strlen(message));
+ }
+
+ if (unlikely(state_changed))
+ {
+ store.set_charset(thd->variables.collation_database);
+
+ thd->session_tracker.store(thd, &store);
+ }
+
+ DBUG_ASSERT(store.length() <= MAX_PACKET_LENGTH);
+
+ error= my_net_write(net, (const unsigned char*)store.ptr(), store.length());
+ if (!error && (!skip_flush || is_eof))
error= net_flush(net);
+ thd->server_status&= ~SERVER_SESSION_STATE_CHANGED;
thd->get_stmt_da()->set_overwrite_status(false);
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
@@ -261,6 +296,7 @@ net_send_ok(THD *thd,
DBUG_RETURN(error);
}
+
static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
/**
@@ -292,6 +328,22 @@ net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
NET *net= &thd->net;
bool error= FALSE;
DBUG_ENTER("net_send_eof");
+
+ /*
+ Check if client understand new format packets (OK instead of EOF)
+
+ Normally end of statement reply is signaled by OK packet, but in case
+ of binlog dump request an EOF packet is sent instead. Also, old clients
+ expect EOF packet instead of OK
+ */
+ if ((thd->client_capabilities & CLIENT_DEPRECATE_EOF) &&
+ (thd->get_command() != COM_BINLOG_DUMP ))
+ {
+ error= net_send_ok(thd, server_status, statement_warn_count, 0, 0, NULL,
+ true, false);
+ DBUG_RETURN(error);
+ }
+
/* Set to TRUE if no active vio, to work well in case of --init-file */
if (net->vio != 0)
{
@@ -374,7 +426,8 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
uint error;
char converted_err[MYSQL_ERRMSG_SIZE];
char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
-
+ my_bool ret;
+ uint8 save_compress;
DBUG_ENTER("send_error_packet");
if (net->vio == 0)
@@ -402,8 +455,16 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
/* Converted error message is always null-terminated. */
length= (uint) (strmake(pos, converted_err, MYSQL_ERRMSG_SIZE - 1) - buff);
- DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) buff,
- length));
+ /*
+ Ensure that errors are not compressed. This is to ensure we can
+ detect out of bands error messages in the client
+ */
+ if ((save_compress= net->compress))
+ net->compress= 2;
+ ret= net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) buff,
+ length);
+ net->compress= save_compress;
+ DBUG_RETURN(ret);
}
#endif /* EMBEDDED_LIBRARY */
@@ -546,9 +607,9 @@ bool Protocol::send_ok(uint server_status, uint statement_warn_count,
const char *message, bool skip_flush)
{
DBUG_ENTER("Protocol::send_ok");
- const bool retval=
+ const bool retval=
net_send_ok(thd, server_status, statement_warn_count,
- affected_rows, last_insert_id, message, skip_flush);
+ affected_rows, last_insert_id, message, false, skip_flush);
DBUG_RETURN(retval);
}
@@ -562,7 +623,7 @@ bool Protocol::send_ok(uint server_status, uint statement_warn_count,
bool Protocol::send_eof(uint server_status, uint statement_warn_count)
{
DBUG_ENTER("Protocol::send_eof");
- const bool retval= net_send_eof(thd, server_status, statement_warn_count);
+ bool retval= net_send_eof(thd, server_status, statement_warn_count);
DBUG_RETURN(retval);
}
@@ -862,14 +923,19 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
if (flags & SEND_EOF)
{
- /*
- Mark the end of meta-data result set, and store thd->server_status,
- to show that there is no cursor.
- Send no warning information, as it will be sent at statement end.
- */
- if (write_eof_packet(thd, &thd->net, thd->server_status,
- thd->get_stmt_da()->current_statement_warn_count()))
- DBUG_RETURN(1);
+
+ /* if it is new client do not send EOF packet */
+ if (!(thd->client_capabilities & CLIENT_DEPRECATE_EOF))
+ {
+ /*
+ Mark the end of meta-data result set, and store thd->server_status,
+ to show that there is no cursor.
+ Send no warning information, as it will be sent at statement end.
+ */
+ if (write_eof_packet(thd, &thd->net, thd->server_status,
+ thd->get_stmt_da()->current_statement_warn_count()))
+ DBUG_RETURN(1);
+ }
}
DBUG_RETURN(prepare_for_send(list->elements));
@@ -1505,6 +1571,7 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals)
bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
{
+ bool ret;
if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
{
/* The client does not support OUT-parameters. */
@@ -1558,8 +1625,7 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
/* Restore THD::server_status. */
thd->server_status&= ~SERVER_PS_OUT_PARAMS;
- /* Send EOF-packet. */
- net_send_eof(thd, thd->server_status, 0);
+ ret= net_send_eof(thd, thd->server_status, 0);
/*
Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
@@ -1567,5 +1633,5 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
*/
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- return FALSE;
+ return ret ? FALSE : TRUE;
}
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 02dbac46eb5..6048d26998b 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -1233,8 +1233,6 @@ Master_info_index::get_master_info(const LEX_STRING *connection_name,
connection_name->str));
mysql_mutex_assert_owner(&LOCK_active_mi);
- if (!this) // master_info_index is set to NULL on server shutdown
- DBUG_RETURN(NULL);
/* Make name lower case for comparison */
res= strmake(buff, connection_name->str, connection_name->length);
@@ -1388,8 +1386,6 @@ bool Master_info_index::give_error_if_slave_running()
{
DBUG_ENTER("give_error_if_slave_running");
mysql_mutex_assert_owner(&LOCK_active_mi);
- if (!this) // master_info_index is set to NULL on server shutdown
- DBUG_RETURN(TRUE);
for (uint i= 0; i< master_info_hash.records; ++i)
{
@@ -1420,8 +1416,7 @@ uint Master_info_index::any_slave_sql_running()
{
uint count= 0;
DBUG_ENTER("any_slave_sql_running");
- if (!this) // master_info_index is set to NULL on server shutdown
- DBUG_RETURN(count);
+ mysql_mutex_assert_owner(&LOCK_active_mi);
for (uint i= 0; i< master_info_hash.records; ++i)
{
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 18c83608cd3..8cd7474cb73 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -107,6 +107,25 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
}
+/*
+ Wait for any pending deadlock kills. Since deadlock kills happen
+ asynchronously, we need to be sure they will be completed before starting a
+ new transaction. Otherwise the new transaction might suffer a spurious kill.
+*/
+static void
+wait_for_pending_deadlock_kill(THD *thd, rpl_group_info *rgi)
+{
+ PSI_stage_info old_stage;
+
+ mysql_mutex_lock(&thd->LOCK_wakeup_ready);
+ thd->ENTER_COND(&thd->COND_wakeup_ready, &thd->LOCK_wakeup_ready,
+ &stage_waiting_for_deadlock_kill, &old_stage);
+ while (rgi->killed_for_retry == rpl_group_info::RETRY_KILL_PENDING)
+ mysql_cond_wait(&thd->COND_wakeup_ready, &thd->LOCK_wakeup_ready);
+ thd->EXIT_COND(&old_stage);
+}
+
+
static void
finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
rpl_parallel_entry *entry, rpl_group_info *rgi)
@@ -212,6 +231,8 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
entry->stop_on_error_sub_id= sub_id;
mysql_mutex_unlock(&entry->LOCK_parallel_entry);
+ if (rgi->killed_for_retry == rpl_group_info::RETRY_KILL_PENDING)
+ wait_for_pending_deadlock_kill(thd, rgi);
thd->clear_error();
thd->reset_killed();
/*
@@ -307,6 +328,7 @@ do_gco_wait(rpl_group_info *rgi, group_commit_orderer *gco,
&stage_waiting_for_prior_transaction_to_start_commit,
old_stage);
*did_enter_cond= true;
+ thd->set_time_for_next_stage();
do
{
if (thd->check_killed() && !rgi->worker_error)
@@ -369,6 +391,7 @@ do_ftwrl_wait(rpl_group_info *rgi,
thd->ENTER_COND(&entry->COND_parallel_entry, &entry->LOCK_parallel_entry,
&stage_waiting_for_ftwrl, old_stage);
*did_enter_cond= true;
+ thd->set_time_for_next_stage();
do
{
if (entry->force_abort || rgi->worker_error)
@@ -417,8 +440,11 @@ pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd)
*/
mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
if (thd)
+ {
thd->ENTER_COND(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool,
&stage_waiting_for_rpl_thread_pool, &old_stage);
+ thd->set_time_for_next_stage();
+ }
while (pool->busy)
{
if (thd && thd->check_killed())
@@ -534,6 +560,7 @@ rpl_pause_for_ftwrl(THD *thd)
e->pause_sub_id= e->largest_started_sub_id;
thd->ENTER_COND(&e->COND_parallel_entry, &e->LOCK_parallel_entry,
&stage_waiting_for_ftwrl_threads_to_pause, &old_stage);
+ thd->set_time_for_next_stage();
while (e->pause_sub_id < (uint64)ULONGLONG_MAX &&
e->last_committed_sub_id < e->pause_sub_id &&
!err)
@@ -604,7 +631,6 @@ convert_kill_to_deadlock_error(rpl_group_info *rgi)
{
thd->clear_error();
my_error(ER_LOCK_DEADLOCK, MYF(0));
- rgi->killed_for_retry= false;
thd->reset_killed();
}
}
@@ -695,14 +721,16 @@ do_retry:
thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
DBUG_EXECUTE_IF("inject_mdev8031", {
/* Simulate that we get deadlock killed at this exact point. */
- rgi->killed_for_retry= true;
+ rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
mysql_mutex_lock(&thd->LOCK_thd_data);
thd->killed= KILL_CONNECTION;
mysql_mutex_unlock(&thd->LOCK_thd_data);
});
rgi->cleanup_context(thd, 1);
+ wait_for_pending_deadlock_kill(thd, rgi);
thd->reset_killed();
thd->clear_error();
+ rgi->killed_for_retry = rpl_group_info::RETRY_KILL_NONE;
/*
If we retry due to a deadlock kill that occurred during the commit step, we
@@ -841,7 +869,7 @@ do_retry:
{
/* Simulate that we get deadlock killed during open_binlog(). */
thd->reset_for_next_command();
- rgi->killed_for_retry= true;
+ rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
mysql_mutex_lock(&thd->LOCK_thd_data);
thd->killed= KILL_CONNECTION;
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -995,7 +1023,6 @@ handle_rpl_parallel_thread(void *arg)
*/
thd->variables.tx_isolation= ISO_REPEATABLE_READ;
-
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
rpt->thd= thd;
@@ -1005,8 +1032,10 @@ handle_rpl_parallel_thread(void *arg)
rpt->running= true;
mysql_cond_signal(&rpt->COND_rpl_thread);
+ thd->set_command(COM_SLAVE_WORKER);
while (!rpt->stop)
{
+ uint wait_count= 0;
rpl_parallel_thread::queued_event *qev, *next_qev;
thd->ENTER_COND(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread,
@@ -1025,7 +1054,11 @@ handle_rpl_parallel_thread(void *arg)
(rpt->current_owner && !in_event_group) ||
(rpt->current_owner && group_rgi->parallel_entry->force_abort) ||
rpt->stop))
+ {
+ if (!wait_count++)
+ thd->set_time_for_next_stage();
mysql_cond_wait(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread);
+ }
rpt->dequeue1(events);
thd->EXIT_COND(&old_stage);
@@ -1736,7 +1769,7 @@ rpl_parallel_thread::get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev,
rgi->relay_log= rli->last_inuse_relaylog;
rgi->retry_start_offset= rli->future_event_relay_log_pos-event_size;
rgi->retry_event_count= 0;
- rgi->killed_for_retry= false;
+ rgi->killed_for_retry= rpl_group_info::RETRY_KILL_NONE;
return rgi;
}
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index c1a1c440922..de6e37aecaf 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1939,8 +1939,8 @@ rpl_group_info::mark_start_commit_no_lock()
{
if (did_mark_start_commit)
return;
- mark_start_commit_inner(parallel_entry, gco, this);
did_mark_start_commit= true;
+ mark_start_commit_inner(parallel_entry, gco, this);
}
@@ -1951,12 +1951,12 @@ rpl_group_info::mark_start_commit()
if (did_mark_start_commit)
return;
+ did_mark_start_commit= true;
e= this->parallel_entry;
mysql_mutex_lock(&e->LOCK_parallel_entry);
mark_start_commit_inner(e, gco, this);
mysql_mutex_unlock(&e->LOCK_parallel_entry);
- did_mark_start_commit= true;
}
@@ -1999,12 +1999,12 @@ rpl_group_info::unmark_start_commit()
if (!did_mark_start_commit)
return;
+ did_mark_start_commit= false;
e= this->parallel_entry;
mysql_mutex_lock(&e->LOCK_parallel_entry);
--e->count_committing_event_groups;
mysql_mutex_unlock(&e->LOCK_parallel_entry);
- did_mark_start_commit= false;
}
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index c5b495cbc30..c8673db7f73 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -713,7 +713,12 @@ struct rpl_group_info
*/
SPECULATE_WAIT
} speculation;
- bool killed_for_retry;
+ enum enum_retry_killed {
+ RETRY_KILL_NONE = 0,
+ RETRY_KILL_PENDING,
+ RETRY_KILL_KILLED
+ };
+ uchar killed_for_retry;
rpl_group_info(Relay_log_info *rli_);
~rpl_group_info();
diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
new file mode 100644
index 00000000000..3272d2a41f0
--- /dev/null
+++ b/sql/session_tracker.cc
@@ -0,0 +1,1710 @@
+/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2016, MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#ifndef EMBEDDED_LIBRARY
+#include "sql_plugin.h"
+#include "session_tracker.h"
+
+#include "hash.h"
+#include "table.h"
+#include "rpl_gtid.h"
+#include "sql_class.h"
+#include "sql_show.h"
+#include "sql_plugin.h"
+#include "set_var.h"
+
+void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name)
+{
+ m_changed= true;
+ thd->lex->safe_to_cache_query= 0;
+ thd->server_status|= SERVER_SESSION_STATE_CHANGED;
+}
+
+
+class Not_implemented_tracker : public State_tracker
+{
+public:
+ bool enable(THD *thd)
+ { return false; }
+ bool update(THD *, set_var *)
+ { return false; }
+ bool store(THD *, String *)
+ { return false; }
+ void mark_as_changed(THD *, LEX_CSTRING *tracked_item_name)
+ {}
+
+};
+
+/**
+ Session_sysvars_tracker
+
+ This is a tracker class that enables & manages the tracking of session
+ system variables. It internally maintains a hash of user supplied variable
+ references and a boolean field to store if the variable was changed by the
+ last statement.
+*/
+
+class Session_sysvars_tracker : public State_tracker
+{
+private:
+
+ struct sysvar_node_st {
+ sys_var *m_svar;
+ bool *test_load;
+ bool m_changed;
+ };
+
+ class vars_list
+ {
+ private:
+ /**
+ Registered system variables. (@@session_track_system_variables)
+ A hash to store the name of all the system variables specified by the
+ user.
+ */
+ HASH m_registered_sysvars;
+ /** Size of buffer for string representation */
+ size_t buffer_length;
+ myf m_mem_flag;
+ /**
+ If TRUE then we want to check all session variable.
+ */
+ bool track_all;
+ void init()
+ {
+ my_hash_init(&m_registered_sysvars,
+ &my_charset_bin,
+ 4, 0, 0, (my_hash_get_key) sysvars_get_key,
+ my_free, MYF(HASH_UNIQUE |
+ ((m_mem_flag & MY_THREAD_SPECIFIC) ?
+ HASH_THREAD_SPECIFIC : 0)));
+ }
+ void free_hash()
+ {
+ if (my_hash_inited(&m_registered_sysvars))
+ {
+ my_hash_free(&m_registered_sysvars);
+ }
+ }
+
+ uchar* search(const sys_var *svar)
+ {
+ return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar,
+ sizeof(sys_var *)));
+ }
+
+ public:
+ vars_list() :
+ buffer_length(0)
+ {
+ m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0;
+ init();
+ }
+
+ size_t get_buffer_length()
+ {
+ DBUG_ASSERT(buffer_length != 0); // asked earlier then should
+ return buffer_length;
+ }
+ ~vars_list()
+ {
+ /* free the allocated hash. */
+ if (my_hash_inited(&m_registered_sysvars))
+ {
+ my_hash_free(&m_registered_sysvars);
+ }
+ }
+
+ uchar* insert_or_search(sysvar_node_st *node, const sys_var *svar)
+ {
+ uchar *res;
+ res= search(svar);
+ if (!res)
+ {
+ if (track_all)
+ {
+ insert(node, svar, m_mem_flag);
+ return search(svar);
+ }
+ }
+ return res;
+ }
+
+ bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag);
+ void reinit();
+ void reset();
+ inline bool is_enabled()
+ {
+ return track_all || m_registered_sysvars.records;
+ }
+ void copy(vars_list* from, THD *thd);
+ bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
+ CHARSET_INFO *char_set, bool take_mutex);
+ bool construct_var_list(char *buf, size_t buf_len);
+ bool store(THD *thd, String *buf);
+ };
+ /**
+ Two objects of vars_list type are maintained to manage
+ various operations.
+ */
+ vars_list *orig_list, *tool_list;
+
+public:
+ Session_sysvars_tracker()
+ {
+ orig_list= new (std::nothrow) vars_list();
+ tool_list= new (std::nothrow) vars_list();
+ }
+
+ ~Session_sysvars_tracker()
+ {
+ if (orig_list)
+ delete orig_list;
+ if (tool_list)
+ delete tool_list;
+ }
+
+ size_t get_buffer_length()
+ {
+ return orig_list->get_buffer_length();
+ }
+ bool construct_var_list(char *buf, size_t buf_len)
+ {
+ return orig_list->construct_var_list(buf, buf_len);
+ }
+
+ /**
+ Method used to check the validity of string provided
+ for session_track_system_variables during the server
+ startup.
+ */
+ static bool server_init_check(THD *thd, CHARSET_INFO *char_set,
+ LEX_STRING var_list)
+ {
+ return check_var_list(thd, var_list, false, char_set, false);
+ }
+
+ static bool server_init_process(THD *thd, CHARSET_INFO *char_set,
+ LEX_STRING var_list)
+ {
+ vars_list dummy;
+ bool result;
+ result= dummy.parse_var_list(thd, var_list, false, char_set, false);
+ if (!result)
+ dummy.construct_var_list(var_list.str, var_list.length + 1);
+ return result;
+ }
+
+ void reset();
+ bool enable(THD *thd);
+ bool check_str(THD *thd, LEX_STRING *val);
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+ void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
+ /* callback */
+ static uchar *sysvars_get_key(const char *entry, size_t *length,
+ my_bool not_used __attribute__((unused)));
+
+ // hash iterators
+ static my_bool name_array_filler(void *ptr, void *data_ptr);
+ static my_bool store_variable(void *ptr, void *data_ptr);
+ static my_bool reset_variable(void *ptr, void *data_ptr);
+
+ static bool check_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
+ CHARSET_INFO *char_set, bool take_mutex);
+};
+
+
+
+/**
+ Current_schema_tracker,
+
+ This is a tracker class that enables & manages the tracking of current
+ schema for a particular connection.
+*/
+
+class Current_schema_tracker : public State_tracker
+{
+private:
+ bool schema_track_inited;
+ void reset();
+
+public:
+
+ Current_schema_tracker()
+ {
+ schema_track_inited= false;
+ }
+
+ bool enable(THD *thd)
+ { return update(thd, NULL); }
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+};
+
+/*
+ Session_state_change_tracker
+
+ This is a boolean tracker class that will monitor any change that contributes
+ to a session state change.
+ Attributes that contribute to session state change include:
+ - Successful change to System variables
+ - User defined variables assignments
+ - temporary tables created, altered or deleted
+ - prepared statements added or removed
+ - change in current database
+ - change of current role
+*/
+
+class Session_state_change_tracker : public State_tracker
+{
+private:
+
+ void reset();
+
+public:
+ Session_state_change_tracker();
+ bool enable(THD *thd)
+ { return update(thd, NULL); };
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+ bool is_state_changed(THD*);
+};
+
+
+/* To be used in expanding the buffer. */
+static const unsigned int EXTRA_ALLOC= 1024;
+
+
+void Session_sysvars_tracker::vars_list::reinit()
+{
+ buffer_length= 0;
+ track_all= 0;
+ if (m_registered_sysvars.records)
+ my_hash_reset(&m_registered_sysvars);
+}
+
+/**
+ Copy the given list.
+
+ @param from Source vars_list object.
+ @param thd THD handle to retrive the charset in use.
+
+ @retval true there is something to track
+ @retval false nothing to track
+*/
+
+void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
+{
+ reinit();
+ track_all= from->track_all;
+ free_hash();
+ buffer_length= from->buffer_length;
+ m_registered_sysvars= from->m_registered_sysvars;
+ from->init();
+}
+
+/**
+ Inserts the variable to be tracked into m_registered_sysvars hash.
+
+ @param node Node to be inserted.
+ @param svar address of the system variable
+
+ @retval false success
+ @retval true error
+*/
+
+bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
+ const sys_var *svar,
+ myf mem_flag)
+{
+ if (!node)
+ {
+ if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
+ MYF(MY_WME | mem_flag))))
+ {
+ reinit();
+ return true;
+ }
+ }
+
+ node->m_svar= (sys_var *)svar;
+ node->test_load= node->m_svar->test_load;
+ node->m_changed= false;
+ if (my_hash_insert(&m_registered_sysvars, (uchar *) node))
+ {
+ my_free(node);
+ if (!search((sys_var *)svar))
+ {
+ //EOF (error is already reported)
+ reinit();
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ Parse the specified system variables list.
+
+ @Note In case of invalid entry a warning is raised per invalid entry.
+ This is done in order to handle 'potentially' valid system
+ variables from uninstalled plugins which might get installed in
+ future.
+
+
+ @param thd [IN] The thd handle.
+ @param var_list [IN] System variable list.
+ @param throw_error [IN] bool when set to true, returns an error
+ in case of invalid/duplicate values.
+ @param char_set [IN] charecter set information used for string
+ manipulations.
+ @param take_mutex [IN] take LOCK_plugin
+
+ @return
+ true Error
+ false Success
+*/
+bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
+ LEX_STRING var_list,
+ bool throw_error,
+ CHARSET_INFO *char_set,
+ bool take_mutex)
+{
+ const char separator= ',';
+ char *token, *lasts= NULL;
+ size_t rest= var_list.length;
+ reinit();
+
+ if (!var_list.str || var_list.length == 0)
+ {
+ buffer_length= 1;
+ return false;
+ }
+
+ if(!strcmp(var_list.str,(const char *)"*"))
+ {
+ track_all= true;
+ buffer_length= 2;
+ return false;
+ }
+
+ buffer_length= var_list.length + 1;
+ token= var_list.str;
+
+ track_all= false;
+ /*
+ If Lock to the plugin mutex is not acquired here itself, it results
+ in having to acquire it multiple times in find_sys_var_ex for each
+ token value. Hence the mutex is handled here to avoid a performance
+ overhead.
+ */
+ if (!thd || take_mutex)
+ mysql_mutex_lock(&LOCK_plugin);
+ for (;;)
+ {
+ sys_var *svar;
+ LEX_STRING var;
+
+ lasts= (char *) memchr(token, separator, rest);
+
+ var.str= token;
+ if (lasts)
+ {
+ var.length= (lasts - token);
+ rest-= var.length + 1;
+ }
+ else
+ var.length= rest;
+
+ /* Remove leading/trailing whitespace. */
+ trim_whitespace(char_set, &var);
+
+ if(!strcmp(var.str,(const char *)"*"))
+ {
+ track_all= true;
+ }
+ else if ((svar=
+ find_sys_var_ex(thd, var.str, var.length, throw_error, true)))
+ {
+ if (insert(NULL, svar, m_mem_flag) == TRUE)
+ goto error;
+ }
+ else if (throw_error && thd)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "%.*s is not a valid system variable and will"
+ "be ignored.", (int)var.length, token);
+ }
+ else
+ goto error;
+
+ if (lasts)
+ token= lasts + 1;
+ else
+ break;
+ }
+ if (!thd || take_mutex)
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ return false;
+
+error:
+ if (!thd || take_mutex)
+ mysql_mutex_unlock(&LOCK_plugin);
+ return true;
+}
+
+
+bool Session_sysvars_tracker::check_var_list(THD *thd,
+ LEX_STRING var_list,
+ bool throw_error,
+ CHARSET_INFO *char_set,
+ bool take_mutex)
+{
+ const char separator= ',';
+ char *token, *lasts= NULL;
+ size_t rest= var_list.length;
+
+ if (!var_list.str || var_list.length == 0 ||
+ !strcmp(var_list.str,(const char *)"*"))
+ {
+ return false;
+ }
+
+ token= var_list.str;
+
+ /*
+ If Lock to the plugin mutex is not acquired here itself, it results
+ in having to acquire it multiple times in find_sys_var_ex for each
+ token value. Hence the mutex is handled here to avoid a performance
+ overhead.
+ */
+ if (!thd || take_mutex)
+ mysql_mutex_lock(&LOCK_plugin);
+ for (;;)
+ {
+ LEX_STRING var;
+
+ lasts= (char *) memchr(token, separator, rest);
+
+ var.str= token;
+ if (lasts)
+ {
+ var.length= (lasts - token);
+ rest-= var.length + 1;
+ }
+ else
+ var.length= rest;
+
+ /* Remove leading/trailing whitespace. */
+ trim_whitespace(char_set, &var);
+
+ if(!strcmp(var.str,(const char *)"*") &&
+ !find_sys_var_ex(thd, var.str, var.length, throw_error, true))
+ {
+ if (throw_error && take_mutex && thd)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "%.*s is not a valid system variable and will"
+ "be ignored.", (int)var.length, token);
+ }
+ else
+ {
+ if (!thd || take_mutex)
+ mysql_mutex_unlock(&LOCK_plugin);
+ return true;
+ }
+ }
+
+ if (lasts)
+ token= lasts + 1;
+ else
+ break;
+ }
+ if (!thd || take_mutex)
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ return false;
+}
+
+struct name_array_filler_data
+{
+ LEX_CSTRING **names;
+ uint idx;
+
+};
+
+/** Collects variable references into array */
+my_bool Session_sysvars_tracker::name_array_filler(void *ptr,
+ void *data_ptr)
+{
+ Session_sysvars_tracker::sysvar_node_st *node=
+ (Session_sysvars_tracker::sysvar_node_st *)ptr;
+ name_array_filler_data *data= (struct name_array_filler_data *)data_ptr;
+ if (*node->test_load)
+ data->names[data->idx++]= &node->m_svar->name;
+ return FALSE;
+}
+
+/* Sorts variable references array */
+static int name_array_sorter(const void *a, const void *b)
+{
+ LEX_CSTRING **an= (LEX_CSTRING **)a, **bn=(LEX_CSTRING **)b;
+ size_t min= MY_MIN((*an)->length, (*bn)->length);
+ int res= strncmp((*an)->str, (*bn)->str, min);
+ if (res == 0)
+ res= ((int)(*bn)->length)- ((int)(*an)->length);
+ return res;
+}
+
+/**
+ Construct variable list by internal hash with references
+*/
+
+bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
+ size_t buf_len)
+{
+ struct name_array_filler_data data;
+ size_t left= buf_len;
+ size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *);
+ const char separator= ',';
+
+ if (unlikely(buf_len < 1))
+ return true;
+
+ if (unlikely(track_all))
+ {
+ if (buf_len < 2)
+ return true;
+ buf[0]= '*';
+ buf[1]= '\0';
+ return false;
+ }
+
+ if (m_registered_sysvars.records == 0)
+ {
+ buf[0]= '\0';
+ return false;
+ }
+
+ data.names= (LEX_CSTRING**)my_safe_alloca(names_size);
+
+ if (unlikely(!data.names))
+ return true;
+
+ data.idx= 0;
+
+ mysql_mutex_lock(&LOCK_plugin);
+ my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data);
+ DBUG_ASSERT(data.idx <= m_registered_sysvars.records);
+
+ /*
+ We check number of records again here because number of variables
+ could be reduced in case of plugin unload.
+ */
+ if (m_registered_sysvars.records == 0)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ buf[0]= '\0';
+ return false;
+ }
+
+ my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *),
+ &name_array_sorter);
+
+ for(uint i= 0; i < data.idx; i++)
+ {
+ LEX_CSTRING *nm= data.names[i];
+ size_t ln= nm->length + 1;
+ if (ln > left)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ my_safe_afree(data.names, names_size);
+ return true;
+ }
+ memcpy(buf, nm->str, nm->length);
+ buf[nm->length]= separator;
+ buf+= ln;
+ left-= ln;
+ }
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ buf--; buf[0]= '\0';
+ my_safe_afree(data.names, names_size);
+
+ return false;
+}
+
+/**
+ Enable session tracker by parsing global value of tracked variables.
+
+ @param thd [IN] The thd handle.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::enable(THD *thd)
+{
+ mysql_mutex_lock(&LOCK_plugin);
+ LEX_STRING tmp;
+ tmp.str= global_system_variables.session_track_system_variables;
+ tmp.length= safe_strlen(tmp.str);
+ if (tool_list->parse_var_list(thd, tmp,
+ true, thd->charset(), false) == true)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ return true;
+ }
+ mysql_mutex_unlock(&LOCK_plugin);
+ orig_list->copy(tool_list, thd);
+ m_enabled= true;
+
+ return false;
+}
+
+
+/**
+ Check system variable name(s).
+
+ @note This function is called from the ON_CHECK() function of the
+ session_track_system_variables' sys_var class.
+
+ @param thd [IN] The thd handle.
+ @param var [IN] A pointer to set_var holding the specified list of
+ system variable names.
+
+ @retval true Error
+ @retval false Success
+*/
+
+inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val)
+{
+ return Session_sysvars_tracker::check_var_list(thd, *val, true,
+ thd->charset(), true);
+}
+
+
+/**
+ Once the value of the @@session_track_system_variables has been
+ successfully updated, this function calls
+ Session_sysvars_tracker::vars_list::copy updating the hash in orig_list
+ which represents the system variables to be tracked.
+
+ @note This function is called from the ON_UPDATE() function of the
+ session_track_system_variables' sys_var class.
+
+ @param thd [IN] The thd handle.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::update(THD *thd, set_var *var)
+{
+ /*
+ We are doing via tool list because there possible errors with memory
+ in this case value will be unchanged.
+ */
+ tool_list->reinit();
+ if (tool_list->parse_var_list(thd, var->save_result.string_value, true,
+ thd->charset(), true))
+ return true;
+ orig_list->copy(tool_list, thd);
+ return false;
+}
+
+
+/*
+ Function and structure to support storing variables from hash to the buffer.
+*/
+
+struct st_store_variable_param
+{
+ THD *thd;
+ String *buf;
+};
+
+my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr)
+{
+ Session_sysvars_tracker::sysvar_node_st *node=
+ (Session_sysvars_tracker::sysvar_node_st *)ptr;
+ if (node->m_changed)
+ {
+ THD *thd= ((st_store_variable_param *)data_ptr)->thd;
+ String *buf= ((st_store_variable_param *)data_ptr)->buf;
+ char val_buf[SHOW_VAR_FUNC_BUFF_SIZE];
+ SHOW_VAR show;
+ CHARSET_INFO *charset;
+ size_t val_length, length;
+ mysql_mutex_lock(&LOCK_plugin);
+ if (!*node->test_load)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ return false;
+ }
+ sys_var *svar= node->m_svar;
+ bool is_plugin= svar->cast_pluginvar();
+ if (!is_plugin)
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ /* As its always system variable. */
+ show.type= SHOW_SYS;
+ show.name= svar->name.str;
+ show.value= (char *) svar;
+
+ const char *value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL,
+ &charset, val_buf, &val_length);
+ if (is_plugin)
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ length= net_length_size(svar->name.length) +
+ svar->name.length +
+ net_length_size(val_length) +
+ val_length;
+
+ compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251);
+ if (unlikely((1 + net_length_size(length) + length + buf->length() >=
+ MAX_PACKET_LENGTH) ||
+ buf->reserve(1 + net_length_size(length) + length,
+ EXTRA_ALLOC)))
+ return true;
+
+
+ /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */
+ buf->q_append((char)SESSION_TRACK_SYSTEM_VARIABLES);
+
+ /* Length of the overall entity. */
+ buf->q_net_store_length((ulonglong)length);
+
+ /* System variable's name (length-encoded string). */
+ buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length);
+
+ /* System variable's value (length-encoded string). */
+ buf->q_net_store_data((const uchar*)value, val_length);
+ }
+ return false;
+}
+
+bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf)
+{
+ st_store_variable_param data= {thd, buf};
+ return my_hash_iterate(&m_registered_sysvars, &store_variable, &data);
+}
+
+/**
+ Store the data for changed system variables in the specified buffer.
+ Once the data is stored, we reset the flags related to state-change
+ (see reset()).
+
+ @param thd [IN] The thd handle.
+ @paran buf [INOUT] Buffer to store the information to.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::store(THD *thd, String *buf)
+{
+ if (!orig_list->is_enabled())
+ return false;
+
+ if (orig_list->store(thd, buf))
+ return true;
+
+ reset();
+
+ return false;
+}
+
+
+/**
+ Mark the system variable as changed.
+
+ @param [IN] pointer on a variable
+*/
+
+void Session_sysvars_tracker::mark_as_changed(THD *thd,
+ LEX_CSTRING *var)
+{
+ sysvar_node_st *node= NULL;
+ sys_var *svar= (sys_var *)var;
+ /*
+ Check if the specified system variable is being tracked, if so
+ mark it as changed and also set the class's m_changed flag.
+ */
+ if (orig_list->is_enabled() &&
+ (node= (sysvar_node_st *) (orig_list->insert_or_search(node, svar))))
+ {
+ node->m_changed= true;
+ State_tracker::mark_as_changed(thd, var);
+ }
+}
+
+
+/**
+ Supply key to a hash.
+
+ @param entry [IN] A single entry.
+ @param length [OUT] Length of the key.
+ @param not_used Unused.
+
+ @return Pointer to the key buffer.
+*/
+
+uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry,
+ size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= sizeof(sys_var *);
+ return (uchar *) &(((sysvar_node_st *) entry)->m_svar);
+}
+
+
+/* Function to support resetting hash nodes for the variables */
+
+my_bool Session_sysvars_tracker::reset_variable(void *ptr,
+ void *data_ptr)
+{
+ ((Session_sysvars_tracker::sysvar_node_st *)ptr)->m_changed= false;
+ return false;
+}
+
+void Session_sysvars_tracker::vars_list::reset()
+{
+ my_hash_iterate(&m_registered_sysvars, &reset_variable, NULL);
+}
+
+/**
+ Prepare/reset the m_registered_sysvars hash for next statement.
+*/
+
+void Session_sysvars_tracker::reset()
+{
+
+ orig_list->reset();
+ m_changed= false;
+}
+
+static Session_sysvars_tracker* sysvar_tracker(THD *thd)
+{
+ return (Session_sysvars_tracker*)
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER);
+}
+
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
+{
+ LEX_STRING tmp= {(char *)str, len};
+ return Session_sysvars_tracker::server_init_check(thd, system_charset_info,
+ tmp);
+}
+bool sysvartrack_reprint_value(THD *thd, char *str, size_t len)
+{
+ LEX_STRING tmp= {str, len};
+ return Session_sysvars_tracker::server_init_process(thd,
+ system_charset_info,
+ tmp);
+}
+bool sysvartrack_update(THD *thd, set_var *var)
+{
+ return sysvar_tracker(thd)->update(thd, var);
+}
+size_t sysvartrack_value_len(THD *thd)
+{
+ return sysvar_tracker(thd)->get_buffer_length();
+}
+bool sysvartrack_value_construct(THD *thd, char *val, size_t len)
+{
+ return sysvar_tracker(thd)->construct_var_list(val, len);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ Enable/disable the tracker based on @@session_track_schema's value.
+
+ @param thd [IN] The thd handle.
+
+ @return
+ false (always)
+*/
+
+bool Current_schema_tracker::update(THD *thd, set_var *)
+{
+ m_enabled= thd->variables.session_track_schema;
+ return false;
+}
+
+
+/**
+ Store the schema name as length-encoded string in the specified buffer.
+
+ @param thd [IN] The thd handle.
+ @paran buf [INOUT] Buffer to store the information to.
+
+ @reval false Success
+ @retval true Error
+*/
+
+bool Current_schema_tracker::store(THD *thd, String *buf)
+{
+ ulonglong db_length, length;
+
+ /*
+ Protocol made (by unknown reasons) redundant:
+ It saves length of database name and name of database name +
+ length of saved length of database length.
+ */
+ length= db_length= thd->db_length;
+ length += net_length_size(length);
+
+ compile_time_assert(SESSION_TRACK_SCHEMA < 251);
+ compile_time_assert(NAME_LEN < 251);
+ DBUG_ASSERT(length < 251);
+ if (unlikely((1 + 1 + length + buf->length() >= MAX_PACKET_LENGTH) ||
+ buf->reserve(1 + 1 + length, EXTRA_ALLOC)))
+ return true;
+
+ /* Session state type (SESSION_TRACK_SCHEMA) */
+ buf->q_append((char)SESSION_TRACK_SCHEMA);
+
+ /* Length of the overall entity. */
+ buf->q_net_store_length(length);
+
+ /* Length and current schema name */
+ buf->q_net_store_data((const uchar *)thd->db, thd->db_length);
+
+ reset();
+
+ return false;
+}
+
+
+/**
+ Reset the m_changed flag for next statement.
+
+ @return void
+*/
+
+void Current_schema_tracker::reset()
+{
+ m_changed= false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+Transaction_state_tracker::Transaction_state_tracker()
+{
+ m_enabled = false;
+ tx_changed = TX_CHG_NONE;
+ tx_curr_state =
+ tx_reported_state= TX_EMPTY;
+ tx_read_flags = TX_READ_INHERIT;
+ tx_isol_level = TX_ISOL_INHERIT;
+}
+
+/**
+ Enable/disable the tracker based on @@session_track_transaction_info.
+
+ @param thd [IN] The thd handle.
+
+ @retval true if updating the tracking level failed
+ @retval false otherwise
+*/
+
+bool Transaction_state_tracker::update(THD *thd, set_var *)
+{
+ if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
+ {
+ /*
+ If we only just turned reporting on (rather than changing between
+ state and characteristics reporting), start from a defined state.
+ */
+ if (!m_enabled)
+ {
+ tx_curr_state =
+ tx_reported_state = TX_EMPTY;
+ tx_changed |= TX_CHG_STATE;
+ m_enabled= true;
+ }
+ if (thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS)
+ tx_changed |= TX_CHG_CHISTICS;
+ mark_as_changed(thd, NULL);
+ }
+ else
+ m_enabled= false;
+
+ return false;
+}
+
+
+/**
+ Store the transaction state (and, optionally, characteristics)
+ as length-encoded string in the specified buffer. Once the data
+ is stored, we reset the flags related to state-change (see reset()).
+
+
+ @param thd [IN] The thd handle.
+ @paran buf [INOUT] Buffer to store the information to.
+
+ @retval false Success
+ @retval true Error
+*/
+
+static LEX_CSTRING isol[]= {
+ { STRING_WITH_LEN("READ UNCOMMITTED") },
+ { STRING_WITH_LEN("READ COMMITTED") },
+ { STRING_WITH_LEN("REPEATABLE READ") },
+ { STRING_WITH_LEN("SERIALIZABLE") }
+};
+
+bool Transaction_state_tracker::store(THD *thd, String *buf)
+{
+ /* STATE */
+ if (tx_changed & TX_CHG_STATE)
+ {
+ if (unlikely((11 + buf->length() >= MAX_PACKET_LENGTH) ||
+ buf->reserve(11, EXTRA_ALLOC)))
+ return true;
+
+ buf->q_append((char)SESSION_TRACK_TRANSACTION_STATE);
+
+ buf->q_append((char)9); // whole packet length
+ buf->q_append((char)8); // results length
+
+ buf->q_append((char)((tx_curr_state & TX_EXPLICIT) ? 'T' :
+ ((tx_curr_state & TX_IMPLICIT) ? 'I' : '_')));
+ buf->q_append((char)((tx_curr_state & TX_READ_UNSAFE) ? 'r' : '_'));
+ buf->q_append((char)(((tx_curr_state & TX_READ_TRX) ||
+ (tx_curr_state & TX_WITH_SNAPSHOT)) ? 'R' : '_'));
+ buf->q_append((char)((tx_curr_state & TX_WRITE_UNSAFE) ? 'w' : '_'));
+ buf->q_append((char)((tx_curr_state & TX_WRITE_TRX) ? 'W' : '_'));
+ buf->q_append((char)((tx_curr_state & TX_STMT_UNSAFE) ? 's' : '_'));
+ buf->q_append((char)((tx_curr_state & TX_RESULT_SET) ? 'S' : '_'));
+ buf->q_append((char)((tx_curr_state & TX_LOCKED_TABLES) ? 'L' : '_'));
+ }
+
+ /* CHARACTERISTICS -- How to restart the transaction */
+
+ if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) &&
+ (tx_changed & TX_CHG_CHISTICS))
+ {
+ bool is_xa= (thd->transaction.xid_state.xa_state != XA_NOTR);
+ size_t start;
+
+ /* 2 length by 1 byte and code */
+ if (unlikely((1 + 1 + 1 + 110 + buf->length() >= MAX_PACKET_LENGTH) ||
+ buf->reserve(1 + 1 + 1, EXTRA_ALLOC)))
+ return true;
+
+ compile_time_assert(SESSION_TRACK_TRANSACTION_CHARACTERISTICS < 251);
+ /* Session state type (SESSION_TRACK_TRANSACTION_CHARACTERISTICS) */
+ buf->q_append((char)SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
+
+ /* placeholders for lengths. will be filled in at the end */
+ buf->q_append('\0');
+ buf->q_append('\0');
+
+ start= buf->length();
+
+ {
+ /*
+ We have four basic replay scenarios:
+
+ a) SET TRANSACTION was used, but before an actual transaction
+ was started, the load balancer moves the connection elsewhere.
+ In that case, the same one-shots should be set up in the
+ target session. (read-only/read-write; isolation-level)
+
+ b) The initial transaction has begun; the relevant characteristics
+ are the session defaults, possibly overridden by previous
+ SET TRANSACTION statements, possibly overridden or extended
+ by options passed to the START TRANSACTION statement.
+ If the load balancer wishes to move this transaction,
+ it needs to be replayed with the correct characteristics.
+ (read-only/read-write from SET or START;
+ isolation-level from SET only, snapshot from START only)
+
+ c) A subsequent transaction started with START TRANSACTION
+ (which is legal syntax in lieu of COMMIT AND CHAIN in MySQL)
+ may add/modify the current one-shots:
+
+ - It may set up a read-only/read-write one-shot.
+ This one-shot will override the value used in the previous
+ transaction (whether that came from the default or a one-shot),
+ and, like all one-shots currently do, it will carry over into
+ any subsequent transactions that don't explicitly override them
+ in turn. This behavior is not guaranteed in the docs and may
+ change in the future, but the tracker item should correctly
+ reflect whatever behavior a given version of mysqld implements.
+
+ - It may also set up a WITH CONSISTENT SNAPSHOT one-shot.
+ This one-shot does not currently carry over into subsequent
+ transactions (meaning that with "traditional syntax", WITH
+ CONSISTENT SNAPSHOT can only be requested for the first part
+ of a transaction chain). Again, the tracker item should reflect
+ mysqld behavior.
+
+ d) A subsequent transaction started using COMMIT AND CHAIN
+ (or, for that matter, BEGIN WORK, which is currently
+ legal and equivalent syntax in MySQL, or START TRANSACTION
+ sans options) will re-use any one-shots set up so far
+ (with SET before the first transaction started, and with
+ all subsequent STARTs), except for WITH CONSISTANT SNAPSHOT,
+ which will never be chained and only applies when explicitly
+ given.
+
+ It bears noting that if we switch sessions in a follow-up
+ transaction, SET TRANSACTION would be illegal in the old
+ session (as a transaction is active), whereas in the target
+ session which is being prepared, it should be legal, as no
+ transaction (chain) should have started yet.
+
+ Therefore, we are free to generate SET TRANSACTION as a replay
+ statement even for a transaction that isn't the first in an
+ ongoing chain. Consider
+
+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
+ START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
+ # work
+ COMMIT AND CHAIN;
+
+ If we switch away at this point, the replay in the new session
+ needs to be
+
+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
+ START TRANSACTION READ ONLY;
+
+ When a transaction ends (COMMIT/ROLLBACK sans CHAIN), all
+ per-transaction characteristics are reset to the session's
+ defaults.
+
+ This also holds for a transaction ended implicitly! (transaction.cc)
+ Once again, the aim is to have the tracker item reflect on a
+ given mysqld's actual behavior.
+ */
+
+ /*
+ "ISOLATION LEVEL"
+ Only legal in SET TRANSACTION, so will always be replayed as such.
+ */
+ if (tx_isol_level != TX_ISOL_INHERIT)
+ {
+ /*
+ Unfortunately, we can't re-use tx_isolation_names /
+ tx_isolation_typelib as it hyphenates its items.
+ */
+ buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL "));
+ buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length);
+ buf->append(STRING_WITH_LEN("; "));
+ }
+
+ /*
+ Start transaction will usually result in TX_EXPLICIT (transaction
+ started, but no data attached yet), except when WITH CONSISTENT
+ SNAPSHOT, in which case we may have data pending.
+ If it's an XA transaction, we don't go through here so we can
+ first print the trx access mode ("SET TRANSACTION READ ...")
+ separately before adding XA START (whereas with START TRANSACTION,
+ we can merge the access mode into the same statement).
+ */
+ if ((tx_curr_state & TX_EXPLICIT) && !is_xa)
+ {
+ buf->append(STRING_WITH_LEN("START TRANSACTION"));
+
+ /*
+ "WITH CONSISTENT SNAPSHOT"
+ Defaults to no, can only be enabled.
+ Only appears in START TRANSACTION.
+ */
+ if (tx_curr_state & TX_WITH_SNAPSHOT)
+ {
+ buf->append(STRING_WITH_LEN(" WITH CONSISTENT SNAPSHOT"));
+ if (tx_read_flags != TX_READ_INHERIT)
+ buf->append(STRING_WITH_LEN(","));
+ }
+
+ /*
+ "READ WRITE / READ ONLY" can be set globally, per-session,
+ or just for one transaction.
+
+ The latter case can take the form of
+ START TRANSACTION READ (WRITE|ONLY), or of
+ SET TRANSACTION READ (ONLY|WRITE).
+ (Both set thd->read_only for the upcoming transaction;
+ it will ultimately be re-set to the session default.)
+
+ As the regular session-variable tracker does not monitor the one-shot,
+ we'll have to do it here.
+
+ If READ is flagged as set explicitly (rather than just inherited
+ from the session's default), we'll get the actual bool from the THD.
+ */
+ if (tx_read_flags != TX_READ_INHERIT)
+ {
+ if (tx_read_flags == TX_READ_ONLY)
+ buf->append(STRING_WITH_LEN(" READ ONLY"));
+ else
+ buf->append(STRING_WITH_LEN(" READ WRITE"));
+ }
+ buf->append(STRING_WITH_LEN("; "));
+ }
+ else if (tx_read_flags != TX_READ_INHERIT)
+ {
+ /*
+ "READ ONLY" / "READ WRITE"
+ We could transform this to SET TRANSACTION even when it occurs
+ in START TRANSACTION, but for now, we'll resysynthesize the original
+ command as closely as possible.
+ */
+ buf->append(STRING_WITH_LEN("SET TRANSACTION "));
+ if (tx_read_flags == TX_READ_ONLY)
+ buf->append(STRING_WITH_LEN("READ ONLY; "));
+ else
+ buf->append(STRING_WITH_LEN("READ WRITE; "));
+ }
+
+ if ((tx_curr_state & TX_EXPLICIT) && is_xa)
+ {
+ XID *xid= &thd->transaction.xid_state.xid;
+ long glen, blen;
+
+ buf->append(STRING_WITH_LEN("XA START"));
+
+ if ((glen= xid->gtrid_length) > 0)
+ {
+ buf->append(STRING_WITH_LEN(" '"));
+ buf->append(xid->data, glen);
+
+ if ((blen= xid->bqual_length) > 0)
+ {
+ buf->append(STRING_WITH_LEN("','"));
+ buf->append(xid->data + glen, blen);
+ }
+ buf->append(STRING_WITH_LEN("'"));
+
+ if (xid->formatID != 1)
+ {
+ buf->append(STRING_WITH_LEN(","));
+ buf->append_ulonglong(xid->formatID);
+ }
+ }
+
+ buf->append(STRING_WITH_LEN("; "));
+ }
+
+ // discard trailing space
+ if (buf->length() > start)
+ buf->length(buf->length() - 1);
+ }
+
+ {
+ ulonglong length= buf->length() - start;
+ uchar *place= (uchar *)(buf->ptr() + (start - 2));
+ DBUG_ASSERT(length < 249); // in fact < 110
+ DBUG_ASSERT(start >= 3);
+
+ DBUG_ASSERT((place - 1)[0] == SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
+ /* Length of the overall entity. */
+ place[0]= length + 1;
+ /* Transaction characteristics (length-encoded string). */
+ place[1]= length;
+ }
+ }
+
+ reset();
+
+ return false;
+}
+
+
+/**
+ Reset the m_changed flag for next statement.
+*/
+
+void Transaction_state_tracker::reset()
+{
+ m_changed= false;
+ tx_reported_state= tx_curr_state;
+ tx_changed= TX_CHG_NONE;
+}
+
+
+/**
+ Helper function: turn table info into table access flag.
+ Accepts table lock type and engine type flag (transactional/
+ non-transactional), and returns the corresponding access flag
+ out of TX_READ_TRX, TX_READ_UNSAFE, TX_WRITE_TRX, TX_WRITE_UNSAFE.
+
+ @param thd [IN] The thd handle
+ @param set [IN] The table's access/lock type
+ @param set [IN] Whether the table's engine is transactional
+
+ @return The table access flag
+*/
+
+enum_tx_state Transaction_state_tracker::calc_trx_state(THD *thd,
+ thr_lock_type l,
+ bool has_trx)
+{
+ enum_tx_state s;
+ bool read= (l <= TL_READ_NO_INSERT);
+
+ if (read)
+ s= has_trx ? TX_READ_TRX : TX_READ_UNSAFE;
+ else
+ s= has_trx ? TX_WRITE_TRX : TX_WRITE_UNSAFE;
+
+ return s;
+}
+
+
+/**
+ Register the end of an (implicit or explicit) transaction.
+
+ @param thd [IN] The thd handle
+*/
+void Transaction_state_tracker::end_trx(THD *thd)
+{
+ DBUG_ASSERT(thd->variables.session_track_transaction_info > TX_TRACK_NONE);
+
+ if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
+ return;
+
+ if (tx_curr_state != TX_EMPTY)
+ {
+ if (tx_curr_state & TX_EXPLICIT)
+ tx_changed |= TX_CHG_CHISTICS;
+ tx_curr_state &= TX_LOCKED_TABLES;
+ }
+ update_change_flags(thd);
+}
+
+
+/**
+ Clear flags pertaining to the current statement or transaction.
+ May be called repeatedly within the same execution cycle.
+
+ @param thd [IN] The thd handle.
+ @param set [IN] The flags to clear
+*/
+
+void Transaction_state_tracker::clear_trx_state(THD *thd, uint clear)
+{
+ if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
+ return;
+
+ tx_curr_state &= ~clear;
+ update_change_flags(thd);
+}
+
+
+/**
+ Add flags pertaining to the current statement or transaction.
+ May be called repeatedly within the same execution cycle,
+ e.g. to add access info for more tables.
+
+ @param thd [IN] The thd handle.
+ @param set [IN] The flags to add
+*/
+
+void Transaction_state_tracker::add_trx_state(THD *thd, uint add)
+{
+ if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
+ return;
+
+ if (add == TX_EXPLICIT)
+ {
+ /* Always send characteristic item (if tracked), always replace state. */
+ tx_changed |= TX_CHG_CHISTICS;
+ tx_curr_state = TX_EXPLICIT;
+ }
+
+ /*
+ If we're not in an implicit or explicit transaction, but
+ autocommit==0 and tables are accessed, we flag "implicit transaction."
+ */
+ else if (!(tx_curr_state & (TX_EXPLICIT|TX_IMPLICIT)) &&
+ (thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) &&
+ (add &
+ (TX_READ_TRX | TX_READ_UNSAFE | TX_WRITE_TRX | TX_WRITE_UNSAFE)))
+ tx_curr_state |= TX_IMPLICIT;
+
+ /*
+ Only flag state when in transaction or LOCK TABLES is added.
+ */
+ if ((tx_curr_state & (TX_EXPLICIT | TX_IMPLICIT)) ||
+ (add & TX_LOCKED_TABLES))
+ tx_curr_state |= add;
+
+ update_change_flags(thd);
+}
+
+
+/**
+ Add "unsafe statement" flag if applicable.
+
+ @param thd [IN] The thd handle.
+ @param set [IN] The flags to add
+*/
+
+void Transaction_state_tracker::add_trx_state_from_thd(THD *thd)
+{
+ if (m_enabled)
+ {
+ if (thd->lex->is_stmt_unsafe())
+ add_trx_state(thd, TX_STMT_UNSAFE);
+ }
+}
+
+
+/**
+ Set read flags (read only/read write) pertaining to the next
+ transaction.
+
+ @param thd [IN] The thd handle.
+ @param set [IN] The flags to set
+*/
+
+void Transaction_state_tracker::set_read_flags(THD *thd,
+ enum enum_tx_read_flags flags)
+{
+ if (m_enabled && (tx_read_flags != flags))
+ {
+ tx_read_flags = flags;
+ tx_changed |= TX_CHG_CHISTICS;
+ mark_as_changed(thd, NULL);
+ }
+}
+
+
+/**
+ Set isolation level pertaining to the next transaction.
+
+ @param thd [IN] The thd handle.
+ @param set [IN] The isolation level to set
+*/
+
+void Transaction_state_tracker::set_isol_level(THD *thd,
+ enum enum_tx_isol_level level)
+{
+ if (m_enabled && (tx_isol_level != level))
+ {
+ tx_isol_level = level;
+ tx_changed |= TX_CHG_CHISTICS;
+ mark_as_changed(thd, NULL);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+Session_state_change_tracker::Session_state_change_tracker()
+{
+ m_changed= false;
+}
+
+/**
+ @Enable/disable the tracker based on @@session_track_state_change value.
+
+ @param thd [IN] The thd handle.
+ @return false (always)
+
+**/
+
+bool Session_state_change_tracker::update(THD *thd, set_var *)
+{
+ m_enabled= thd->variables.session_track_state_change;
+ return false;
+}
+
+/**
+ Store the '1' in the specified buffer when state is changed.
+
+ @param thd [IN] The thd handle.
+ @paran buf [INOUT] Buffer to store the information to.
+
+ @reval false Success
+ @retval true Error
+**/
+
+bool Session_state_change_tracker::store(THD *thd, String *buf)
+{
+ if (unlikely((1 + 1 + 1 + buf->length() >= MAX_PACKET_LENGTH) ||
+ buf->reserve(1 + 1 + 1, EXTRA_ALLOC)))
+ return true;
+
+ compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251);
+ /* Session state type (SESSION_TRACK_STATE_CHANGE) */
+ buf->q_append((char)SESSION_TRACK_STATE_CHANGE);
+
+ /* Length of the overall entity (1 byte) */
+ buf->q_append('\1');
+
+ DBUG_ASSERT(is_state_changed(thd));
+ buf->q_append('1');
+
+ reset();
+
+ return false;
+}
+
+
+/**
+ Reset the m_changed flag for next statement.
+*/
+
+void Session_state_change_tracker::reset()
+{
+ m_changed= false;
+}
+
+
+/**
+ Find if there is a session state change.
+*/
+
+bool Session_state_change_tracker::is_state_changed(THD *)
+{
+ return m_changed;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ @brief Initialize session tracker objects.
+*/
+
+Session_tracker::Session_tracker()
+{
+ /* track data ID fit into one byte in net coding */
+ compile_time_assert(SESSION_TRACK_always_at_the_end < 251);
+ /* one tracker could serv several tracking data */
+ compile_time_assert((uint)SESSION_TRACK_always_at_the_end >=
+ (uint)SESSION_TRACKER_END);
+
+ for (int i= 0; i < SESSION_TRACKER_END; i++)
+ m_trackers[i]= NULL;
+}
+
+
+/**
+ @brief Enables the tracker objects.
+
+ @param thd [IN] The thread handle.
+
+ @return void
+*/
+
+void Session_tracker::enable(THD *thd)
+{
+ /*
+ Originally and correctly this allocation was in the constructor and
+ deallocation in the destructor, but in this case memory counting
+ system works incorrectly (for example in INSERT DELAYED thread)
+ */
+ deinit();
+ m_trackers[SESSION_SYSVARS_TRACKER]=
+ new (std::nothrow) Session_sysvars_tracker();
+ m_trackers[CURRENT_SCHEMA_TRACKER]=
+ new (std::nothrow) Current_schema_tracker;
+ m_trackers[SESSION_STATE_CHANGE_TRACKER]=
+ new (std::nothrow) Session_state_change_tracker;
+ m_trackers[SESSION_GTIDS_TRACKER]=
+ new (std::nothrow) Not_implemented_tracker;
+ m_trackers[TRANSACTION_INFO_TRACKER]=
+ new (std::nothrow) Transaction_state_tracker;
+
+ for (int i= 0; i < SESSION_TRACKER_END; i++)
+ m_trackers[i]->enable(thd);
+}
+
+
+/**
+ Method called during the server startup to verify the contents
+ of @@session_track_system_variables.
+
+ @retval false Success
+ @retval true Failure
+*/
+
+bool Session_tracker::server_boot_verify(CHARSET_INFO *char_set)
+{
+ bool result;
+ LEX_STRING tmp;
+ tmp.str= global_system_variables.session_track_system_variables;
+ tmp.length= safe_strlen(tmp.str);
+ result=
+ Session_sysvars_tracker::server_init_check(NULL, char_set, tmp);
+ return result;
+}
+
+
+/**
+ @brief Store all change information in the specified buffer.
+
+ @param thd [IN] The thd handle.
+ @param buf [OUT] Reference to the string buffer to which the state
+ change data needs to be written.
+*/
+
+void Session_tracker::store(THD *thd, String *buf)
+{
+ size_t start;
+
+ /*
+ Probably most track result will fit in 251 byte so lets made it at
+ least efficient. We allocate 1 byte for length and then will move
+ string if there is more.
+ */
+ buf->append('\0');
+ start= buf->length();
+
+ /* Get total length. */
+ for (int i= 0; i < SESSION_TRACKER_END; i++)
+ {
+ if (m_trackers[i]->is_changed() &&
+ m_trackers[i]->store(thd, buf))
+ {
+ buf->length(start); // it is safer to have 0-length block in case of error
+ return;
+ }
+ }
+
+ size_t length= buf->length() - start;
+ uchar *data= (uchar *)(buf->ptr() + start);
+ uint size;
+
+ if ((size= net_length_size(length)) != 1)
+ {
+ if (buf->reserve(size - 1, EXTRA_ALLOC))
+ {
+ buf->length(start); // it is safer to have 0-length block in case of error
+ return;
+ }
+ memmove(data + (size - 1), data, length);
+ }
+
+ net_store_length(data - 1, length);
+}
+
+#endif //EMBEDDED_LIBRARY
diff --git a/sql/session_tracker.h b/sql/session_tracker.h
new file mode 100644
index 00000000000..3f73b5dc705
--- /dev/null
+++ b/sql/session_tracker.h
@@ -0,0 +1,304 @@
+#ifndef SESSION_TRACKER_INCLUDED
+#define SESSION_TRACKER_INCLUDED
+
+/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2016, MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "m_string.h"
+#include "thr_lock.h"
+
+#ifndef EMBEDDED_LIBRARY
+/* forward declarations */
+class THD;
+class set_var;
+class String;
+
+
+enum enum_session_tracker
+{
+ SESSION_SYSVARS_TRACKER, /* Session system variables */
+ CURRENT_SCHEMA_TRACKER, /* Current schema */
+ SESSION_STATE_CHANGE_TRACKER,
+ SESSION_GTIDS_TRACKER, /* Tracks GTIDs */
+ TRANSACTION_INFO_TRACKER, /* Transaction state */
+ SESSION_TRACKER_END /* must be the last */
+};
+
+/**
+ State_tracker
+
+ An abstract class that defines the interface for any of the server's
+ 'session state change tracker'. A tracker, however, is a sub- class of
+ this class which takes care of tracking the change in value of a part-
+ icular session state type and thus defines various methods listed in this
+ interface. The change information is later serialized and transmitted to
+ the client through protocol's OK packet.
+
+ Tracker system variables :-
+ A tracker is normally mapped to a system variable. So in order to enable,
+ disable or modify the sub-entities of a tracker, the user needs to modify
+ the respective system variable either through SET command or via command
+ line option. As required in system variable handling, this interface also
+ includes two functions to help in the verification of the supplied value
+ (ON_UPDATE) of the tracker system variable, namely - update().
+*/
+
+class State_tracker
+{
+protected:
+ /**
+ Is tracking enabled for a particular session state type ?
+
+ @note: it is a cache of the corresponding thd->variables.session_track_xxx
+ variable
+ */
+ bool m_enabled;
+
+ /** Has the session state type changed ? */
+ bool m_changed;
+
+public:
+ /** Constructor */
+ State_tracker() : m_enabled(false), m_changed(false)
+ {}
+
+ /** Destructor */
+ virtual ~State_tracker()
+ {}
+
+ /** Getters */
+ bool is_enabled() const
+ { return m_enabled; }
+
+ bool is_changed() const
+ { return m_changed; }
+
+ /** Called in the constructor of THD*/
+ virtual bool enable(THD *thd)= 0;
+
+ /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/
+ virtual bool update(THD *thd, set_var *var)= 0;
+
+ /** Store changed data into the given buffer. */
+ virtual bool store(THD *thd, String *buf)= 0;
+
+ /** Mark the entity as changed. */
+ virtual void mark_as_changed(THD *thd, LEX_CSTRING *name);
+};
+
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
+bool sysvartrack_reprint_value(THD *thd, char *str, size_t len);
+bool sysvartrack_update(THD *thd, set_var *var);
+size_t sysvartrack_value_len(THD *thd);
+bool sysvartrack_value_construct(THD *thd, char *val, size_t len);
+
+
+/**
+ Session_tracker
+
+ This class holds an object each for all tracker classes and provides
+ methods necessary for systematic detection and generation of session
+ state change information.
+*/
+
+class Session_tracker
+{
+private:
+ State_tracker *m_trackers[SESSION_TRACKER_END];
+
+ /* The following two functions are private to disable copying. */
+ Session_tracker(Session_tracker const &other)
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ Session_tracker& operator= (Session_tracker const &rhs)
+ {
+ DBUG_ASSERT(FALSE);
+ return *this;
+ }
+
+public:
+
+ Session_tracker();
+ ~Session_tracker()
+ {
+ deinit();
+ }
+
+ /* trick to make happy memory accounting system */
+ void deinit()
+ {
+ for (int i= 0; i < SESSION_TRACKER_END; i++)
+ {
+ if (m_trackers[i])
+ delete m_trackers[i];
+ m_trackers[i]= NULL;
+ }
+ }
+
+ void enable(THD *thd);
+ static bool server_boot_verify(CHARSET_INFO *char_set);
+
+ /** Returns the pointer to the tracker object for the specified tracker. */
+ inline State_tracker *get_tracker(enum_session_tracker tracker) const
+ {
+ return m_trackers[tracker];
+ }
+
+ inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
+ LEX_CSTRING *data)
+ {
+ if (m_trackers[tracker]->is_enabled())
+ m_trackers[tracker]->mark_as_changed(thd, data);
+ }
+
+
+ void store(THD *thd, String *main_buf);
+};
+
+
+/*
+ Transaction_state_tracker
+*/
+
+/**
+ Transaction state (no transaction, transaction active, work attached, etc.)
+*/
+enum enum_tx_state {
+ TX_EMPTY = 0, ///< "none of the below"
+ TX_EXPLICIT = 1, ///< an explicit transaction is active
+ TX_IMPLICIT = 2, ///< an implicit transaction is active
+ TX_READ_TRX = 4, ///< transactional reads were done
+ TX_READ_UNSAFE = 8, ///< non-transaction reads were done
+ TX_WRITE_TRX = 16, ///< transactional writes were done
+ TX_WRITE_UNSAFE = 32, ///< non-transactional writes were done
+ TX_STMT_UNSAFE = 64, ///< "unsafe" (non-deterministic like UUID()) stmts
+ TX_RESULT_SET = 128, ///< result set was sent
+ TX_WITH_SNAPSHOT= 256, ///< WITH CONSISTENT SNAPSHOT was used
+ TX_LOCKED_TABLES= 512 ///< LOCK TABLES is active
+};
+
+
+/**
+ Transaction access mode
+*/
+enum enum_tx_read_flags {
+ TX_READ_INHERIT = 0, ///< not explicitly set, inherit session.tx_read_only
+ TX_READ_ONLY = 1, ///< START TRANSACTION READ ONLY, or tx_read_only=1
+ TX_READ_WRITE = 2, ///< START TRANSACTION READ WRITE, or tx_read_only=0
+};
+
+
+/**
+ Transaction isolation level
+*/
+enum enum_tx_isol_level {
+ TX_ISOL_INHERIT = 0, ///< not explicitly set, inherit session.tx_isolation
+ TX_ISOL_UNCOMMITTED = 1,
+ TX_ISOL_COMMITTED = 2,
+ TX_ISOL_REPEATABLE = 3,
+ TX_ISOL_SERIALIZABLE= 4
+};
+
+
+/**
+ Transaction tracking level
+*/
+enum enum_session_track_transaction_info {
+ TX_TRACK_NONE = 0, ///< do not send tracker items on transaction info
+ TX_TRACK_STATE = 1, ///< track transaction status
+ TX_TRACK_CHISTICS = 2 ///< track status and characteristics
+};
+
+
+/**
+ This is a tracker class that enables & manages the tracking of
+ current transaction info for a particular connection.
+*/
+
+class Transaction_state_tracker : public State_tracker
+{
+private:
+ /** Helper function: turn table info into table access flag */
+ enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx);
+public:
+ /** Constructor */
+ Transaction_state_tracker();
+ bool enable(THD *thd)
+ { return update(thd, NULL); }
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+
+ /** Change transaction characteristics */
+ void set_read_flags(THD *thd, enum enum_tx_read_flags flags);
+ void set_isol_level(THD *thd, enum enum_tx_isol_level level);
+
+ /** Change transaction state */
+ void clear_trx_state(THD *thd, uint clear);
+ void add_trx_state(THD *thd, uint add);
+ void inline add_trx_state(THD *thd, thr_lock_type l, bool has_trx)
+ {
+ add_trx_state(thd, calc_trx_state(thd, l, has_trx));
+ }
+ void add_trx_state_from_thd(THD *thd);
+ void end_trx(THD *thd);
+
+
+private:
+ enum enum_tx_changed {
+ TX_CHG_NONE = 0, ///< no changes from previous stmt
+ TX_CHG_STATE = 1, ///< state has changed from previous stmt
+ TX_CHG_CHISTICS = 2 ///< characteristics have changed from previous stmt
+ };
+
+ /** any trackable changes caused by this statement? */
+ uint tx_changed;
+
+ /** transaction state */
+ uint tx_curr_state, tx_reported_state;
+
+ /** r/w or r/o set? session default? */
+ enum enum_tx_read_flags tx_read_flags;
+
+ /** isolation level */
+ enum enum_tx_isol_level tx_isol_level;
+
+ void reset();
+
+ inline void update_change_flags(THD *thd)
+ {
+ tx_changed &= ~TX_CHG_STATE;
+ tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0;
+ if (tx_changed != TX_CHG_NONE)
+ mark_as_changed(thd, NULL);
+ }
+};
+
+#define TRANSACT_TRACKER(X) \
+ do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \
+ {((Transaction_state_tracker *) \
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)) \
+ ->X; } } while(0)
+#define SESSION_TRACKER_CHANGED(A,B,C) \
+ thd->session_tracker.mark_as_changed(A,B,C)
+#else
+
+#define TRANSACT_TRACKER(X) do{}while(0)
+#define SESSION_TRACKER_CHANGED(A,B,C) do{}while(0)
+
+#endif //EMBEDDED_LIBRARY
+
+#endif /* SESSION_TRACKER_INCLUDED */
diff --git a/sql/set_var.cc b/sql/set_var.cc
index b178681e952..26eb5127a0b 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -115,6 +115,9 @@ void sys_var_end()
DBUG_VOID_RETURN;
}
+
+static bool static_test_load= TRUE;
+
/**
sys_var constructor
@@ -184,6 +187,8 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
else
chain->first= this;
chain->last= this;
+
+ test_load= &static_test_load;
}
bool sys_var::update(THD *thd, set_var *var)
@@ -204,8 +209,28 @@ bool sys_var::update(THD *thd, set_var *var)
(on_update && on_update(this, thd, OPT_GLOBAL));
}
else
- return session_update(thd, var) ||
+ {
+ bool ret= session_update(thd, var) ||
(on_update && on_update(this, thd, OPT_SESSION));
+
+ /*
+ Make sure we don't session-track variables that are not actually
+ part of the session. tx_isolation and and tx_read_only for example
+ exist as GLOBAL, SESSION, and one-shot ("for next transaction only").
+ */
+ if ((var->type == OPT_SESSION) && (!ret))
+ {
+ SESSION_TRACKER_CHANGED(thd, SESSION_SYSVARS_TRACKER,
+ (LEX_CSTRING*)var->var);
+ /*
+ Here MySQL sends variable name to avoid reporting change of
+ the tracker itself, but we decided that it is not needed
+ */
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ }
+
+ return ret;
+ }
}
uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base)
@@ -867,6 +892,8 @@ int set_var_user::update(THD *thd)
MYF(0));
return -1;
}
+
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
return 0;
}
@@ -914,7 +941,11 @@ int set_var_role::check(THD *thd)
int set_var_role::update(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- return acl_setrole(thd, role.str, access);
+ int res= acl_setrole(thd, role.str, access);
+ if (!res)
+ thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
+ NULL);
+ return res;
#else
return 0;
#endif
@@ -968,6 +999,33 @@ int set_var_collation_client::update(THD *thd)
{
thd->update_charset(character_set_client, collation_connection,
character_set_results);
+
+ /* Mark client collation variables as changed */
+#ifndef EMBEDDED_LIBRARY
+ if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
+ {
+ sys_var *svar;
+ mysql_mutex_lock(&LOCK_plugin);
+ if ((svar= find_sys_var_ex(thd, "character_set_client",
+ sizeof("character_set_client") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ if ((svar= find_sys_var_ex(thd, "character_set_results",
+ sizeof("character_set_results") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ if ((svar= find_sys_var_ex(thd, "character_set_connection",
+ sizeof("character_set_connection") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ mysql_mutex_unlock(&LOCK_plugin);
+ }
+ thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+#endif //EMBEDDED_LIBRARY
+
thd->protocol_text.init(thd);
thd->protocol_binary.init(thd);
return 0;
diff --git a/sql/set_var.h b/sql/set_var.h
index 060a4e1a57c..ba8027edc72 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -48,6 +48,7 @@ struct sys_var_chain
int mysql_add_sys_var_chain(sys_var *chain);
int mysql_del_sys_var_chain(sys_var *chain);
+
/**
A class representing one system variable - that is something
that can be accessed as @@global.variable_name or @@session.variable_name,
@@ -60,6 +61,7 @@ class sys_var: protected Value_source // for double_from_string_with_check
public:
sys_var *next;
LEX_CSTRING name;
+ bool *test_load;
enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023,
READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096,
NO_SET_STATEMENT=8192, AUTO_SET=16384};
@@ -240,6 +242,9 @@ protected:
uchar *global_var_ptr()
{ return ((uchar*)&global_system_variables) + offset; }
+
+ friend class Session_sysvars_tracker;
+ friend class Session_tracker;
};
#include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */
@@ -385,7 +390,7 @@ extern SHOW_COMP_OPTION have_openssl;
SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond);
-sys_var *find_sys_var(THD *thd, const char *str, uint length=0);
+sys_var *find_sys_var(THD *thd, const char *str, size_t length=0);
int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free);
#define SYSVAR_AUTOSIZE(VAR,VAL) \
diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml
index 9764d629625..912d196cc3c 100644
--- a/sql/share/charsets/Index.xml
+++ b/sql/share/charsets/Index.xml
@@ -67,6 +67,12 @@ To make maintaining easier please:
<collation name="latin2_hungarian_ci" id="21" order="Hungarian"/>
<collation name="latin2_croatian_ci" id="27" order="Croatian"/>
<collation name="latin2_bin" id="77" order="Binary" flag="binary"/>
+ <collation name="latin2_general_nopad_ci" id="1033" flag="nopad">
+ <rules>
+ <import source="latin2_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="latin2_nopad_bin" id="1101" flag="binary" flag="nopad"/>
</charset>
<charset name="dec8">
@@ -83,6 +89,12 @@ To make maintaining easier please:
<order>Portuguese</order>
<order>Spanish</order>
</collation>
+ <collation name="dec8_swedish_nopad_ci" id="1027" flag="nopad">
+ <rules>
+ <import source="dec8_swedish_ci"/>
+ </rules>
+ </collation>
+ <collation name="dec8_nopad_bin" id="1093" flag="binary" flag="nopad"/>
</charset>
<charset name="cp850">
@@ -102,6 +114,12 @@ To make maintaining easier please:
<order>Spanish</order>
</collation>
<collation name="cp850_bin" id="80" order="Binary" flag="binary"/>
+ <collation name="cp850_general_nopad_ci" id="1028" flag="nopad">
+ <rules>
+ <import source="cp850_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp850_nopad_bin" id="1104" flag="binary" flag="nopad"/>
</charset>
<charset name="latin1">
@@ -163,6 +181,12 @@ To make maintaining easier please:
<order>Portuguese</order>
<order>Spanish</order>
</collation>
+ <collation name="hp8_english_nopad_ci" id="1030" flag="nopad">
+ <rules>
+ <import source="hp8_english_ci"/>
+ </rules>
+ </collation>
+ <collation name="hp8_nopad_bin" id="1096" flag="binary" flag="nopad"/>
</charset>
<charset name="koi8r">
@@ -172,6 +196,12 @@ To make maintaining easier please:
<alias>cskoi8r</alias>
<collation name="koi8r_general_ci" id="7" order="Russian" flag="primary"/>
<collation name="koi8r_bin" id="74" order="Binary" flag="binary"/>
+ <collation name="koi8r_general_nopad_ci" id="1031" flag="nopad">
+ <rules>
+ <import source="koi8r_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="koi8r_nopad_bin" id="1098" flag="binary" flag="nopad"/>
</charset>
<charset name="swe7">
@@ -180,6 +210,12 @@ To make maintaining easier please:
<alias>iso-646-se</alias>
<collation name="swe7_swedish_ci" id="10" order="Swedish" flag="primary"/>
<collation name="swe7_bin" id="82" order="Binary" flag="binary"/>
+ <collation name="swe7_swedish_nopad_ci" id="1034" flag="nopad">
+ <rules>
+ <import source="swe7_swedish_ci"/>
+ </rules>
+ </collation>
+ <collation name="swe7_nopad_bin" id="1106" flag="binary" flag="nopad"/>
</charset>
<charset name="ascii">
@@ -192,6 +228,12 @@ To make maintaining easier please:
<alias>iso646-us</alias>
<collation name="ascii_general_ci" id="11" order="English" flag="primary"/>
<collation name="ascii_bin" id="65" order="Binary" flag="binary"/>
+ <collation name="ascii_general_nopad_ci" id="1035" flag="nopad">
+ <rules>
+ <import source="ascii_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="ascii_nopad_bin" id="1089" flag="binary" flag="nopad"/>
</charset>
<charset name="ujis">
@@ -259,6 +301,12 @@ To make maintaining easier please:
<order>Mongolian</order>
<order>Ukrainian</order>
</collation>
+ <collation name="cp1251_general_nopad_ci" id="1075" flag="nopad">
+ <rules>
+ <import source="cp1251_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp1251_nopad_bin" id="1074" flag="binary" flag="nopad"/>
</charset>
<charset name="hebrew">
@@ -269,6 +317,12 @@ To make maintaining easier please:
<alias>iso-ir-138</alias>
<collation name="hebrew_general_ci" id="16" order="Hebrew" flag="primary"/>
<collation name="hebrew_bin" id="71" order="Binary" flag="binary"/>
+ <collation name="hebrew_general_nopad_ci" id="1040" flag="nopad">
+ <rules>
+ <import source="hebrew_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="hebrew_nopad_bin" id="1095" flag="binary" flag="nopad"/>
</charset>
<charset name="tis620">
@@ -319,6 +373,12 @@ To make maintaining easier please:
<order>Lithuanian</order>
</collation>
<collation name="latin7_bin" id="79" order="Binary" flag="binary"/>
+ <collation name="latin7_general_nopad_ci" id="1065" flag="nopad">
+ <rules>
+ <import source="latin7_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="latin7_nopad_bin" id="1103" flag="binary" flag="nopad"/>
</charset>
<charset name="koi8u">
@@ -327,6 +387,12 @@ To make maintaining easier please:
<alias>koi8-u</alias>
<collation name="koi8u_general_ci" id="22" order="Ukranian" flag="primary"/>
<collation name="koi8u_bin" id="75" order="Binary" flag="binary"/>
+ <collation name="koi8u_general_nopad_ci" id="1046" flag="nopad">
+ <rules>
+ <import source="koi8u_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="koi8u_nopad_bin" id="1099" flag="binary" flag="nopad"/>
</charset>
<charset name="gb2312">
@@ -354,6 +420,12 @@ To make maintaining easier please:
<alias>iso-ir-126</alias>
<collation name="greek_general_ci" id="25" order="Greek" flag="primary"/>
<collation name="greek_bin" id="70" order="Binary" flag="binary"/>
+ <collation name="greek_general_nopad_ci" id="1049" flag="nopad">
+ <rules>
+ <import source="greek_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="greek_nopad_bin" id="1094" flag="binary" flag="nopad"/>
</charset>
<charset name="cp1250">
@@ -380,6 +452,12 @@ To make maintaining easier please:
<flag>compiled</flag>
</collation>
<collation name="cp1250_bin" id="66" order="Binary" flag="binary"/>
+ <collation name="cp1250_general_nopad_ci" id="1050" flag="nopad">
+ <rules>
+ <import source="cp1250_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp1250_nopad_bin" id="1090" flag="binary" flag="nopad"/>
</charset>
<charset name="gbk">
@@ -407,6 +485,12 @@ To make maintaining easier please:
<order>Latvian</order>
<order>Lithuanian</order>
</collation>
+ <collation name="cp1257_general_nopad_ci" id="1083" flag="nopad">
+ <rules>
+ <import source="cp1257_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp1257_nopad_bin" id="1082" flag="binary" flag="nopad"/>
<!--collation name="cp1257_ci" id="60"/-->
<!--collation name="cp1257_cs" id="61"/-->
</charset>
@@ -422,6 +506,12 @@ To make maintaining easier please:
<alias>turkish</alias>
<collation name="latin5_turkish_ci" id="30" order="Turkish" flag="primary"/>
<collation name="latin5_bin" id="78" order="Binary" flag="binary"/>
+ <collation name="latin5_turkish_nopad_ci" id="1054" flag="nopad">
+ <rules>
+ <import source="latin5_turkish_ci"/>
+ </rules>
+ </collation>
+ <collation name="latin5_nopad_bin" id="1102" flag="binary" flag="nopad"/>
</charset>
<charset name="armscii8">
@@ -430,6 +520,12 @@ To make maintaining easier please:
<alias>armscii-8</alias>
<collation name="armscii8_general_ci" id="32" order="Armenian" flag="primary"/>
<collation name="armscii8_bin" id="64" order="Binary" flag="binary"/>
+ <collation name="armscii8_general_nopad_ci" id="1056" flag="nopad">
+ <rules>
+ <import source="armscii8_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="armscii8_nopad_bin" id="1088" flag="binary" flag="nopad"/>
</charset>
<charset name="utf8">
@@ -468,6 +564,12 @@ To make maintaining easier please:
<alias>DOSCyrillicRussian</alias>
<collation name="cp866_general_ci" id="36" order="Russian" flag="primary"/>
<collation name="cp866_bin" id="68" order="Binary" flag="binary"/>
+ <collation name="cp866_general_nopad_ci" id="1060" flag="nopad">
+ <rules>
+ <import source="cp866_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp866_nopad_bin" id="1092" flag="binary" flag="nopad"/>
</charset>
<charset name="keybcs2">
@@ -475,6 +577,12 @@ To make maintaining easier please:
<description>DOS Kamenicky Czech-Slovak</description>
<collation name="keybcs2_general_ci" id="37" order="Czech" flag="primary"/>
<collation name="keybcs2_bin" id="73" order="Binary" flag="binary"/>
+ <collation name="keybcs2_general_nopad_ci" id="1061" flag="nopad">
+ <rules>
+ <import source="keybcs2_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="keybcs2_nopad_bin" id="1097" flag="binary" flag="nopad"/>
</charset>
<charset name="macce">
@@ -491,6 +599,12 @@ To make maintaining easier please:
<order>Sorbian</order>
</collation>
<collation name="macce_bin" id="43" order="Binary" flag="binary"/>
+ <collation name="macce_general_nopad_ci" id="1062" flag="nopad">
+ <rules>
+ <import source="macce_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="macce_nopad_bin" id="1067" flag="binary" flag="nopad"/>
</charset>
<charset name="macroman">
@@ -513,6 +627,12 @@ To make maintaining easier please:
<!--collation name="macroman_ci" id="54"/-->
<!--collation name="macroman_ci_ai" id="55"/-->
<!--collation name="macroman_cs" id="56"/-->
+ <collation name="macroman_general_nopad_ci" id="1063" flag="nopad">
+ <rules>
+ <import source="macroman_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="macroman_nopad_bin" id="1077" flag="binary" flag="nopad"/>
</charset>
<charset name="cp852">
@@ -531,6 +651,12 @@ To make maintaining easier please:
<order>Sorbian</order>
</collation>
<collation name="cp852_bin" id="81" order="Binary" flag="binary"/>
+ <collation name="cp852_general_nopad_ci" id="1064" flag="nopad">
+ <rules>
+ <import source="cp852_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp852_nopad_bin" id="1105" flag="binary" flag="nopad"/>
</charset>
<charset name="cp1256">
@@ -545,6 +671,12 @@ To make maintaining easier please:
<order>Pakistani</order>
<order>Urdu</order>
</collation>
+ <collation name="cp1256_general_nopad_ci" id="1081" flag="nopad">
+ <rules>
+ <import source="cp1256_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="cp1256_nopad_bin" id="1091" flag="binary" flag="nopad"/>
</charset>
<charset name="geostd8">
@@ -552,6 +684,12 @@ To make maintaining easier please:
<description>GEOSTD8 Georgian</description>
<collation name="geostd8_general_ci" id="92" order="Georgian" flag="primary"/>
<collation name="geostd8_bin" id="93" order="Binary" flag="binary"/>
+ <collation name="geostd8_general_nopad_ci" id="1116" flag="nopad">
+ <rules>
+ <import source="geostd8_general_ci"/>
+ </rules>
+ </collation>
+ <collation name="geostd8_nopad_bin" id="1117" flag="binary" flag="nopad"/>
</charset>
<charset name="binary">
@@ -596,4 +734,3 @@ To make maintaining easier please:
</charset>
</charsets>
-
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index fe6290faa94..4d3861b2936 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -4064,21 +4064,21 @@ ER_LOCK_OR_ACTIVE_TRANSACTION
swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
ukr "Не можу виконати подану команду тому, що таблиця заблокована або виконується транзакція"
ER_UNKNOWN_SYSTEM_VARIABLE
- cze "Neznámá systémová proměnná '%-.64s'"
- dan "Ukendt systemvariabel '%-.64s'"
- nla "Onbekende systeem variabele '%-.64s'"
- eng "Unknown system variable '%-.64s'"
- est "Tundmatu süsteemne muutuja '%-.64s'"
- fre "Variable système '%-.64s' inconnue"
- ger "Unbekannte Systemvariable '%-.64s'"
- ita "Variabile di sistema '%-.64s' sconosciuta"
- jpn "'%-.64s' は不明なシステム変数です。"
- por "Variável de sistema '%-.64s' desconhecida"
- rus "Неизвестная системная переменная '%-.64s'"
- serbian "Nepoznata sistemska promenljiva '%-.64s'"
- spa "Desconocida variable de sistema '%-.64s'"
- swe "Okänd systemvariabel: '%-.64s'"
- ukr "Невідома системна змінна '%-.64s'"
+ cze "Neznámá systémová proměnná '%-.*s'"
+ dan "Ukendt systemvariabel '%-.*s'"
+ nla "Onbekende systeem variabele '%-.*s'"
+ eng "Unknown system variable '%-.*s'"
+ est "Tundmatu süsteemne muutuja '%-.*s'"
+ fre "Variable système '%-.*s' inconnue"
+ ger "Unbekannte Systemvariable '%-.*s'"
+ ita "Variabile di sistema '%-.*s' sconosciuta"
+ jpn "'%-.*s' は不明なシステム変数です。"
+ por "Variável de sistema '%-.*s' desconhecida"
+ rus "Неизвестная системная переменная '%-.*s'"
+ serbian "Nepoznata sistemska promenljiva '%-.*s'"
+ spa "Desconocida variable de sistema '%-.*s'"
+ swe "Okänd systemvariabel: '%-.*s'"
+ ukr "Невідома системна змінна '%-.*s'"
ER_CRASHED_ON_USAGE
cze "Tabulka '%-.192s' je označena jako porušená a měla by být opravena"
dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"
@@ -6762,8 +6762,8 @@ ER_FK_CANNOT_OPEN_PARENT
ER_FK_INCORRECT_OPTION
eng "Failed to add the foreign key constraint on table '%s'. Incorrect options in FOREIGN KEY constraint '%s'"
-ER_FK_DUP_NAME
- eng "Duplicate foreign key constraint name '%s'"
+ER_DUP_CONSTRAINT_NAME
+ eng "Duplicate %s constraint name '%s'"
ER_PASSWORD_FORMAT
eng "The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function."
@@ -7139,6 +7139,9 @@ ER_KILL_QUERY_DENIED_ERROR
ER_NO_EIS_FOR_FIELD
eng "Engine-independent statistics are not collected for column '%s'"
ukr "Незалежна від типу таблиці статистика не збирається для стовбця '%s'"
+ER_WARN_AGGFUNC_DEPENDENCE
+ eng "Aggregate function '%-.192s)' of SELECT #%d belongs to SELECT #%d"
+ ukr "Агрегатна функція '%-.192s)' з SELECTу #%d належить до SELECTу #%d"
#
# Internal errors, not used
@@ -7167,14 +7170,20 @@ ER_BAD_COMMAND_IN_MULTI
ukr "Команда '%s' не дозволена для COM_MULTI"
ER_WITH_COL_WRONG_LIST
eng "WITH column list and SELECT field list have different column counts"
+ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE
+ eng "Too many WITH elements in WITH clause"
ER_DUP_QUERY_NAME
eng "Duplicate query name in WITH clause"
-ER_WRONG_ORDER_IN_WITH_CLAUSE
- eng "The definition of the table '%s' refers to the table '%s' defined later in a non-recursive WITH clause"
-ER_RECURSIVE_QUERY_IN_WITH_CLAUSE
- eng "Recursive queries in WITH clause are not supported yet"
+ER_RECURSIVE_WITHOUT_ANCHORS
+ eng "No anchors for recursive WITH element '%s'"
+ER_UNACCEPTABLE_MUTUAL_RECURSION
+ eng "Unacceptable mutual recursion with anchored table '%s'"
+ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED
+ eng "Reference to recursive WITH table '%s' in materialized derived"
+ER_NOT_STANDARDS_COMPLIANT_RECURSIVE
+ eng "Restrictions imposed on recursive definitions are violated for table '%s'"R_WRONG_WINDOW_SPEC_NAME
ER_WRONG_WINDOW_SPEC_NAME
- eng "Window specification with name '%s' is not defined"
+ eng "Window specification with name '%s' is not defined"
ER_DUP_WINDOW_NAME
eng "Multiple window specifications with the same name '%s'"
ER_PARTITION_LIST_IN_REFERENCING_WINDOW_SPEC
@@ -7218,3 +7227,6 @@ ER_CALCULATING_DEFAULT_VALUE
eng "Got an error when calculating default value for %`s"
ER_EXPRESSION_REFERS_TO_UNINIT_FIELD 01000
eng "Expression for field %`-.64s is refering to uninitialized field %`s"
+ER_PARTITION_DEFAULT_ERROR
+ eng "Only one DEFAULT partition allowed"
+ ukr "Припустимо мати тільки один DEFAULT розділ"
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index bbe714fc5b4..f9478134ab4 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -65,6 +65,12 @@ extern "C" sig_handler handle_fatal_signal(int sig)
#ifdef HAVE_STACKTRACE
THD *thd;
#endif
+ /*
+ This flag remembers if the query pointer was found invalid.
+ We will try and print the query at the end of the signal handler, in case
+ we're wrong.
+ */
+ bool print_invalid_query_pointer= false;
if (segfaulted)
{
@@ -107,6 +113,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"diagnose the problem, but since we have already crashed, \n"
"something is definitely wrong and this may fail.\n\n");
+ set_server_version(server_version, sizeof(server_version));
my_safe_printf_stderr("Server version: %s\n", server_version);
if (dflt_key_cache)
@@ -200,7 +207,12 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"Some pointers may be invalid and cause the dump to abort.\n");
my_safe_printf_stderr("Query (%p): ", thd->query());
- my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length()));
+ if (my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length())))
+ {
+ // Query was found invalid. We will try to print it at the end.
+ print_invalid_query_pointer= true;
+ }
+
my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n",
(ulong) thd->thread_id);
my_safe_printf_stderr("Status: %s\n\n", kreason);
@@ -264,6 +276,16 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"\"mlockall\" bugs.\n");
}
+ if (print_invalid_query_pointer)
+ {
+ my_safe_printf_stderr(
+ "\nWe think the query pointer is invalid, but we will try "
+ "to print it anyway. \n"
+ "Query: ");
+ my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length()));
+ my_safe_printf_stderr("\n\n");
+ }
+
#ifdef HAVE_WRITE_CORE
if (test_flags & TEST_CORE_ON_SIGNAL)
{
diff --git a/sql/slave.cc b/sql/slave.cc
index d83bafa5b3d..08cbf9acb6a 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -283,18 +283,27 @@ static void init_slave_psi_keys(void)
#endif /* HAVE_PSI_INTERFACE */
-static bool slave_init_thread_running;
+static bool slave_background_thread_running;
+static bool slave_background_thread_stop;
+static bool slave_background_thread_gtid_loaded;
+
+struct slave_background_kill_t {
+ slave_background_kill_t *next;
+ THD *to_kill;
+} *slave_background_kill_list;
pthread_handler_t
-handle_slave_init(void *arg __attribute__((unused)))
+handle_slave_background(void *arg __attribute__((unused)))
{
THD *thd;
+ PSI_stage_info old_stage;
+ bool stop;
my_thread_init();
thd= new THD(next_thread_id());
thd->thread_stack= (char*) &thd; /* Set approximate stack start */
- thd->system_thread = SYSTEM_THREAD_SLAVE_INIT;
+ thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND;
thread_safe_increment32(&service_thread_count);
thd->store_globals();
thd->security_ctx->skip_grants();
@@ -307,49 +316,137 @@ handle_slave_init(void *arg __attribute__((unused)))
rpl_gtid_slave_state_table_name.str,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
- delete thd;
- thread_safe_decrement32(&service_thread_count);
- /* Signal run_slave_init_thread() that we are done */
+ mysql_mutex_lock(&LOCK_slave_background);
+ slave_background_thread_gtid_loaded= true;
+ mysql_cond_broadcast(&COND_slave_background);
- mysql_mutex_lock(&LOCK_start_thread);
- slave_init_thread_running= false;
- mysql_cond_broadcast(&COND_start_thread);
- mysql_mutex_unlock(&LOCK_start_thread);
+ THD_STAGE_INFO(thd, stage_slave_background_process_request);
+ do
+ {
+ slave_background_kill_t *kill_list;
+
+ thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background,
+ &stage_slave_background_wait_request,
+ &old_stage);
+ for (;;)
+ {
+ stop= abort_loop || thd->killed || slave_background_thread_stop;
+ kill_list= slave_background_kill_list;
+ if (stop || kill_list)
+ break;
+ mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
+ }
+
+ slave_background_kill_list= NULL;
+ thd->EXIT_COND(&old_stage);
+
+ while (kill_list)
+ {
+ slave_background_kill_t *p = kill_list;
+ THD *to_kill= p->to_kill;
+ kill_list= p->next;
+
+ mysql_mutex_lock(&to_kill->LOCK_thd_data);
+ to_kill->awake(KILL_CONNECTION);
+ mysql_mutex_unlock(&to_kill->LOCK_thd_data);
+ mysql_mutex_lock(&to_kill->LOCK_wakeup_ready);
+ to_kill->rgi_slave->killed_for_retry=
+ rpl_group_info::RETRY_KILL_KILLED;
+ mysql_cond_broadcast(&to_kill->COND_wakeup_ready);
+ mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready);
+ my_free(p);
+ }
+ mysql_mutex_lock(&LOCK_slave_background);
+ } while (!stop);
+
+ slave_background_thread_running= false;
+ mysql_cond_broadcast(&COND_slave_background);
+ mysql_mutex_unlock(&LOCK_slave_background);
+
+ delete thd;
+ thread_safe_decrement32(&service_thread_count);
+ signal_thd_deleted();
my_thread_end();
return 0;
}
+
+void
+slave_background_kill_request(THD *to_kill)
+{
+ if (to_kill->rgi_slave->killed_for_retry)
+ return; // Already deadlock killed.
+ slave_background_kill_t *p=
+ (slave_background_kill_t *)my_malloc(sizeof(*p), MYF(MY_WME));
+ if (p)
+ {
+ p->to_kill= to_kill;
+ to_kill->rgi_slave->killed_for_retry=
+ rpl_group_info::RETRY_KILL_PENDING;
+ mysql_mutex_lock(&LOCK_slave_background);
+ p->next= slave_background_kill_list;
+ slave_background_kill_list= p;
+ mysql_cond_signal(&COND_slave_background);
+ mysql_mutex_unlock(&LOCK_slave_background);
+ }
+}
+
+
/*
- Start the slave init thread.
+ Start the slave background thread.
- This thread is used to load the GTID state from mysql.gtid_slave_pos at
- server start; reading from table requires valid THD, which is otherwise not
- available during server init.
+ This thread is currently used for two purposes:
+
+ 1. To load the GTID state from mysql.gtid_slave_pos at server start; reading
+ from table requires valid THD, which is otherwise not available during
+ server init.
+
+ 2. To kill worker thread transactions during parallel replication, when a
+ storage engine attempts to take an errorneous conflicting lock that would
+ cause a deadlock. Killing is done asynchroneously, as the kill may not
+ be safe within the context of a callback from inside storage engine
+ locking code.
*/
static int
-run_slave_init_thread()
+start_slave_background_thread()
{
pthread_t th;
- slave_init_thread_running= true;
- if (mysql_thread_create(key_thread_slave_init, &th, &connection_attrib,
- handle_slave_init, NULL))
+ slave_background_thread_running= true;
+ slave_background_thread_stop= false;
+ slave_background_thread_gtid_loaded= false;
+ if (mysql_thread_create(key_thread_slave_background,
+ &th, &connection_attrib, handle_slave_background,
+ NULL))
{
sql_print_error("Failed to create thread while initialising slave");
return 1;
}
- mysql_mutex_lock(&LOCK_start_thread);
- while (slave_init_thread_running)
- mysql_cond_wait(&COND_start_thread, &LOCK_start_thread);
- mysql_mutex_unlock(&LOCK_start_thread);
+ mysql_mutex_lock(&LOCK_slave_background);
+ while (!slave_background_thread_gtid_loaded)
+ mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
+ mysql_mutex_unlock(&LOCK_slave_background);
+
return 0;
}
+static void
+stop_slave_background_thread()
+{
+ mysql_mutex_lock(&LOCK_slave_background);
+ slave_background_thread_stop= true;
+ mysql_cond_broadcast(&COND_slave_background);
+ while (slave_background_thread_running)
+ mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
+ mysql_mutex_unlock(&LOCK_slave_background);
+}
+
+
/* Initialize slave structures */
int init_slave()
@@ -361,7 +458,7 @@ int init_slave()
init_slave_psi_keys();
#endif
- if (run_slave_init_thread())
+ if (start_slave_background_thread())
return 1;
if (global_rpl_thread_pool.init(opt_slave_parallel_threads))
@@ -647,6 +744,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
mysql_mutex_unlock(log_lock);
}
if (opt_slave_parallel_threads > 0 &&
+ master_info_index &&// master_info_index is set to NULL on server shutdown
!master_info_index->any_slave_sql_running())
rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL))
@@ -1000,6 +1098,9 @@ void end_slave()
master_info_index= 0;
active_mi= 0;
mysql_mutex_unlock(&LOCK_active_mi);
+
+ stop_slave_background_thread();
+
global_rpl_thread_pool.destroy();
free_all_rpl_filters();
DBUG_VOID_RETURN;
@@ -4111,6 +4212,7 @@ connected:
}
DBUG_PRINT("info",("Starting reading binary log from master"));
+ thd->set_command(COM_SLAVE_IO);
while (!io_slave_killed(mi))
{
THD_STAGE_INFO(thd, stage_requesting_binlog_dump);
@@ -4604,11 +4706,11 @@ pthread_handler_t handle_slave_sql(void *arg)
{
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
"Error initializing relay log position: %s", errmsg);
- goto err;
+ goto err_before_start;
}
rli->reset_inuse_relaylog();
if (rli->alloc_inuse_relaylog(rli->group_relay_log_name))
- goto err;
+ goto err_before_start;
strcpy(rli->future_event_master_log_name, rli->group_master_log_name);
THD_CHECK_SENTRY(thd);
@@ -4733,6 +4835,7 @@ pthread_handler_t handle_slave_sql(void *arg)
/* Read queries from the IO/THREAD until this thread is killed */
+ thd->set_command(COM_SLAVE_SQL);
while (!sql_slave_killed(serial_rgi))
{
THD_STAGE_INFO(thd, stage_reading_event_from_the_relay_log);
@@ -4785,6 +4888,7 @@ pthread_handler_t handle_slave_sql(void *arg)
}
}
+ err:
if (mi->using_parallel())
rli->parallel.wait_for_done(thd, rli);
@@ -4802,15 +4906,7 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->group_master_log_pos, tmp.c_ptr_safe());
}
- err:
-
- /*
- Once again, in case we aborted with an error and skipped the first one.
- (We want the first one to be before the printout of stop position to
- get the correct position printed.)
- */
- if (mi->using_parallel())
- rli->parallel.wait_for_done(thd, rli);
+ err_before_start:
/*
Some events set some playgrounds, which won't be cleared because thread
@@ -6210,9 +6306,9 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
#ifndef DBUG_OFF
mi->events_till_disconnect = disconnect_slave_event_count;
#endif
- ulong client_flag= 0;
+ ulong client_flag= CLIENT_REMEMBER_OPTIONS;
if (opt_slave_compressed_protocol)
- client_flag=CLIENT_COMPRESS; /* We will use compression */
+ client_flag|= CLIENT_COMPRESS; /* We will use compression */
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
diff --git a/sql/slave.h b/sql/slave.h
index 7f412c1091c..e8a925ce560 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -250,6 +250,7 @@ pthread_handler_t handle_slave_io(void *arg);
void slave_output_error_info(rpl_group_info *rgi, THD *thd);
pthread_handler_t handle_slave_sql(void *arg);
bool net_request_file(NET* net, const char* fname);
+void slave_background_kill_request(THD *to_kill);
extern bool volatile abort_loop;
extern Master_info *active_mi; /* active_mi for multi-master */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 6b048cec68b..41006f07a0a 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2048,6 +2048,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
break;
}
}
+
+ TRANSACT_TRACKER(add_trx_state_from_thd(thd));
}
/*
@@ -2977,6 +2979,18 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
reinit_stmt_before_use(thd, m_lex);
+#ifndef EMBEDDED_LIBRARY
+ /*
+ If there was instruction which changed tracking state,
+ the result of changed tracking state send to client in OK packed.
+ So it changes result sent to client and probably can be different
+ independent on query text. So we can't cache such results.
+ */
+ if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
+ (thd->server_status & SERVER_SESSION_STATE_CHANGED))
+ thd->lex->safe_to_cache_query= 0;
+#endif
+
if (open_tables)
res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
@@ -3053,6 +3067,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
what is needed from the substatement gained
*/
thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table;
+
+ TRANSACT_TRACKER(add_trx_state_from_thd(thd));
+
/*
Unlike for PS we should not call Item's destructors for newly created
items after execution of each instruction in stored routine. This is
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 37e6e769a89..f0f96d340cc 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -31,7 +31,7 @@
#include "sql_base.h" // close_mysql_tables
#include "key.h" // key_copy, key_cmp_if_same, key_restore
#include "sql_show.h" // append_identifier
-#include "sql_table.h" // build_table_filename
+#include "sql_table.h" // write_bin_log
#include "hash_filo.h"
#include "sql_parse.h" // check_access
#include "sql_view.h" // VIEW_ANY_ACL
@@ -2009,8 +2009,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= acl_role->access;
if (acl_role->user.str)
- strmake_buf(sctx->priv_user, user);
- sctx->priv_host[0]= 0;
+ strmake_buf(sctx->priv_role, user);
}
}
@@ -2796,7 +2795,7 @@ bool change_password(THD *thd, LEX_USER *user)
if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
{
- thd->set_query_inner(buff, query_length, system_charset_info);
+ thd->set_query(buff, query_length, system_charset_info);
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
}
@@ -2857,7 +2856,7 @@ error: // this label is used in WSREP_TO_ISOLATION_BEGIN
{
WSREP_TO_ISOLATION_END;
- thd->set_query_inner(query_save);
+ thd->set_query(query_save);
thd->wsrep_exec_mode = LOCAL_STATE;
}
#endif /* WITH_WSREP */
@@ -2921,7 +2920,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
{
- thd->set_query_inner(buff, query_length, system_charset_info);
+ thd->set_query(buff, query_length, system_charset_info);
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
}
@@ -3008,7 +3007,7 @@ error: // this label is used in WSREP_TO_ISOLATION_END
{
WSREP_TO_ISOLATION_END;
- thd->set_query_inner(query_save);
+ thd->set_query(query_save);
thd->wsrep_exec_mode = LOCAL_STATE;
}
#endif /* WITH_WSREP */
@@ -7162,7 +7161,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
GRANT_INFO *grant;
const char *db_name;
const char *table_name;
- Security_context *sctx= MY_TEST(table_ref->security_ctx) ?
+ Security_context *sctx= table_ref->security_ctx ?
table_ref->security_ctx : thd->security_ctx;
if (table_ref->view || table_ref->field_translation)
@@ -11078,7 +11077,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
/* global privileges */
grant->privilege= sctx->master_access;
- if (!sctx->priv_user[0])
+ if (!sctx->priv_user[0] && !sctx->priv_role[0])
{
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN; // it is slave
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 578b78d8f35..ed23d9cccd6 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -296,6 +296,97 @@ static inline bool table_not_corrupt_error(uint sql_errno)
}
+static bool open_only_one_table(THD* thd, TABLE_LIST* table,
+ bool repair_table_use_frm,
+ bool is_view_operator_func)
+{
+ LEX *lex= thd->lex;
+ SELECT_LEX *select= &lex->select_lex;
+ TABLE_LIST *save_next_global, *save_next_local;
+ bool open_error;
+ save_next_global= table->next_global;
+ table->next_global= 0;
+ save_next_local= table->next_local;
+ table->next_local= 0;
+ select->table_list.first= table;
+ /*
+ Time zone tables and SP tables can be add to lex->query_tables list,
+ so it have to be prepared.
+ TODO: Investigate if we can put extra tables into argument instead of
+ using lex->query_tables
+ */
+ lex->query_tables= table;
+ lex->query_tables_last= &table->next_global;
+ lex->query_tables_own_last= 0;
+
+ /*
+ CHECK TABLE command is allowed for views as well. Check on alter flags
+ to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
+ allowed.
+ */
+ if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
+ !is_view_operator_func)
+ {
+ table->required_type=FRMTYPE_TABLE;
+ DBUG_ASSERT(!lex->only_view);
+ }
+ else if (lex->only_view)
+ {
+ table->required_type= FRMTYPE_VIEW;
+ }
+ else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR)
+ {
+ table->required_type= FRMTYPE_TABLE;
+ }
+
+ if (lex->sql_command == SQLCOM_CHECK ||
+ lex->sql_command == SQLCOM_REPAIR ||
+ lex->sql_command == SQLCOM_ANALYZE ||
+ lex->sql_command == SQLCOM_OPTIMIZE)
+ thd->prepare_derived_at_open= TRUE;
+ if (!thd->locked_tables_mode && repair_table_use_frm)
+ {
+ /*
+ If we're not under LOCK TABLES and we're executing REPAIR TABLE
+ USE_FRM, we need to ignore errors from open_and_lock_tables().
+ REPAIR TABLE USE_FRM is a heavy weapon used when a table is
+ critically damaged, so open_and_lock_tables() will most likely
+ report errors. Those errors are not interesting for the user
+ because it's already known that the table is badly damaged.
+ */
+
+ Diagnostics_area *da= thd->get_stmt_da();
+ Warning_info tmp_wi(thd->query_id, false, true);
+
+ da->push_warning_info(&tmp_wi);
+
+ open_error= (thd->open_temporary_tables(table) ||
+ open_and_lock_tables(thd, table, TRUE, 0));
+
+ da->pop_warning_info();
+ }
+ else
+ {
+ /*
+ It's assumed that even if it is REPAIR TABLE USE_FRM, the table
+ can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
+ would fail). Thus, the only errors we could have from
+ open_and_lock_tables() are logical ones, like incorrect locking
+ mode. It does make sense for the user to see such errors.
+ */
+
+ open_error= (thd->open_temporary_tables(table) ||
+ open_and_lock_tables(thd, table, TRUE, 0));
+ }
+ thd->prepare_derived_at_open= FALSE;
+
+ table->next_global= save_next_global;
+ table->next_local= save_next_local;
+
+ return open_error;
+}
+
+
/*
RETURN VALUES
FALSE Message sent to net (admin operation went ok)
@@ -317,7 +408,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
HA_CHECK_OPT *))
{
TABLE_LIST *table;
- SELECT_LEX *select= &thd->lex->select_lex;
List<Item> field_list;
Item *item;
Protocol *protocol= thd->protocol;
@@ -392,85 +482,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
/* open only one table from local list of command */
while (1)
{
- TABLE_LIST *save_next_global, *save_next_local;
- save_next_global= table->next_global;
- table->next_global= 0;
- save_next_local= table->next_local;
- table->next_local= 0;
- select->table_list.first= table;
- /*
- Time zone tables and SP tables can be add to lex->query_tables list,
- so it have to be prepared.
- TODO: Investigate if we can put extra tables into argument instead of
- using lex->query_tables
- */
- lex->query_tables= table;
- lex->query_tables_last= &table->next_global;
- lex->query_tables_own_last= 0;
-
- /*
- CHECK TABLE command is allowed for views as well. Check on alter flags
- to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
- allowed.
- */
- if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
- view_operator_func == NULL)
- {
- table->required_type=FRMTYPE_TABLE;
- DBUG_ASSERT(!lex->only_view);
- }
- else if (lex->only_view)
- {
- table->required_type= FRMTYPE_VIEW;
- }
- else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR)
- {
- table->required_type= FRMTYPE_TABLE;
- }
-
- if (lex->sql_command == SQLCOM_CHECK ||
- lex->sql_command == SQLCOM_REPAIR ||
- lex->sql_command == SQLCOM_ANALYZE ||
- lex->sql_command == SQLCOM_OPTIMIZE)
- thd->prepare_derived_at_open= TRUE;
- if (!thd->locked_tables_mode && repair_table_use_frm)
- {
- /*
- If we're not under LOCK TABLES and we're executing REPAIR TABLE
- USE_FRM, we need to ignore errors from open_and_lock_tables().
- REPAIR TABLE USE_FRM is a heavy weapon used when a table is
- critically damaged, so open_and_lock_tables() will most likely
- report errors. Those errors are not interesting for the user
- because it's already known that the table is badly damaged.
- */
-
- Diagnostics_area *da= thd->get_stmt_da();
- Warning_info tmp_wi(thd->query_id, false, true);
-
- da->push_warning_info(&tmp_wi);
-
- open_error= (thd->open_temporary_tables(table) ||
- open_and_lock_tables(thd, table, TRUE, 0));
-
- da->pop_warning_info();
- }
- else
- {
- /*
- It's assumed that even if it is REPAIR TABLE USE_FRM, the table
- can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
- would fail). Thus, the only errors we could have from
- open_and_lock_tables() are logical ones, like incorrect locking
- mode. It does make sense for the user to see such errors.
- */
-
- open_error= (thd->open_temporary_tables(table) ||
- open_and_lock_tables(thd, table, TRUE, 0));
- }
- thd->prepare_derived_at_open= FALSE;
-
- table->next_global= save_next_global;
- table->next_local= save_next_local;
+ open_error= open_only_one_table(thd, table,
+ repair_table_use_frm,
+ (view_operator_func != NULL));
thd->open_options&= ~extra_open_options;
/*
@@ -711,7 +725,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (operator_func == &handler::ha_analyze)
{
TABLE *tab= table->table;
- Field **field_ptr= tab->field;
if (lex->with_persistent_for_clause &&
tab->s->table_category != TABLE_CATEGORY_USER)
@@ -723,58 +736,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
(get_use_stat_tables_mode(thd) > NEVER ||
lex->with_persistent_for_clause));
- if (collect_eis)
- {
- if (!lex->column_list)
- {
- bitmap_clear_all(tab->read_set);
- for (uint fields= 0; *field_ptr; field_ptr++, fields++)
- {
- enum enum_field_types type= (*field_ptr)->type();
- if (type < MYSQL_TYPE_MEDIUM_BLOB ||
- type > MYSQL_TYPE_BLOB)
- bitmap_set_bit(tab->read_set, fields);
- else if (collect_eis)
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_NO_EIS_FOR_FIELD,
- ER_THD(thd, ER_NO_EIS_FOR_FIELD),
- (*field_ptr)->field_name);
- }
- }
- else
- {
- int pos;
- LEX_STRING *column_name;
- List_iterator_fast<LEX_STRING> it(*lex->column_list);
-
- bitmap_clear_all(tab->read_set);
- while ((column_name= it++))
- {
- if (tab->s->fieldnames.type_names == 0 ||
- (pos= find_type(&tab->s->fieldnames, column_name->str,
- column_name->length, 1)) <= 0)
- {
- compl_result_code= result_code= HA_ADMIN_INVALID;
- break;
- }
- pos--;
- enum enum_field_types type= tab->field[pos]->type();
- if (type < MYSQL_TYPE_MEDIUM_BLOB ||
- type > MYSQL_TYPE_BLOB)
- bitmap_set_bit(tab->read_set, pos);
- else if (collect_eis)
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_NO_EIS_FOR_FIELD,
- ER_THD(thd, ER_NO_EIS_FOR_FIELD),
- column_name->str);
- }
- tab->file->column_bitmaps_signal();
- }
- }
- else
- {
- DBUG_ASSERT(!lex->column_list);
- }
if (!lex->index_list)
{
@@ -812,7 +773,77 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (compl_result_code == HA_ADMIN_OK && collect_eis)
{
- if (!(compl_result_code=
+ /*
+ Here we close and reopen table in read mode because operation of
+ collecting statistics is long and it will be better do not block
+ the table completely.
+ InnoDB/XtraDB will allow read/write and MyISAM read/insert.
+ */
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ thd->open_options|= extra_open_options;
+ close_thread_tables(thd);
+ table->table= NULL;
+ thd->mdl_context.release_transactional_locks();
+ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION);
+ table->mdl_request.set_type(MDL_SHARED_READ);
+
+ table->lock_type= TL_READ;
+ DBUG_ASSERT(view_operator_func == NULL);
+ open_error= open_only_one_table(thd, table,
+ repair_table_use_frm, FALSE);
+ thd->open_options&= ~extra_open_options;
+
+ TABLE *tab= table->table;
+ Field **field_ptr= tab->field;
+ if (!lex->column_list)
+ {
+ bitmap_clear_all(tab->read_set);
+ for (uint fields= 0; *field_ptr; field_ptr++, fields++)
+ {
+ enum enum_field_types type= (*field_ptr)->type();
+ if (type < MYSQL_TYPE_MEDIUM_BLOB ||
+ type > MYSQL_TYPE_BLOB)
+ bitmap_set_bit(tab->read_set, fields);
+ else
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NO_EIS_FOR_FIELD,
+ ER_THD(thd, ER_NO_EIS_FOR_FIELD),
+ (*field_ptr)->field_name);
+ }
+ }
+ else
+ {
+ int pos;
+ LEX_STRING *column_name;
+ List_iterator_fast<LEX_STRING> it(*lex->column_list);
+
+ bitmap_clear_all(tab->read_set);
+ while ((column_name= it++))
+ {
+ if (tab->s->fieldnames.type_names == 0 ||
+ (pos= find_type(&tab->s->fieldnames, column_name->str,
+ column_name->length, 1)) <= 0)
+ {
+ compl_result_code= result_code= HA_ADMIN_INVALID;
+ break;
+ }
+ pos--;
+ enum enum_field_types type= tab->field[pos]->type();
+ if (type < MYSQL_TYPE_MEDIUM_BLOB ||
+ type > MYSQL_TYPE_BLOB)
+ bitmap_set_bit(tab->read_set, pos);
+ else
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NO_EIS_FOR_FIELD,
+ ER_THD(thd, ER_NO_EIS_FOR_FIELD),
+ column_name->str);
+ }
+ tab->file->column_bitmaps_signal();
+ }
+ if (!open_error &&
+ !(compl_result_code=
alloc_statistics_for_table(thd, table->table)) &&
!(compl_result_code=
collect_statistics_for_table(thd, table->table)))
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
index 9e5fbaa425f..faba3a14a1b 100644
--- a/sql/sql_alter.h
+++ b/sql/sql_alter.h
@@ -174,6 +174,8 @@ public:
List<Key> key_list;
// List of columns, used by both CREATE and ALTER TABLE.
List<Create_field> create_list;
+
+ static const uint CHECK_CONSTRAINT_IF_NOT_EXISTS= 1;
List<Virtual_column_info> check_constraint_list;
// Type of ALTER TABLE operation.
uint flags;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 3481bf1a2d3..d3832a7068e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
@@ -638,6 +638,8 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
ha_extra_function extra,
TABLE *skip_table)
{
+ DBUG_ASSERT(!share->tmp_table);
+
char key[MAX_DBKEY_LENGTH];
uint key_length= share->table_cache_key.length;
const char *db= key;
@@ -876,8 +878,7 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
Do this *before* entering the TABLE_SHARE::tdc.LOCK_table_share
critical section.
*/
- if (table->file != NULL)
- MYSQL_UNBIND_TABLE(table->file);
+ MYSQL_UNBIND_TABLE(table->file);
tc_release_table(table);
DBUG_VOID_RETURN;
@@ -1173,6 +1174,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function)
{
DBUG_ENTER("wait_while_table_is_used");
+ DBUG_ASSERT(!table->s->tmp_table);
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->tdc->version));
@@ -2090,6 +2092,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
return TRUE;
}
}
+
+ TRANSACT_TRACKER(add_trx_state(thd, TX_LOCKED_TABLES));
+
thd->enter_locked_tables_mode(LTM_LOCK_TABLES);
return FALSE;
@@ -2130,6 +2135,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
}
thd->leave_locked_tables_mode();
+ TRANSACT_TRACKER(clear_trx_state(thd, TX_LOCKED_TABLES));
+
DBUG_ASSERT(thd->transaction.stmt.is_empty());
close_thread_tables(thd);
@@ -4203,6 +4210,15 @@ handle_view(THD *thd, Query_tables_list *prelocking_ctx,
&table_list->view->sroutines_list,
table_list->top_table());
}
+
+ /*
+ If a trigger was defined on one of the associated tables then assign the
+ 'trg_event_map' value of the view to the next table in table_list. When a
+ Stored function is invoked, all the associated tables including the tables
+ associated with the trigger are prelocked.
+ */
+ if (table_list->trg_event_map && table_list->next_global)
+ table_list->next_global->trg_event_map= table_list->trg_event_map;
return FALSE;
}
@@ -4351,6 +4367,13 @@ static bool check_lock_and_start_stmt(THD *thd,
table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
}
+
+ /*
+ Record in transaction state tracking
+ */
+ TRANSACT_TRACKER(add_trx_state(thd, lock_type,
+ table_list->table->file->has_transactions()));
+
DBUG_RETURN(0);
}
@@ -4684,6 +4707,45 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
}
+static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables)
+{
+ Security_context *save_security_ctx= thd->security_ctx;
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ DBUG_ENTER("fix_session_vcol_expr");
+
+ for (TABLE_LIST *table= tables; table && table != first_not_own;
+ table= table->next_global)
+ {
+ TABLE *t= table->table;
+ if (!table->placeholder() && t->s->vcols_need_refixing &&
+ table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ if (table->security_ctx)
+ thd->security_ctx= table->security_ctx;
+
+ for (Field **vf= t->vfield; vf && *vf; vf++)
+ if (fix_session_vcol_expr(thd, (*vf)->vcol_info))
+ goto err;
+
+ for (Field **df= t->default_field; df && *df; df++)
+ if ((*df)->default_value &&
+ fix_session_vcol_expr(thd, (*df)->default_value))
+ goto err;
+
+ for (Virtual_column_info **cc= t->check_constraints; cc && *cc; cc++)
+ if (fix_session_vcol_expr(thd, (*cc)))
+ goto err;
+
+ thd->security_ctx= save_security_ctx;
+ }
+ }
+ DBUG_RETURN(0);
+err:
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(1);
+}
+
+
/**
Lock all tables in a list.
@@ -4845,7 +4907,11 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
}
}
- DBUG_RETURN(thd->decide_logging_format(tables));
+ bool res= fix_all_session_vcol_exprs(thd, tables);
+ if (!res)
+ res= thd->decide_logging_format(tables);
+
+ DBUG_RETURN(res);
}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 06c5f992939..8ff4684f0ff 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1381,6 +1381,21 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_VOID_RETURN;
}
+ /*
+ Do not store queries while tracking transaction state.
+ The tracker already flags queries that actually have
+ transaction tracker items, but this will make behavior
+ more straight forward.
+ */
+#ifndef EMBEDDED_LIBRARY
+ if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
+ {
+ DBUG_PRINT("qcache", ("Do not work with transaction tracking"));
+ DBUG_VOID_RETURN;
+ }
+#endif //EMBEDDED_LIBRARY
+
+
/* The following assert fails if we haven't called send_result_to_client */
DBUG_ASSERT(thd->base_query.is_alloced() ||
thd->base_query.ptr() == thd->query());
@@ -1719,6 +1734,20 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
goto err;
}
+ /*
+ Don't allow serving from Query_cache while tracking transaction
+ state. This is a safeguard in case an otherwise matching query
+ was added to the cache before tracking was turned on.
+ */
+#ifndef EMBEDDED_LIBRARY
+ if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
+ {
+ DBUG_PRINT("qcache", ("Do not work with transaction tracking"));
+ goto err;
+ }
+#endif //EMBEDDED_LIBRARY
+
+
thd->query_cache_is_applicable= 1;
sql= org_sql; sql_end= sql + query_length;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9710ea8bbe3..6433786a079 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -870,7 +870,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
is_fatal_sub_stmt_error(false),
rand_used(0),
time_zone_used(0),
- in_lock_tables(0), in_stored_expression(0),
+ in_lock_tables(0),
bootstrap(0),
derived_tables_processing(FALSE),
waiting_on_group_commit(FALSE), has_waiter(FALSE),
@@ -1018,6 +1018,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
wsrep_TOI_pre_query_len = 0;
wsrep_info[sizeof(wsrep_info) - 1] = '\0'; /* make sure it is 0-terminated */
wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
+ wsrep_affected_rows = 0;
#endif
/* Call to init() below requires fully initialized Open_tables_state. */
reset_open_tables_state(this);
@@ -1448,6 +1449,7 @@ void THD::init(void)
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
+ wsrep_affected_rows = 0;
#endif /* WITH_WSREP */
if (variables.sql_log_bin)
@@ -1465,6 +1467,11 @@ void THD::init(void)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
debug_sync_init_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
+
+#ifndef EMBEDDED_LIBRARY
+ session_tracker.enable(this);
+#endif //EMBEDDED_LIBRARY
+
apc_target.init(&LOCK_thd_data);
DBUG_VOID_RETURN;
}
@@ -1661,7 +1668,9 @@ void THD::free_connection()
/* close all prepared statements, to save memory */
stmt_map.reset();
free_connection_done= 1;
+#if defined(ENABLED_PROFILING)
profiling.restart(); // Reset profiling
+#endif
}
/*
@@ -1686,7 +1695,9 @@ void THD::reset_for_reuse()
abort_on_warning= 0;
free_connection_done= 0;
m_command= COM_CONNECT;
+#if defined(ENABLED_PROFILING)
profiling.reset();
+#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
@@ -1759,6 +1770,12 @@ THD::~THD()
lf_hash_put_pins(xid_hash_pins);
/* Ensure everything is freed */
status_var.local_memory_used-= sizeof(THD);
+
+ /* trick to make happy memory accounting system */
+#ifndef EMBEDDED_LIBRARY
+ session_tracker.deinit();
+#endif //EMBEDDED_LIBRARY
+
if (status_var.local_memory_used != 0)
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used));
@@ -2296,6 +2313,8 @@ void THD::cleanup_after_query()
#ifdef WITH_WSREP
wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
+ if (!in_active_multi_stmt_transaction())
+ wsrep_affected_rows= 0;
#endif /* WITH_WSREP */
DBUG_VOID_RETURN;
@@ -2334,7 +2353,7 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
to->length= copy_and_convert((char*) to->str, new_length, to_cs,
from, from_length, from_cs, &errors);
to->str[to->length]= 0; // Safety
- if (errors && in_stored_expression)
+ if (errors && lex->parse_vcol_expr)
{
my_error(ER_BAD_DATA, MYF(0),
ErrConvString(from, from_length, from_cs).ptr(),
@@ -4665,13 +4684,88 @@ thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd)
cause replication to rollback (and later re-try) the other transaction,
releasing the lock for this transaction so replication can proceed.
*/
- other_rgi->killed_for_retry= true;
+ other_rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
mysql_mutex_lock(&other_thd->LOCK_thd_data);
other_thd->awake(KILL_CONNECTION);
mysql_mutex_unlock(&other_thd->LOCK_thd_data);
}
/*
+ Used by storage engines (currently TokuDB) to report that one transaction
+ THD is about to go to wait for a transactional lock held by another
+ transactions OTHER_THD.
+
+ This is used for parallel replication, where transactions are required to
+ commit in the same order on the slave as they did on the master. If the
+ transactions on the slave encounter lock conflicts on the slave that did not
+ exist on the master, this can cause deadlocks. This is primarily used in
+ optimistic (and aggressive) modes.
+
+ Normally, such conflicts will not occur in conservative mode, because the
+ same conflict would have prevented the two transactions from committing in
+ parallel on the master, thus preventing them from running in parallel on the
+ slave in the first place. However, it is possible in case when the optimizer
+ chooses a different plan on the slave than on the master (eg. table scan
+ instead of index scan).
+
+ InnoDB/XtraDB reports lock waits using this call. If a lock wait causes a
+ deadlock with the pre-determined commit order, we kill the later transaction,
+ and later re-try it, to resolve the deadlock.
+
+ This call need only receive reports about waits for locks that will remain
+ until the holding transaction commits. InnoDB/XtraDB auto-increment locks,
+ for example, are released earlier, and so need not be reported. (Such false
+ positives are not harmful, but could lead to unnecessary kill and retry, so
+ best avoided).
+
+ Returns 1 if the OTHER_THD will be killed to resolve deadlock, 0 if not. The
+ actual kill will happen later, asynchronously from another thread. The
+ caller does not need to take any actions on the return value if the
+ handlerton kill_query method is implemented to abort the to-be-killed
+ transaction.
+*/
+extern "C" int
+thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
+{
+ rpl_group_info *rgi;
+ rpl_group_info *other_rgi;
+
+ if (!thd)
+ return 0;
+ DEBUG_SYNC(thd, "thd_report_wait_for");
+ thd->transaction.stmt.mark_trans_did_wait();
+ if (!other_thd)
+ return 0;
+ binlog_report_wait_for(thd, other_thd);
+ rgi= thd->rgi_slave;
+ other_rgi= other_thd->rgi_slave;
+ if (!rgi || !other_rgi)
+ return 0;
+ if (!rgi->is_parallel_exec)
+ return 0;
+ if (rgi->rli != other_rgi->rli)
+ return 0;
+ if (!rgi->gtid_sub_id || !other_rgi->gtid_sub_id)
+ return 0;
+ if (rgi->current_gtid.domain_id != other_rgi->current_gtid.domain_id)
+ return 0;
+ if (rgi->gtid_sub_id > other_rgi->gtid_sub_id)
+ return 0;
+ /*
+ This transaction is about to wait for another transaction that is required
+ by replication binlog order to commit after. This would cause a deadlock.
+
+ So send a kill to the other transaction, with a temporary error; this will
+ cause replication to rollback (and later re-try) the other transaction,
+ releasing the lock for this transaction so replication can proceed.
+ */
+#ifdef HAVE_REPLICATION
+ slave_background_kill_request(other_thd);
+#endif
+ return 1;
+}
+
+/*
This function is called from InnoDB/XtraDB to check if the commit order of
two transactions has already been decided by the upper layer. This happens
in parallel replication, where the commit order is forced to be the same on
@@ -5267,7 +5361,11 @@ void THD::get_definer(LEX_USER *definer, bool role)
{
binlog_invoker(role);
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#ifdef WITH_WSREP
+ if ((wsrep_applier || slave_thread) && has_invoker())
+#else
if (slave_thread && has_invoker())
+#endif
{
definer->user = invoker_user;
definer->host= invoker_host;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5a24a2ed2a4..2240b5fe0a9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -46,6 +46,7 @@
#include <mysql/psi/mysql_idle.h>
#include <mysql/psi/mysql_table.h>
#include <mysql_com_server.h>
+#include "session_tracker.h"
extern "C"
void set_thd_stage_info(void *thd,
@@ -566,6 +567,7 @@ typedef struct system_variables
ulong max_allowed_packet;
ulong max_error_count;
ulong max_length_for_sort_data;
+ ulong max_recursive_iterations;
ulong max_sort_length;
ulong max_tmp_tables;
ulong max_insert_delayed_threads;
@@ -636,6 +638,7 @@ typedef struct system_variables
my_bool old_alter_table;
my_bool old_passwords;
my_bool big_tables;
+ my_bool only_standards_compliant_cte;
my_bool query_cache_strip_comments;
my_bool sql_log_slow;
my_bool sql_log_bin;
@@ -689,6 +692,11 @@ typedef struct system_variables
my_bool pseudo_slave_mode;
+ char *session_track_system_variables;
+ ulong session_track_transaction_info;
+ my_bool session_track_schema;
+ my_bool session_track_state_change;
+
} SV;
/**
@@ -723,9 +731,11 @@ typedef struct system_status_var
ulong ha_read_key_count;
ulong ha_read_next_count;
ulong ha_read_prev_count;
+ ulong ha_read_retry_count;
ulong ha_read_rnd_count;
ulong ha_read_rnd_next_count;
ulong ha_read_rnd_deleted_count;
+
/*
This number doesn't include calls to the default implementation and
calls made by range access. The intent is to count only calls made by
@@ -759,6 +769,8 @@ typedef struct system_status_var
ulong select_range_count_;
ulong select_range_check_count_;
ulong select_scan_count_;
+ ulong update_scan_count;
+ ulong delete_scan_count;
ulong executed_triggers;
ulong long_query_count;
ulong filesort_merge_passes_;
@@ -824,8 +836,7 @@ typedef struct system_status_var
Global status variables
*/
-extern ulong feature_files_opened_with_delayed_keys;
-
+extern ulong feature_files_opened_with_delayed_keys, feature_check_constraint;
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
@@ -1519,7 +1530,7 @@ enum enum_thread_type
SYSTEM_THREAD_EVENT_SCHEDULER= 8,
SYSTEM_THREAD_EVENT_WORKER= 16,
SYSTEM_THREAD_BINLOG_BACKGROUND= 32,
- SYSTEM_THREAD_SLAVE_INIT= 64
+ SYSTEM_THREAD_SLAVE_BACKGROUND= 64
};
inline char const *
@@ -1534,7 +1545,7 @@ show_system_thread(enum_thread_type thread)
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_SQL);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER);
- RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_INIT);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND);
default:
sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread);
return buf;
@@ -2859,7 +2870,7 @@ public:
bool query_start_sec_part_used;
/* for IS NULL => = last_insert_id() fix in remove_eq_conds() */
bool substitute_null_with_insert_id;
- bool in_lock_tables, in_stored_expression;
+ bool in_lock_tables;
bool bootstrap, cleanup_done, free_connection_done;
/** is set if some thread specific value(s) used in a statement. */
@@ -3177,12 +3188,12 @@ public:
set_start_time();
start_utime= utime_after_lock= microsecond_interval_timer();
}
- inline void set_time(my_hrtime_t t)
+ inline void set_time(my_hrtime_t t)
{
user_time= t;
set_time();
}
- inline void set_time(my_time_t t, ulong sec_part)
+ inline void set_time(my_time_t t, ulong sec_part)
{
my_hrtime_t hrtime= { hrtime_from_time(t) + sec_part };
set_time(hrtime);
@@ -3195,6 +3206,12 @@ public:
}
ulonglong current_utime() { return microsecond_interval_timer(); }
+ /* Tell SHOW PROCESSLIST to show time from this point */
+ inline void set_time_for_next_stage()
+ {
+ utime_after_query= current_utime();
+ }
+
/**
Update server status after execution of a top level statement.
Currently only checks if a query was slow, and assigns
@@ -3204,7 +3221,7 @@ public:
*/
void update_server_status()
{
- utime_after_query= current_utime();
+ set_time_for_next_stage();
if (utime_after_query > utime_after_lock + variables.long_query_time)
server_status|= SERVER_QUERY_WAS_SLOW;
}
@@ -4050,6 +4067,9 @@ private:
LEX_STRING invoker_host;
public:
+#ifndef EMBEDDED_LIBRARY
+ Session_tracker session_tracker;
+#endif //EMBEDDED_LIBRARY
/*
Flag, mutex and condition for a thread to wait for a signal from another
thread.
@@ -4194,6 +4214,7 @@ public:
*/
bool wsrep_ignore_table;
wsrep_gtid_t wsrep_sync_wait_gtid;
+ ulong wsrep_affected_rows;
#endif /* WITH_WSREP */
/* Handling of timeouts for commands */
@@ -4283,6 +4304,8 @@ my_eof(THD *thd)
{
thd->set_row_count_func(-1);
thd->get_stmt_da()->set_eof_status(thd);
+
+ TRANSACT_TRACKER(add_trx_state(thd, TX_RESULT_SET));
}
#define tmp_disable_binlog(A) \
@@ -4367,6 +4390,7 @@ protected:
/* Something used only by the parser: */
public:
select_result(THD *thd_arg): select_result_sink(thd_arg) {}
+ void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
virtual ~select_result() {};
/**
Change wrapped select_result.
@@ -4811,6 +4835,7 @@ public:
}
};
+
class select_union :public select_result_interceptor
{
public:
@@ -4848,6 +4873,30 @@ public:
};
+class select_union_recursive :public select_union
+{
+ public:
+ /* The temporary table with the new records generated by one iterative step */
+ TABLE *incr_table;
+ /* One of tables from the list rec_tables (determined dynamically) */
+ TABLE *first_rec_table_to_update;
+ /* The temporary tables used for recursive table references */
+ List<TABLE> rec_tables;
+
+ select_union_recursive(THD *thd_arg):
+ select_union(thd_arg),
+ incr_table(0), first_rec_table_to_update(0) {};
+
+ int send_data(List<Item> &items);
+ bool create_result_table(THD *thd, List<Item> *column_types,
+ bool is_distinct, ulonglong options,
+ const char *alias,
+ bool bit_fields_as_long,
+ bool create_table,
+ bool keep_row_order= FALSE);
+ void cleanup();
+};
+
/**
UNION result that is passed directly to the receiving select_result
without filling a temporary table.
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index 77f0bcf04ba..f6447b29827 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -3,6 +3,39 @@
#include "sql_cte.h"
#include "sql_view.h" // for make_valid_column_names
#include "sql_parse.h"
+#include "sql_select.h"
+
+
+/**
+ @brief
+ Add a new element to this with clause
+
+ @param elem The with element to add to this with clause
+
+ @details
+ The method adds the with element 'elem' to the elements
+ in this with clause. The method reports an error if
+ the number of the added element exceeds the value
+ of the constant max_number_of_elements_in_with_clause.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool With_clause::add_with_element(With_element *elem)
+{
+ if (with_list.elements == max_number_of_elements_in_with_clause)
+ {
+ my_error(ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE, MYF(0));
+ return true;
+ }
+ elem->owner= this;
+ elem->number= with_list.elements;
+ elem->spec->with_element= elem;
+ with_list.link_in_list(elem, &elem->next);
+ return false;
+}
/**
@@ -13,8 +46,12 @@
with_clauses_list Pointer to the first clause in the list
@details
- The procedure just calls the method With_clause::check_dependencies
- for each member of the given list.
+ For each with clause from the given list the procedure finds all
+ dependencies between tables defined in the clause by calling the
+ method With_clause::checked_dependencies.
+ Additionally, based on the info collected by this method the procedure
+ finds anchors for each recursive definition and moves them at the head
+ of the definition.
@retval
false on success
@@ -29,6 +66,9 @@ bool check_dependencies_in_with_clauses(With_clause *with_clauses_list)
{
if (with_clause->check_dependencies())
return true;
+ if (with_clause->check_anchors())
+ return true;
+ with_clause->move_anchors_ahead();
}
return false;
}
@@ -39,18 +79,13 @@ bool check_dependencies_in_with_clauses(With_clause *with_clauses_list)
Check dependencies between tables defined in this with clause
@details
- The method performs the following actions for this with clause:
-
- 1. Test for definitions of the tables with the same name.
- 2. For each table T defined in this with clause look for tables
- from the same with clause that are used in the query that
- specifies T and set the dependencies of T on these tables
- in dependency_map.
- 3. Build the transitive closure of the above direct dependencies
- to find out all recursive definitions.
- 4. If this with clause is not specified as recursive then
- for each with table T defined in this with clause check whether
- it is used in any definition that follows the definition of T.
+ The method performs the following for this with clause:
+ - checks that there are no definitions of the tables with the same name
+ - for each table T defined in this with clause looks for the tables
+ from the same with clause that are used in the query that specifies T
+ and set the dependencies of T on these tables in a bitmap.
+ - builds the transitive closure of the above direct dependencies
+ to find out all recursive definitions.
@retval
true if an error is reported
@@ -64,18 +99,18 @@ bool With_clause::check_dependencies()
/*
Look for for definitions with the same query name.
When found report an error and return true immediately.
- For each table T defined in this with clause look for all other tables from
- the same with with clause that are used in the specification of T.
+ For each table T defined in this with clause look for all other tables
+ from the same with clause that are used in the specification of T.
For each such table set the dependency bit in the dependency map of
- with element for T.
+ the with element for T.
*/
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
{
- for (With_element *elem= first_elem;
+ for (With_element *elem= with_list.first;
elem != with_elem;
- elem= elem->next_elem)
+ elem= elem->next)
{
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
elem->query_name->str) == 0)
@@ -84,106 +119,81 @@ bool With_clause::check_dependencies()
return true;
}
}
- with_elem->check_dependencies_in_unit(with_elem->spec);
+ if (with_elem->check_dependencies_in_spec())
+ return true;
}
/* Build the transitive closure of the direct dependencies found above */
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ with_elem->derived_dep_map= with_elem->base_dep_map;
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
{
table_map with_elem_map= with_elem->get_elem_map();
- for (With_element *elem= first_elem; elem != NULL; elem= elem->next_elem)
+ for (With_element *elem= with_list.first; elem; elem= elem->next)
{
- if (elem->dependency_map & with_elem_map)
- elem->dependency_map |= with_elem->dependency_map;
+ if (elem->derived_dep_map & with_elem_map)
+ elem->derived_dep_map |= with_elem->derived_dep_map;
}
}
/*
- Mark those elements where tables are defined with direct or indirect recursion.
- Report an error when recursion (direct or indirect) is used to define a table.
+ Mark those elements where tables are defined with direct or indirect
+ make recursion.
*/
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
{
- if (with_elem->dependency_map & with_elem->get_elem_map())
+ if (with_elem->derived_dep_map & with_elem->get_elem_map())
with_elem->is_recursive= true;
}
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
- {
- if (with_elem->is_recursive)
- {
- my_error(ER_RECURSIVE_QUERY_IN_WITH_CLAUSE, MYF(0),
- with_elem->query_name->str);
- return true;
- }
- }
-
- if (!with_recursive)
- {
- /*
- For each with table T defined in this with clause check whether
- it is used in any definition that follows the definition of T.
- */
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
- {
- With_element *checked_elem= with_elem->next_elem;
- for (uint i = with_elem->number+1;
- i < elements;
- i++, checked_elem= checked_elem->next_elem)
- {
- if (with_elem->check_dependency_on(checked_elem))
- {
- my_error(ER_WRONG_ORDER_IN_WITH_CLAUSE, MYF(0),
- with_elem->query_name->str, checked_elem->query_name->str);
- return true;
- }
- }
- }
- }
dependencies_are_checked= true;
return false;
}
+/*
+ This structure describes an element of the stack of embedded units.
+ The stack is used when looking for a definition of a table in
+ with clauses. The definition can be found only in the scopes
+ of the with clauses attached to the units from the stack.
+ The with clauses are looked through from starting from the top
+ element of the stack.
+*/
+
+struct st_unit_ctxt_elem
+{
+ st_unit_ctxt_elem *prev; // the previous element of the stack
+ st_select_lex_unit *unit;
+};
+
+
/**
@brief
- Check dependencies on the sibling with tables used in the given unit
-
- @param unit The unit where the siblings are to be searched for
+ Find the dependencies of this element on its siblings in its specification
@details
- The method recursively looks through all from lists encountered
- the given unit. If it finds a reference to a table that is
- defined in the same with clause to which this element belongs
- the method set the bit of dependency on this table in the
- dependency_map of this element.
+ For each table reference ref(T) from the FROM list of every select sl
+ immediately contained in the specification query of this element this
+ method searches for the definition of T in the the with clause which
+ this element belongs to. If such definition is found then the dependency
+ on it is set in sl->with_dep and in this->base_dep_map.
*/
-void With_element::check_dependencies_in_unit(st_select_lex_unit *unit)
-{
- st_select_lex *sl= unit->first_select();
- for (; sl; sl= sl->next_select())
+bool With_element::check_dependencies_in_spec()
+{
+ for (st_select_lex *sl= spec->first_select(); sl; sl= sl->next_select())
{
- for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local)
- {
- if (!tbl->with)
- tbl->with= owner->find_table_def(tbl);
- if (!tbl->with && tbl->select_lex)
- tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
- if (tbl->with && tbl->with->owner== this->owner)
- set_dependency_on(tbl->with);
- }
- st_select_lex_unit *inner_unit= sl->first_inner_unit();
- for (; inner_unit; inner_unit= inner_unit->next_unit())
- check_dependencies_in_unit(inner_unit);
+ st_unit_ctxt_elem ctxt0= {NULL, owner->owner};
+ st_unit_ctxt_elem ctxt1= {&ctxt0, spec};
+ check_dependencies_in_select(sl, &ctxt1, false, &sl->with_dep);
+ base_dep_map|= sl->with_dep;
}
+ return false;
}
@@ -192,6 +202,7 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit)
Search for the definition of a table among the elements of this with clause
@param table The reference to the table that is looked for
+ @param barrier The barrier with element for the search
@details
The function looks through the elements of this with clause trying to find
@@ -204,14 +215,17 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit)
NULL - otherwise
*/
-With_element *With_clause::find_table_def(TABLE_LIST *table)
+With_element *With_clause::find_table_def(TABLE_LIST *table,
+ With_element *barrier)
{
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ for (With_element *with_elem= with_list.first;
+ with_elem != barrier;
+ with_elem= with_elem->next)
{
- if (my_strcasecmp(system_charset_info, with_elem->query_name->str, table->table_name) == 0)
+ if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
+ table->table_name) == 0)
{
+ table->set_derived();
return with_elem;
}
}
@@ -221,6 +235,399 @@ With_element *With_clause::find_table_def(TABLE_LIST *table)
/**
@brief
+ Search for the definition of a table in with clauses
+
+ @param tbl The reference to the table that is looked for
+ @param ctxt The context describing in what with clauses of the upper
+ levels the table has to be searched for.
+
+ @details
+ The function looks for the definition of the table tbl in the definitions
+ of the with clauses from the upper levels specified by the parameter ctxt.
+ When it encounters the element with the same query name as the table's name
+ it returns this element. If no such definitions are found the function
+ returns NULL.
+
+ @retval
+ found with element if the search succeeded
+ NULL - otherwise
+*/
+
+With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
+ st_unit_ctxt_elem *ctxt)
+{
+ With_element *barrier= NULL;
+ for (st_unit_ctxt_elem *unit_ctxt_elem= ctxt;
+ unit_ctxt_elem;
+ unit_ctxt_elem= unit_ctxt_elem->prev)
+ {
+ st_select_lex_unit *unit= unit_ctxt_elem->unit;
+ With_clause *with_clause= unit->with_clause;
+ if (with_clause &&
+ (tbl->with= with_clause->find_table_def(tbl, barrier)))
+ return tbl->with;
+ barrier= NULL;
+ if (unit->with_element && !unit->with_element->get_owner()->with_recursive)
+ {
+ /*
+ This unit is the specification if the with element unit->with_element.
+ The with element belongs to a with clause without the specifier RECURSIVE.
+ So when searching for the matching definition of tbl this with clause must
+ be looked up to this with element
+ */
+ barrier= unit->with_element;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ @brief
+ Find the dependencies of this element on its siblings in a select
+
+ @param sl The select where to look for the dependencies
+ @param ctxt The structure specifying the scope of the definitions
+ of the with elements of the upper levels
+ @param in_sbq if true mark dependencies found in subqueries in
+ this->sq_dep_map
+ @param dep_map IN/OUT The bit where to mark the found dependencies
+
+ @details
+ For each table reference ref(T) from the FROM list of the select sl
+ the method searches in with clauses for the definition of the table T.
+ If the found definition belongs to the same with clause as this with
+ element then the method set dependency on T in the in/out parameter
+ dep_map, add if required - in this->sq_dep_map.
+ The parameter ctxt describes the proper context for the search
+ of the definition of T.
+*/
+
+void With_element::check_dependencies_in_select(st_select_lex *sl,
+ st_unit_ctxt_elem *ctxt,
+ bool in_subq,
+ table_map *dep_map)
+{
+ With_clause *with_clause= sl->get_with_clause();
+ for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->derived || tbl->nested_join)
+ continue;
+ tbl->with_internal_reference_map= 0;
+ /*
+ If there is a with clause attached to the unit containing sl
+ look first for the definition of tbl in this with clause.
+ If such definition is not found there look in the with
+ clauses of the upper levels.
+ If the definition of tbl is found somewhere in with clauses
+ then tbl->with is set to point to this definition
+ */
+ if (with_clause && !tbl->with)
+ tbl->with= with_clause->find_table_def(tbl, NULL);
+ if (!tbl->with)
+ tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
+
+ if (tbl->with && tbl->with->owner== this->owner)
+ {
+ /*
+ The found definition T of tbl belongs to the same
+ with clause as this with element. In this case:
+ - set the dependence on T in the bitmap dep_map
+ - set tbl->with_internal_reference_map with
+ the bitmap for this definition
+ - set the dependence on T in the bitmap this->sq_dep_map
+ if needed
+ */
+ *dep_map|= tbl->with->get_elem_map();
+ tbl->with_internal_reference_map= get_elem_map();
+ if (in_subq)
+ sq_dep_map|= tbl->with->get_elem_map();
+ }
+ }
+ /* Now look for the dependencies in the subqueries of sl */
+ st_select_lex_unit *inner_unit= sl->first_inner_unit();
+ for (; inner_unit; inner_unit= inner_unit->next_unit())
+ check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map);
+}
+
+
+/**
+ @brief
+ Find the dependencies of this element on its siblings in a unit
+
+ @param unit The unit where to look for the dependencies
+ @param ctxt The structure specifying the scope of the definitions
+ of the with elements of the upper levels
+ @param in_sbq if true mark dependencies found in subqueries in
+ this->sq_dep_map
+ @param dep_map IN/OUT The bit where to mark the found dependencies
+
+ @details
+ This method searches in the unit 'unit' for the the references in FROM
+ lists of all selects contained in this unit and in the with clause
+ attached to this unit that refer to definitions of tables from the
+ same with clause as this element.
+ If such definitions are found then the dependencies on them are
+ set in the in/out parameter dep_map and optionally in this->sq_dep_map.
+ The parameter ctxt describes the proper context for the search.
+*/
+
+void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
+ st_unit_ctxt_elem *ctxt,
+ bool in_subq,
+ table_map *dep_map)
+{
+ if (unit->with_clause)
+ check_dependencies_in_with_clause(unit->with_clause, ctxt, in_subq, dep_map);
+ in_subq |= unit->item != NULL;
+ st_unit_ctxt_elem unit_ctxt_elem= {ctxt, unit};
+ st_select_lex *sl= unit->first_select();
+ for (; sl; sl= sl->next_select())
+ {
+ check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map);
+ }
+}
+
+
+/**
+ @brief
+ Find the dependencies of this element on its siblings in a with clause
+
+ @param witt_clause The with clause where to look for the dependencies
+ @param ctxt The structure specifying the scope of the definitions
+ of the with elements of the upper levels
+ @param in_sbq if true mark dependencies found in subqueries in
+ this->sq_dep_map
+ @param dep_map IN/OUT The bit where to mark the found dependencies
+
+ @details
+ This method searches in the with_clause for the the references in FROM
+ lists of all selects contained in the specifications of the with elements
+ from this with_clause that refer to definitions of tables from the
+ same with clause as this element.
+ If such definitions are found then the dependencies on them are
+ set in the in/out parameter dep_map and optionally in this->sq_dep_map.
+ The parameter ctxt describes the proper context for the search.
+*/
+
+void
+With_element::check_dependencies_in_with_clause(With_clause *with_clause,
+ st_unit_ctxt_elem *ctxt,
+ bool in_subq,
+ table_map *dep_map)
+{
+ for (With_element *with_elem= with_clause->with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ check_dependencies_in_unit(with_elem->spec, ctxt, in_subq, dep_map);
+ }
+}
+
+
+/**
+ @brief
+ Find mutually recursive with elements and check that they have ancors
+
+ @details
+ This method performs the following:
+ - for each recursive with element finds all mutually recursive with it
+ - links each group of mutually recursive with elements into a ring chain
+ - checks that every group of mutually recursive with elements contains
+ at least one anchor
+ - checks that after removing any with element with anchor the remaining
+ with elements mutually recursive with the removed one are not recursive
+ anymore
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool With_clause::check_anchors()
+{
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ if (!with_elem->is_recursive)
+ continue;
+
+ /*
+ It with_elem is recursive with element find all elements mutually recursive
+ with it (any recursive element is mutually recursive with itself). Mark all
+ these elements in the bitmap->mutually_recursive. Also link all these
+ elements into a ring chain.
+ */
+ if (!with_elem->next_mutually_recursive)
+ {
+ With_element *last_mutually_recursive= with_elem;
+ table_map with_elem_dep= with_elem->derived_dep_map;
+ table_map with_elem_map= with_elem->get_elem_map();
+ for (With_element *elem= with_elem; elem; elem= elem->next)
+ {
+ if (!elem->is_recursive)
+ continue;
+
+ if (elem == with_elem ||
+ ((elem->derived_dep_map & with_elem_map) &&
+ (with_elem_dep & elem->get_elem_map())))
+ {
+ elem->next_mutually_recursive= with_elem;
+ last_mutually_recursive->next_mutually_recursive= elem;
+ last_mutually_recursive= elem;
+ with_elem->mutually_recursive|= elem->get_elem_map();
+ }
+ }
+ for (With_element *elem= with_elem->next_mutually_recursive;
+ elem != with_elem;
+ elem= elem->next_mutually_recursive)
+ elem->mutually_recursive= with_elem->mutually_recursive;
+ }
+
+ /*
+ For each select from the specification of 'with_elem' check whether
+ it is an anchor i.e. does not depend on any with elements mutually
+ recursive with 'with_elem".
+ */
+ for (st_select_lex *sl= with_elem->spec->first_select();
+ sl;
+ sl= sl->next_select())
+ {
+ if (with_elem->is_anchor(sl))
+ {
+ with_elem->with_anchor= true;
+ break;
+ }
+ }
+ }
+
+ /*
+ Check that for any group of mutually recursive with elements
+ - there is at least one anchor
+ - after removing any with element with anchor the remaining with elements
+ mutually recursive with the removed one are not recursive anymore
+ */
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ if (!with_elem->is_recursive)
+ continue;
+
+ if (!with_elem->with_anchor)
+ {
+ /*
+ Check that the other with elements mutually recursive with 'with_elem'
+ contain at least one anchor.
+ */
+ With_element *elem= with_elem;
+ while ((elem= elem->get_next_mutually_recursive()) != with_elem)
+ {
+ if (elem->with_anchor)
+ break;
+ }
+ if (elem == with_elem)
+ {
+ my_error(ER_RECURSIVE_WITHOUT_ANCHORS, MYF(0),
+ with_elem->query_name->str);
+ return true;
+ }
+ }
+ else
+ {
+ /* 'with_elem' is a with element with an anchor */
+ With_element *elem= with_elem;
+ /*
+ For the other with elements mutually recursive with 'with_elem'
+ set dependency bits between those elements in the field work_dep_map
+ and build transitive closure of these dependencies
+ */
+ while ((elem= elem->get_next_mutually_recursive()) != with_elem)
+ elem->work_dep_map= elem->base_dep_map & elem->mutually_recursive;
+ elem= with_elem;
+ while ((elem= elem->get_next_mutually_recursive()) != with_elem)
+ {
+ table_map elem_map= elem->get_elem_map();
+ With_element *el= with_elem;
+ while ((el= el->get_next_mutually_recursive()) != with_elem)
+ {
+ if (el->work_dep_map & elem_map)
+ el->work_dep_map|= elem->work_dep_map;
+ }
+ }
+ /* If the transitive closure displays any cycle report an arror */
+ elem= with_elem;
+ while ((elem= elem->get_next_mutually_recursive()) != with_elem)
+ {
+ if (elem->work_dep_map & elem->get_elem_map())
+ {
+ my_error(ER_UNACCEPTABLE_MUTUAL_RECURSION, MYF(0),
+ with_elem->query_name->str);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ @brief
+ Move anchors at the beginning of the specifications for with elements
+
+ @details
+ This method moves anchors at the beginning of the specifications for
+ all recursive with elements.
+*/
+
+void With_clause::move_anchors_ahead()
+{
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ if (with_elem->is_recursive)
+ with_elem->move_anchors_ahead();
+ }
+}
+
+
+/**
+ @brief
+ Move anchors at the beginning of the specification of this with element
+
+ @details
+ If the specification of this with element contains anchors the method
+ moves them at the very beginning of the specification.
+*/
+
+void With_element::move_anchors_ahead()
+{
+ st_select_lex *next_sl;
+ st_select_lex *new_pos= spec->first_select();
+ st_select_lex *last_sl;
+ new_pos->linkage= UNION_TYPE;
+ for (st_select_lex *sl= new_pos; sl; sl= next_sl)
+ {
+ next_sl= sl->next_select();
+ if (is_anchor(sl))
+ {
+ sl->move_node(new_pos);
+ new_pos= sl->next_select();
+ }
+ last_sl= sl;
+ }
+ if (spec->union_distinct)
+ spec->union_distinct= last_sl;
+ first_recursive= new_pos;
+}
+
+
+/**
+ @brief
Perform context analysis for all unreferenced tables defined in with clause
@param thd The context of the statement containing this with clause
@@ -237,9 +644,9 @@ With_element *With_clause::find_table_def(TABLE_LIST *table)
bool With_clause::prepare_unreferenced_elements(THD *thd)
{
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
{
if (!with_elem->is_referenced() && with_elem->prepare_unreferenced(thd))
return true;
@@ -258,9 +665,9 @@ bool With_clause::prepare_unreferenced_elements(THD *thd)
@param spec_end The end of the specification in the input string
@details
- The method creates for a string copy of the specification used in this element.
- The method is called when the element is parsed. The copy may be used to
- create clones of the specification whenever they are needed.
+ The method creates for a string copy of the specification used in this
+ element. The method is called when the element is parsed. The copy may be
+ used to create clones of the specification whenever they are needed.
@retval
false on success
@@ -438,8 +845,8 @@ With_element::rename_columns_of_derived_unit(THD *thd,
item->is_autogenerated_name= false;
}
}
-
- make_valid_column_names(thd, select->item_list);
+ else
+ make_valid_column_names(thd, select->item_list);
unit->columns_are_renamed= true;
@@ -486,6 +893,12 @@ bool With_element::prepare_unreferenced(THD *thd)
}
+bool With_element::is_anchor(st_select_lex *sel)
+{
+ return !(mutually_recursive & sel->with_dep);
+}
+
+
/**
@brief
Search for the definition of the given table referred in this select node
@@ -493,10 +906,10 @@ bool With_element::prepare_unreferenced(THD *thd)
@param table reference to the table whose definition is searched for
@details
- The method looks for the definition the table whose reference is occurred
+ The method looks for the definition of the table whose reference is occurred
in the FROM list of this select node. First it searches for it in the
with clause attached to the unit this select node belongs to. If such a
- definition is not found there the embedding units are looked through.
+ definition is not found then the embedding units are looked through.
@retval
pointer to the found definition if the search has been successful
@@ -505,17 +918,27 @@ bool With_element::prepare_unreferenced(THD *thd)
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
{
+ st_select_lex_unit *master_unit= NULL;
With_element *found= NULL;
for (st_select_lex *sl= this;
sl;
- sl= sl->master_unit()->outer_select())
+ sl= master_unit->outer_select())
{
+ With_element *with_elem= sl->get_with_element();
+ /*
+ If sl->master_unit() is the spec of a with element then the search for
+ a definition was already done by With_element::check_dependencies_in_spec
+ and it was unsuccesful.
+ */
+ if (with_elem)
+ break;
With_clause *with_clause=sl->get_with_clause();
- if (with_clause && (found= with_clause->find_table_def(table)))
- return found;
+ if (with_clause && (found= with_clause->find_table_def(table,NULL)))
+ break;
+ master_unit= sl->master_unit();
/* Do not look for the table's definition beyond the scope of the view */
- if (sl->master_unit()->is_view)
- break;
+ if (master_unit->is_view)
+ break;
}
return found;
}
@@ -540,7 +963,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
{
with= with_elem;
- if (!with_elem->is_referenced())
+ if (!with_elem->is_referenced() || with_elem->is_recursive)
derived= with_elem->spec;
else
{
@@ -553,6 +976,268 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
}
+bool TABLE_LIST::is_recursive_with_table()
+{
+ return with && with->is_recursive;
+}
+
+
+/*
+ A reference to a with table T is recursive if it occurs somewhere
+ in the query specifying T or in the query specifying one of the tables
+ mutually recursive with T.
+*/
+
+bool TABLE_LIST::is_with_table_recursive_reference()
+{
+ return (with_internal_reference_map &&
+ (with->get_mutually_recursive() & with_internal_reference_map));
+}
+
+
+/*
+ Specifications of with tables with recursive table references
+ in non-mergeable derived tables are not allowed in this
+ implementation.
+*/
+
+
+/*
+ We say that the specification of a with table T is restricted
+ if all below is true.
+ 1. Any immediate select of the specification contains at most one
+ recursive table reference taking into account table references
+ from mergeable derived tables.
+ 2. Any recursive table reference is not an inner operand of an
+ outer join operation used in an immediate select of the
+ specification.
+ 3. Any immediate select from the specification of T does not
+ contain aggregate functions.
+ 4. The specification of T does not contain recursive table references.
+
+ If the specification of T is not restricted we call the corresponding
+ with element unrestricted.
+
+ The SQL standards allows only with elements with restricted specification.
+ By default we comply with the standards here.
+
+ Yet we allow unrestricted specification if the status variable
+ 'standards_compliant_cte' set to 'off'(0).
+*/
+
+
+/**
+ @brief
+ Check if this select makes the including specification unrestricted
+
+ @param
+ only_standards_compliant true if the system variable
+ 'standards_compliant_cte' is set to 'on'
+ @details
+ This method checks whether the conditions 1-4 (see the comment above)
+ are satisfied for this select. If not then mark this element as
+ unrestricted and report an error if 'only_standards_compliant' is true.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
+{
+ With_element *with_elem= get_with_element();
+ if (!with_elem ||!with_elem->is_recursive)
+ {
+ /*
+ If this select is not from the specifiocation of a with elememt or
+ if this not a recursive with element then there is nothing to check.
+ */
+ return false;
+ }
+
+ /* Check conditions 1-2 for restricted specification*/
+ table_map unrestricted= 0;
+ table_map encountered= 0;
+ if (with_elem->check_unrestricted_recursive(this,
+ unrestricted,
+ encountered))
+ return true;
+ with_elem->get_owner()->add_unrestricted(unrestricted);
+
+
+ /* Check conditions 3-4 for restricted specification*/
+ if (with_sum_func ||
+ (with_elem->contains_sq_with_recursive_reference()))
+ with_elem->get_owner()->add_unrestricted(
+ with_elem->get_mutually_recursive());
+
+ /* Report an error on unrestricted specification if this is required */
+ if (only_standards_compliant && with_elem->is_unrestricted())
+ {
+ my_error(ER_NOT_STANDARDS_COMPLIANT_RECURSIVE,
+ MYF(0), with_elem->query_name->str);
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ @brief
+ Check if a select from the spec of this with element is partially restricted
+
+ @param
+ sel select from the specification of this element where to check
+ whether conditions 1-2 are satisfied
+ unrestricted IN/OUT bitmap where to mark unrestricted specs
+ encountered IN/OUT bitmap where to mark encountered recursive references
+ @details
+ This method checks whether the conditions 1-2 (see the comment above)
+ are satisfied for the select sel.
+ This method is called recursively for derived tables.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool With_element::check_unrestricted_recursive(st_select_lex *sel,
+ table_map &unrestricted,
+ table_map &encountered)
+{
+ /* Check conditions 1-for restricted specification*/
+ List_iterator<TABLE_LIST> ti(sel->leaf_tables);
+ TABLE_LIST *tbl;
+ while ((tbl= ti++))
+ {
+ st_select_lex_unit *unit= tbl->get_unit();
+ if (unit)
+ {
+ if(!tbl->is_with_table())
+ {
+ if (tbl->is_materialized_derived())
+ {
+ table_map dep_map;
+ check_dependencies_in_unit(unit, NULL, false, &dep_map);
+ if (dep_map & get_elem_map())
+ {
+ my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED,
+ MYF(0), query_name->str);
+ return true;
+ }
+ }
+ if (check_unrestricted_recursive(unit->first_select(),
+ unrestricted,
+ encountered))
+ return true;
+ }
+ if (!(tbl->is_recursive_with_table() && unit->with_element->owner == owner))
+ continue;
+ With_element *with_elem= unit->with_element;
+ if (encountered & with_elem->get_elem_map())
+ unrestricted|= with_elem->mutually_recursive;
+ else
+ encountered|= with_elem->get_elem_map();
+ }
+ }
+ for (With_element *with_elem= sel->get_with_element()->owner->with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map()))
+ continue;
+ if (encountered & with_elem->get_elem_map())
+ {
+ uint cnt= 0;
+ table_map encountered_mr= encountered & with_elem->mutually_recursive;
+ for (table_map map= encountered_mr >> with_elem->number;
+ map != 0;
+ map>>= 1)
+ {
+ if (map & 1)
+ {
+ if (cnt)
+ {
+ unrestricted|= with_elem->mutually_recursive;
+ break;
+ }
+ else
+ cnt++;
+ }
+ }
+ }
+ }
+
+
+ /* Check conditions 2 for restricted specification*/
+ ti.rewind();
+ while ((tbl= ti++))
+ {
+ for (TABLE_LIST *tab= tbl; tab; tab= tab->embedding)
+ {
+ if (tab->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
+ {
+ unrestricted|= mutually_recursive;
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Check subqueries with recursive table references from FROM list of this select
+
+ @details
+ For each recursive table reference from the FROM list of this select
+ this method checks:
+ - whether this reference is within a materialized derived table and
+ if so it report an error
+ - whether this reference is within a subquery and if so it set a flag
+ in this subquery that disallows some optimization strategies for
+ this subquery.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool st_select_lex::check_subqueries_with_recursive_references()
+{
+ st_select_lex_unit *sl_master= master_unit();
+ List_iterator<TABLE_LIST> ti(leaf_tables);
+ TABLE_LIST *tbl;
+ while ((tbl= ti++))
+ {
+ if (!(tbl->is_with_table_recursive_reference() && sl_master->item))
+ continue;
+ With_element *with_elem= tbl->with;
+ bool check_embedding_materialized_derived= true;
+ for (st_select_lex *sl= this; sl; sl= sl_master->outer_select())
+ {
+ sl_master= sl->master_unit();
+ if (with_elem->get_owner() == sl_master->with_clause)
+ check_embedding_materialized_derived= false;
+ if (check_embedding_materialized_derived && !sl_master->with_element &&
+ sl_master->derived && sl_master->derived->is_materialized_derived())
+ {
+ my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED,
+ MYF(0), with_elem->query_name->str);
+ return true;
+ }
+ if (!sl_master->item)
+ continue;
+ Item_subselect *subq= (Item_subselect *) sl_master->item;
+ subq->with_recursive_reference= true;
+ }
+ }
+ return false;
+}
+
+
/**
@brief
Print this with clause
@@ -567,15 +1252,15 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
void With_clause::print(String *str, enum_query_type query_type)
{
- str->append(STRING_WITH_LEN("WITH "));
+ str->append(STRING_WITH_LEN("with "));
if (with_recursive)
- str->append(STRING_WITH_LEN("RECURSIVE "));
- for (With_element *with_elem= first_elem;
- with_elem != NULL;
- with_elem= with_elem->next_elem)
+ str->append(STRING_WITH_LEN("recursive "));
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
{
with_elem->print(str, query_type);
- if (with_elem != first_elem)
+ if (with_elem != with_list.first)
str->append(", ");
}
}
@@ -596,9 +1281,30 @@ void With_clause::print(String *str, enum_query_type query_type)
void With_element::print(String *str, enum_query_type query_type)
{
str->append(query_name);
- str->append(STRING_WITH_LEN(" AS "));
+ str->append(STRING_WITH_LEN(" as "));
str->append('(');
spec->print(str, query_type);
str->append(')');
}
+
+bool With_element::instantiate_tmp_tables()
+{
+ List_iterator_fast<TABLE> li(rec_result->rec_tables);
+ TABLE *rec_table;
+ while ((rec_table= li++))
+ {
+ if (!rec_table->is_created() &&
+ instantiate_tmp_table(rec_table,
+ rec_result->tmp_table_param.keyinfo,
+ rec_result->tmp_table_param.start_recinfo,
+ &rec_result->tmp_table_param.recinfo,
+ 0))
+ return true;
+
+ rec_table->file->extra(HA_EXTRA_WRITE_CACHE);
+ rec_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ }
+ return false;
+}
+
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index 761d01ccd7f..89223170261 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -2,32 +2,57 @@
#define SQL_CTE_INCLUDED
#include "sql_list.h"
#include "sql_lex.h"
+#include "sql_select.h"
-class With_clause;
+class select_union;
+struct st_unit_ctxt_elem;
-/**
- @class With_clause
- @brief Set of with_elements
- It has a reference to the first with element from this with clause.
- This reference allows to navigate through all the elements of the with clause.
- It contains a reference to the unit to which this with clause is attached.
- It also contains a flag saying whether this with clause was specified as recursive.
-*/
+/**
+ @class With_element
+ @brief Definition of a CTE table
+
+ It contains a reference to the name of the table introduced by this with element,
+ and a reference to the unit that specificies this table. Also it contains
+ a reference to the with clause to which this element belongs to.
+*/
class With_element : public Sql_alloc
{
private:
With_clause *owner; // with clause this object belongs to
- With_element *next_elem; // next element in the with clause
+ With_element *next; // next element in the with clause
uint number; // number of the element in the with clause (starting from 0)
+ table_map elem_map; // The map where with only one 1 set in this->number
/*
- The map dependency_map has 1 in the i-th position if the query that
- specifies this element contains a reference to the element number i
+ The map base_dep_map has 1 in the i-th position if the query that
+ specifies this with element contains a reference to the with element number i
in the query FROM list.
+ (In this case this with element depends directly on the i-th with element.)
*/
- table_map elem_map; // The map where with only one 1 set in this->number
- table_map dependency_map;
+ table_map base_dep_map;
+ /*
+ The map derived_dep_map has 1 in i-th position if this with element depends
+ directly or indirectly from the i-th with element.
+ */
+ table_map derived_dep_map;
+ /*
+ The map sq_dep_map has 1 in i-th position if there is a reference to this
+ with element somewhere in subqueries of the specifications of the tables
+ defined in the with clause containing this element;
+ */
+ table_map sq_dep_map;
+ table_map work_dep_map; // dependency map used for work
+ /* Dependency map of with elements mutually recursive with this with element */
+ table_map mutually_recursive;
+ /*
+ The next with element from the circular chain of the with elements
+ mutually recursive with this with element.
+ (If This element is simply recursive than next_mutually_recursive contains
+ the pointer to itself. If it's not recursive than next_mutually_recursive
+ is set to NULL.)
+ */
+ With_element *next_mutually_recursive;
/*
Total number of references to this element in the FROM lists of
the queries that are in the scope of the element (including
@@ -64,20 +89,67 @@ public:
*/
bool is_recursive;
+ /*
+ Any non-recursive select in the specification of a recursive
+ with element is a called anchor. In the case mutually recursive
+ elements the specification of some them may be without any anchor.
+ Yet at least one of them must contain an anchor.
+ All anchors of any recursivespecification are moved ahead before
+ the prepare stage.
+ */
+ /* Set to true if this is a recursive element with an anchor */
+ bool with_anchor;
+ /*
+ Set to the first recursive select of the unit specifying the element
+ after all anchor have been moved to the head of the unit.
+ */
+ st_select_lex *first_recursive;
+
+ /*
+ The number of the last performed iteration for recursive table
+ (the number of the initial non-recursive step is 0, the number
+ of the first iteration is 1).
+ */
+ uint level;
+
+ /*
+ The pointer to the object used to materialize this with element
+ if it's recursive. This object is built at the end of prepare
+ stage and is used at the execution stage.
+ */
+ select_union_recursive *rec_result;
+
With_element(LEX_STRING *name,
List <LEX_STRING> list,
st_select_lex_unit *unit)
- : next_elem(NULL), dependency_map(0), references(0),
+ : next(NULL), base_dep_map(0), derived_dep_map(0),
+ sq_dep_map(0), work_dep_map(0), mutually_recursive(0),
+ next_mutually_recursive(NULL), references(0),
query_name(name), column_list(list), spec(unit),
- is_recursive(false) {}
+ is_recursive(false), with_anchor(false),
+ level(0), rec_result(NULL)
+ {}
+
+ bool check_dependencies_in_spec();
+
+ void check_dependencies_in_select(st_select_lex *sl, st_unit_ctxt_elem *ctxt,
+ bool in_subq, table_map *dep_map);
+
+ void check_dependencies_in_unit(st_select_lex_unit *unit,
+ st_unit_ctxt_elem *ctxt,
+ bool in_subq,
+ table_map *dep_map);
- void check_dependencies_in_unit(st_select_lex_unit *unit);
+ void check_dependencies_in_with_clause(With_clause *with_clause,
+ st_unit_ctxt_elem *ctxt,
+ bool in_subq,
+ table_map *dep_map);
void set_dependency_on(With_element *with_elem)
- { dependency_map|= with_elem->get_elem_map(); }
+ { base_dep_map|= with_elem->get_elem_map(); }
bool check_dependency_on(With_element *with_elem)
- { return dependency_map & with_elem->get_elem_map(); }
+ { return base_dep_map & with_elem->get_elem_map(); }
bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end);
@@ -91,28 +163,72 @@ public:
bool prepare_unreferenced(THD *thd);
+ bool check_unrestricted_recursive(st_select_lex *sel,
+ table_map &unrestricted,
+ table_map &encountered);
+
void print(String *str, enum_query_type query_type);
+ With_clause *get_owner() { return owner; }
+
+ bool contains_sq_with_recursive_reference()
+ { return sq_dep_map & mutually_recursive; }
+
+ table_map get_mutually_recursive() { return mutually_recursive; }
+
+ With_element *get_next_mutually_recursive()
+ { return next_mutually_recursive; }
+
+ bool is_anchor(st_select_lex *sel);
+
+ void move_anchors_ahead();
+
+ bool is_unrestricted();
+
+ bool is_with_prepared_anchor();
+
+ void mark_as_with_prepared_anchor();
+
+ bool is_cleaned();
+
+ void mark_as_cleaned();
+
+ void reset_recursive_for_exec();
+
+ void cleanup_stabilized();
+
+ void set_as_stabilized();
+
+ bool is_stabilized();
+
+ bool all_are_stabilized();
+
+ bool instantiate_tmp_tables();
+
+ void prepare_for_next_iteration();
+
friend class With_clause;
};
+const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8;
/**
- @class With_element
- @brief Definition of a CTE table
-
- It contains a reference to the name of the table introduced by this with element,
- and a reference to the unit that specificies this table. Also it contains
- a reference to the with clause to which this element belongs to.
-*/
+ @class With_clause
+ @brief Set of with_elements
+
+ It has a reference to the first with element from this with clause.
+ This reference allows to navigate through all the elements of the with clause.
+ It contains a reference to the unit to which this with clause is attached.
+ It also contains a flag saying whether this with clause was specified as recursive.
+*/
class With_clause : public Sql_alloc
{
private:
st_select_lex_unit *owner; // the unit this with clause attached to
- With_element *first_elem; // the first definition in this with clause
- With_element **last_next; // here is set the link for the next added element
- uint elements; // number of the elements/defintions in this with clauses
+
+ /* The list of all with elements from this with clause */
+ SQL_I_List<With_element> with_list;
/*
The with clause immediately containing this with clause if there is any,
otherwise NULL. Now used only at parsing.
@@ -126,29 +242,37 @@ private:
/* Set to true if dependencies between with elements have been checked */
bool dependencies_are_checked;
+ /*
+ The bitmap of all recursive with elements whose specifications
+ are not complied with restrictions imposed by the SQL standards
+ on recursive specifications.
+ */
+ table_map unrestricted;
+ /*
+ The bitmap of all recursive with elements whose anchors
+ has been already prepared.
+ */
+ table_map with_prepared_anchor;
+ table_map cleaned;
+ /*
+ The bitmap of all recursive with elements that
+ has been already materialized
+ */
+ table_map stabilized;
+
public:
/* If true the specifier RECURSIVE is present in the with clause */
bool with_recursive;
With_clause(bool recursive_fl, With_clause *emb_with_clause)
- : owner(NULL), first_elem(NULL), elements(0),
+ : owner(NULL),
embedding_with_clause(emb_with_clause), next_with_clause(NULL),
- dependencies_are_checked(false),
+ dependencies_are_checked(false), unrestricted(0),
+ with_prepared_anchor(0), cleaned(0), stabilized(0),
with_recursive(recursive_fl)
- { last_next= &first_elem; }
-
- /* Add a new element to the current with clause */
- bool add_with_element(With_element *elem)
- {
- elem->owner= this;
- elem->number= elements;
- owner= elem->spec;
- owner->with_element= elem;
- *last_next= elem;
- last_next= &elem->next_elem;
- elements++;
- return false;
- }
+ { }
+
+ bool add_with_element(With_element *elem);
/* Add this with clause to the list of with clauses used in the statement */
void add_to_list(With_clause ** &last_next)
@@ -157,22 +281,136 @@ public:
last_next= &this->next_with_clause;
}
+ void set_owner(st_select_lex_unit *unit) { owner= unit; }
+
With_clause *pop() { return embedding_with_clause; }
bool check_dependencies();
- With_element *find_table_def(TABLE_LIST *table);
+ bool check_anchors();
+
+ void move_anchors_ahead();
+
+ With_element *find_table_def(TABLE_LIST *table, With_element *barrier);
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
bool prepare_unreferenced_elements(THD *thd);
+ void add_unrestricted(table_map map) { unrestricted|= map; }
+
void print(String *str, enum_query_type query_type);
- friend
- bool check_dependencies_in_with_clauses(With_clause *with_clauses_list);
+ friend class With_element;
+ friend
+ bool
+ check_dependencies_in_with_clauses(With_clause *with_clauses_list);
};
+inline
+bool With_element::is_unrestricted()
+{
+ return owner->unrestricted & get_elem_map();
+}
+
+inline
+
+bool With_element::is_with_prepared_anchor()
+{
+ return owner->with_prepared_anchor & get_elem_map();
+}
+
+inline
+void With_element::mark_as_with_prepared_anchor()
+{
+ owner->with_prepared_anchor|= mutually_recursive;
+}
+
+
+inline
+bool With_element::is_cleaned()
+{
+ return owner->cleaned & get_elem_map();
+}
+
+
+inline
+void With_element::mark_as_cleaned()
+{
+ owner->cleaned|= get_elem_map();
+}
+
+
+inline
+void With_element::reset_recursive_for_exec()
+{
+ DBUG_ASSERT(is_recursive);
+ level= 0;
+ owner->with_prepared_anchor&= ~mutually_recursive;
+ owner->cleaned&= ~get_elem_map();
+ cleanup_stabilized();
+ rec_result->first_rec_table_to_update= 0;
+}
+
+
+
+inline
+void With_element::cleanup_stabilized()
+{
+ owner->stabilized&= ~mutually_recursive;
+}
+
+
+inline
+void With_element::set_as_stabilized()
+{
+ owner->stabilized|= get_elem_map();
+}
+
+
+inline
+bool With_element::is_stabilized()
+{
+ return owner->stabilized & get_elem_map();
+}
+
+
+inline
+bool With_element::all_are_stabilized()
+{
+ return (owner->stabilized & mutually_recursive) == mutually_recursive;
+}
+
+
+inline
+void With_element::prepare_for_next_iteration()
+{
+ With_element *with_elem= this;
+ while ((with_elem= with_elem->get_next_mutually_recursive()) != this)
+ {
+ TABLE *rec_table= with_elem->rec_result->first_rec_table_to_update;
+ if (rec_table)
+ rec_table->reginfo.join_tab->preread_init_done= false;
+ }
+}
+
+
+inline
+void st_select_lex_unit::set_with_clause(With_clause *with_cl)
+{
+ with_clause= with_cl;
+ if (with_clause)
+ with_clause->set_owner(this);
+}
+
+
+inline
+void st_select_lex::set_with_clause(With_clause *with_clause)
+{
+ master_unit()->with_clause= with_clause;
+ if (with_clause)
+ with_clause->set_owner(master_unit());
+}
#endif /* SQL_CTE_INCLUDED */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 1a0ee03ec34..20538fe1fb4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1035,7 +1035,10 @@ exit:
it to 0.
*/
if (thd->db && cmp_db_names(thd->db, db) && !error)
+ {
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL);
+ }
my_dirend(dirp);
DBUG_RETURN(error);
}
@@ -1459,7 +1462,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
- DBUG_RETURN(FALSE);
+ goto done;
}
else
{
@@ -1476,8 +1479,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL,
system_charset_info);
-
- DBUG_RETURN(FALSE);
+ goto done;
}
/*
@@ -1564,8 +1566,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
/* The operation succeed. */
-
- DBUG_RETURN(FALSE);
+ goto done;
}
else
{
@@ -1589,6 +1590,9 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
+done:
+ SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL);
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 95aee805c7f..4f9afca2f6d 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -27,7 +27,6 @@
#include "sql_delete.h"
#include "sql_cache.h" // query_cache_*
#include "sql_base.h" // open_temprary_table
-#include "sql_table.h" // build_table_filename
#include "lock.h" // unlock_table_name
#include "sql_view.h" // check_key_in_view, mysql_frm_type
#include "sql_parse.h" // mysql_init_select
@@ -488,6 +487,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
dbug_serve_apcs(thd, 1););
+ if (!(select && select->quick))
+ status_var_increment(thd->status_var.delete_scan_count);
+
if (query_plan.using_filesort)
{
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 05e07a48b49..85a85b9c3b2 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -30,6 +30,7 @@
#include "sql_base.h"
#include "sql_view.h" // check_duplicate_names
#include "sql_acl.h" // SELECT_ACL
+#include "sql_class.h"
#include "sql_cte.h"
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
@@ -479,8 +480,6 @@ unconditional_materialization:
derived->set_materialized_derived();
if (!derived->table || !derived->table->is_created())
res= mysql_derived_create(thd, lex, derived);
- if (!res)
- res= mysql_derived_fill(thd, lex, derived);
goto exit_merge;
}
@@ -631,6 +630,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
true Error
*/
+
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
@@ -638,24 +638,70 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
bool res= FALSE;
DBUG_PRINT("enter", ("unit 0x%lx", (ulong) unit));
+ if (!unit)
+ DBUG_RETURN(FALSE);
+
+ SELECT_LEX *first_select= unit->first_select();
+
+ if (unit->prepared && derived->is_recursive_with_table() &&
+ !derived->table)
+ {
+ /*
+ Here 'derived' is either a non-recursive table reference to a recursive
+ with table or a recursive table reference to a recursvive table whose
+ specification has been already prepared (a secondary recursive table
+ reference.
+ */
+ if (!(derived->derived_result= new (thd->mem_root) select_union(thd)))
+ DBUG_RETURN(TRUE); // out of memory
+ thd->create_tmp_table_for_derived= TRUE;
+ res= derived->derived_result->create_result_table(
+ thd, &unit->types, FALSE,
+ (first_select->options |
+ thd->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS),
+ derived->alias, FALSE, FALSE);
+ thd->create_tmp_table_for_derived= FALSE;
+
+ if (!res && !derived->table)
+ {
+ derived->derived_result->set_unit(unit);
+ derived->table= derived->derived_result->table;
+ if (derived->is_with_table_recursive_reference())
+ {
+ /* Here 'derived" is a secondary recursive table reference */
+ unit->with_element->rec_result->rec_tables.push_back(derived->table);
+ }
+ }
+ DBUG_ASSERT(derived->table || res);
+ goto exit;
+ }
+
// Skip already prepared views/DT
- if (!unit || unit->prepared ||
+ if (unit->prepared ||
(derived->merged_for_insert &&
!(derived->is_multitable() &&
(thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
DBUG_RETURN(FALSE);
- SELECT_LEX *first_select= unit->first_select();
-
/* prevent name resolving out of derived table */
for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
{
sl->context.outer_context= 0;
- // Prepare underlying views/DT first.
- if ((res= sl->handle_derived(lex, DT_PREPARE)))
- goto exit;
-
+ if (!derived->is_with_table_recursive_reference() ||
+ (!derived->with->with_anchor &&
+ !derived->with->is_with_prepared_anchor()))
+ {
+ /*
+ Prepare underlying views/DT first unless 'derived' is a recursive
+ table reference and either the anchors from the specification of
+ 'derived' has been already prepared or there no anchor in this
+ specification
+ */
+ if ((res= sl->handle_derived(lex, DT_PREPARE)))
+ goto exit;
+ }
if (derived->outer_join && sl->first_cond_optimization)
{
/* Mark that table is part of OUTER JOIN and fields may be NULL */
@@ -701,19 +747,21 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
SELECT is last SELECT of UNION).
*/
thd->create_tmp_table_for_derived= TRUE;
- if (derived->derived_result->create_result_table(thd, &unit->types, FALSE,
- (first_select->options |
- thd->variables.option_bits |
- TMP_TABLE_ALL_COLUMNS),
- derived->alias,
- FALSE, FALSE))
+ if (!(derived->table) &&
+ derived->derived_result->create_result_table(thd, &unit->types, FALSE,
+ (first_select->options |
+ thd->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS),
+ derived->alias,
+ FALSE, FALSE, FALSE))
{
thd->create_tmp_table_for_derived= FALSE;
goto exit;
}
thd->create_tmp_table_for_derived= FALSE;
- derived->table= derived->derived_result->table;
+ if (!derived->table)
+ derived->table= derived->derived_result->table;
DBUG_ASSERT(derived->table);
if (derived->is_derived() && derived->is_merged_derived())
first_select->mark_as_belong_to_derived(derived);
@@ -740,7 +788,7 @@ exit:
*/
if (res)
{
- if (derived->table)
+ if (derived->table && !derived->is_with_table_recursive_reference())
free_tmp_table(thd, derived->table);
delete derived->derived_result;
}
@@ -760,8 +808,11 @@ exit:
}
#endif
/* Add new temporary table to list of open derived tables */
- table->next= thd->derived_tables;
- thd->derived_tables= table;
+ if (!derived->is_with_table_recursive_reference())
+ {
+ table->next= thd->derived_tables;
+ thd->derived_tables= table;
+ }
/* If table is used by a left join, mark that any column may be null */
if (derived->outer_join)
@@ -865,7 +916,7 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
if (table->is_created())
DBUG_RETURN(FALSE);
- select_union *result= (select_union*)unit->result;
+ select_union *result= derived->derived_result;
if (table->s->db_type() == TMP_ENGINE_HTON)
{
result->tmp_table_param.keyinfo= table->s->key_info;
@@ -884,6 +935,45 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
}
+/**
+ @brief
+ Fill the recursive with table
+
+ @param thd The thread handle
+
+ @details
+ The method is called only for recursive with tables.
+ The method executes the recursive part of the specification
+ of this with table until no more rows are added to the table
+ or the number of the performed iteration reaches the allowed
+ maximum.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool TABLE_LIST::fill_recursive(THD *thd)
+{
+ bool rc= false;
+ st_select_lex_unit *unit= get_unit();
+ rc= with->instantiate_tmp_tables();
+ while (!rc && !with->all_are_stabilized())
+ {
+ if (with->level > thd->variables.max_recursive_iterations)
+ break;
+ with->prepare_for_next_iteration();
+ rc= unit->exec_recursive();
+ }
+ if (!rc)
+ {
+ TABLE *src= with->rec_result->table;
+ rc =src->insert_all_rows_into(thd, table, true);
+ }
+ return rc;
+}
+
+
/*
Execute subquery of a materialized derived table/view and fill the result
table.
@@ -894,9 +984,10 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
@details
Execute subquery of given 'derived' table/view and fill the result
- table. After result table is filled, if this is not the EXPLAIN statement,
- the entire unit / node is deleted. unit is deleted if UNION is used
- for derived table and node is deleted is it is a simple SELECT.
+ table. After result table is filled, if this is not the EXPLAIN statement
+ and the table is not specified with a recursion the entire unit / node
+ is deleted. unit is deleted if UNION is used for derived table and node
+ is deleted is it is a simple SELECT.
'lex' is unused and 'thd' is passed as an argument to an underlying function.
@note
@@ -907,26 +998,43 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
@return TRUE Error
*/
+
bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_fill");
SELECT_LEX_UNIT *unit= derived->get_unit();
+ bool derived_is_recursive= derived->is_recursive_with_table();
bool res= FALSE;
- if (unit->executed && !unit->uncacheable && !unit->describe)
+ if (unit->executed && !unit->uncacheable && !unit->describe &&
+ !derived_is_recursive)
DBUG_RETURN(FALSE);
/*check that table creation passed without problems. */
DBUG_ASSERT(derived->table && derived->table->is_created());
- SELECT_LEX *first_select= unit->first_select();
select_union *derived_result= derived->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
- if (unit->is_union())
+
+ if (derived_is_recursive)
+ {
+ if (derived->is_with_table_recursive_reference())
+ {
+ /* Here only one iteration step is performed */
+ res= unit->exec_recursive();
+ }
+ else
+ {
+ /* In this case all iteration are performed */
+ res= derived->fill_recursive(thd);
+ }
+ }
+ else if (unit->is_union())
{
// execute union without clean up
res= unit->exec();
}
else
{
+ SELECT_LEX *first_select= unit->first_select();
unit->set_limit(unit->global_parameters());
if (unit->select_limit_cnt == HA_POS_ERROR)
first_select->options&= ~OPTION_FOUND_ROWS;
@@ -946,13 +1054,13 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
derived_result, unit, first_select);
}
- if (!res)
+ if (!res && !derived_is_recursive)
{
if (derived_result->flush())
res= TRUE;
unit->executed= TRUE;
}
- if (res || !lex->describe)
+ if (res || (!lex->describe && !derived_is_recursive))
unit->cleanup();
lex->current_select= save_current_select;
@@ -990,3 +1098,155 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
+
+
+/**
+ @brief
+ Extract the condition depended on derived table/view and pushed it there
+
+ @param thd The thread handle
+ @param cond The condition from which to extract the pushed condition
+ @param derived The reference to the derived table/view
+
+ @details
+ This functiom builds the most restrictive condition depending only on
+ the derived table/view that can be extracted from the condition cond.
+ The built condition is pushed into the having clauses of the
+ selects contained in the query specifying the derived table/view.
+ The function also checks for each select whether any condition depending
+ only on grouping fields can be extracted from the pushed condition.
+ If so, it pushes the condition over grouping fields into the where
+ clause of the select.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
+{
+ if (!cond)
+ return false;
+
+ st_select_lex_unit *unit= derived->get_unit();
+ st_select_lex *sl= unit->first_select();
+
+ /* Check whether any select of 'unit' allows condition pushdown */
+ bool any_select_allows_cond_pushdown= false;
+ for (; sl; sl= sl->next_select())
+ {
+ if (sl->cond_pushdown_is_allowed())
+ {
+ any_select_allows_cond_pushdown= true;
+ break;
+ }
+ }
+ if (!any_select_allows_cond_pushdown)
+ return false;
+
+ /* Do not push conditions into constant derived */
+ if (unit->executed)
+ return false;
+
+ /* Do not push conditions into recursive with tables */
+ if (derived->is_recursive_with_table())
+ return false;
+
+ /*
+ Build the most restrictive condition extractable from 'cond'
+ that can be pushed into the derived table 'derived'.
+ All subexpressions of this condition are cloned from the
+ subexpressions of 'cond'.
+ This condition has to be fixed yet.
+ */
+ Item *extracted_cond;
+ derived->check_pushable_cond_for_table(cond);
+ extracted_cond= derived->build_pushable_cond_for_table(thd, cond);
+ if (!extracted_cond)
+ {
+ /* Nothing can be pushed into the derived table */
+ return false;
+ }
+ /* Push extracted_cond into every select of the unit specifying 'derived' */
+ st_select_lex *save_curr_select= thd->lex->current_select;
+ for (; sl; sl= sl->next_select())
+ {
+ if (!sl->cond_pushdown_is_allowed())
+ continue;
+ thd->lex->current_select= sl;
+ /*
+ For each select of the unit except the last one
+ create a clone of extracted_cond
+ */
+ Item *extracted_cond_copy= !sl->next_select() ? extracted_cond :
+ extracted_cond->build_clone(thd, thd->mem_root);
+ if (!extracted_cond_copy)
+ continue;
+
+ if (!sl->join->group_list && !sl->with_sum_func)
+ {
+ /* extracted_cond_copy is pushed into where of sl */
+ extracted_cond_copy= extracted_cond_copy->transform(thd,
+ &Item::derived_field_transformer_for_where,
+ (uchar*) sl);
+ if (extracted_cond_copy)
+ {
+ extracted_cond_copy->walk(&Item::cleanup_processor, 0, 0);
+ sl->cond_pushed_into_where= extracted_cond_copy;
+ }
+
+ continue;
+ }
+
+ /*
+ Figure out what can be extracted from the pushed condition
+ that could be pushed into the where clause of sl
+ */
+ Item *cond_over_grouping_fields;
+ sl->collect_grouping_fields(thd);
+ sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
+ &Item::exclusive_dependence_on_grouping_fields_processor);
+ cond_over_grouping_fields=
+ sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
+
+ /*
+ Transform the references to the 'derived' columns from the condition
+ pushed into the where clause of sl to make them usable in the new context
+ */
+ if (cond_over_grouping_fields)
+ cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
+ &Item::derived_grouping_field_transformer_for_where,
+ (uchar*) sl);
+
+ if (cond_over_grouping_fields)
+ {
+ /*
+ In extracted_cond_copy remove top conjuncts that
+ has been pushed into the where clause of sl
+ */
+ extracted_cond_copy= remove_pushed_top_conjuncts(thd, extracted_cond_copy);
+
+ cond_over_grouping_fields->walk(&Item::cleanup_processor, 0, 0);
+ sl->cond_pushed_into_where= cond_over_grouping_fields;
+
+ if (!extracted_cond_copy)
+ continue;
+ }
+
+ /*
+ Transform the references to the 'derived' columns from the condition
+ pushed into the having clause of sl to make them usable in the new context
+ */
+ extracted_cond_copy= extracted_cond_copy->transform(thd,
+ &Item::derived_field_transformer_for_having,
+ (uchar*) sl);
+ if (!extracted_cond_copy)
+ continue;
+
+ extracted_cond_copy->walk(&Item::cleanup_processor, 0, 0);
+ sl->cond_pushed_into_having= extracted_cond_copy;
+ }
+ thd->lex->current_select= save_curr_select;
+ return false;
+}
+
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index 1dffef7235b..c451e423032 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -37,4 +37,12 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
*/
bool mysql_derived_cleanup(THD *thd, LEX *lex, TABLE_LIST *derived);
+Item *delete_not_needed_parts(THD *thd, Item *cond);
+
+#if 0
+bool pushdown_cond_for_derived(THD *thd, Item **cond, TABLE_LIST *derived);
+#else
+bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived);
+#endif
+
#endif /* SQL_DERIVED_INCLUDED */
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 1f8b4f2dcb1..131c5a3bcfa 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -547,7 +547,12 @@ void Explain_union::print_explain_json(Explain_query *query,
bool started_object= print_explain_json_cache(writer, is_analyze);
writer->add_member("query_block").start_object();
- writer->add_member("union_result").start_object();
+
+ if (is_recursive_cte)
+ writer->add_member("recursive_union").start_object();
+ else
+ writer->add_member("union_result").start_object();
+
// using_temporary_table
make_union_table_name(table_name_buffer);
writer->add_member("table_name").add_str(table_name_buffer);
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index abdb1bb978b..5793599f4e1 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -327,6 +327,7 @@ class Explain_union : public Explain_node
public:
Explain_union(MEM_ROOT *root, bool is_analyze) :
Explain_node(root),
+ is_recursive_cte(false),
fake_select_lex_explain(root, is_analyze)
{}
@@ -362,6 +363,7 @@ public:
const char *fake_select_type;
bool using_filesort;
bool using_tmp;
+ bool is_recursive_cte;
/*
Explain data structure for "fake_select_lex" (i.e. for the degenerate
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 80287793175..3025c6e36dc 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2489,8 +2489,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
(*field)->default_value= vcol;
*dfield_ptr++= *field;
}
- if ((*field)->has_insert_default_function() ||
- (*field)->has_update_default_function())
+ else
+ if ((*field)->has_update_default_function())
*dfield_ptr++= *field;
}
if (vfield)
@@ -3348,6 +3348,7 @@ bool Delayed_insert::handle_inserts(void)
max_rows= 0; // For DBUG output
#endif
/* Remove all not used rows */
+ mysql_mutex_lock(&mutex);
while ((row=rows.get()))
{
if (table->s->blob_fields)
@@ -3364,7 +3365,6 @@ bool Delayed_insert::handle_inserts(void)
}
DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows));
thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status);
- mysql_mutex_lock(&mutex);
DBUG_RETURN(1);
}
#endif /* EMBEDDED_LIBRARY */
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 638f6832355..4d161b32577 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2063,6 +2063,7 @@ void st_select_lex_unit::init_query()
offset_limit_cnt= 0;
union_distinct= 0;
prepared= optimized= executed= 0;
+ optimize_started= 0;
item= 0;
union_result= 0;
table= 0;
@@ -2092,6 +2093,7 @@ void st_select_lex::init_query()
item_list.empty();
join= 0;
having= prep_having= where= prep_where= 0;
+ cond_pushed_into_where= cond_pushed_into_having= 0;
olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0;
context.select_lex= this;
@@ -2168,7 +2170,9 @@ void st_select_lex::init_select()
m_non_agg_field_used= false;
m_agg_func_used= false;
name_visibility_map= 0;
+ with_dep= 0;
join= 0;
+ lock_type= TL_READ_DEFAULT;
}
/*
@@ -2454,7 +2458,6 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last,
return FALSE;
}
-bool st_select_lex_node::set_braces(bool value) { return 1; }
bool st_select_lex_node::inc_in_sum_expr() { return 1; }
uint st_select_lex_node::get_in_sum_expr() { return 0; }
TABLE_LIST* st_select_lex_node::get_table_list() { return 0; }
@@ -2604,13 +2607,6 @@ st_select_lex* st_select_lex::outer_select()
}
-bool st_select_lex::set_braces(bool value)
-{
- braces= value;
- return 0;
-}
-
-
bool st_select_lex::inc_in_sum_expr()
{
in_sum_expr++;
@@ -3184,6 +3180,8 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
bool st_select_lex_unit::union_needs_tmp_table()
{
+ if (with_element && with_element->is_recursive)
+ return true;
return union_distinct != NULL ||
global_parameters()->order_list.elements != 0 ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
@@ -4231,6 +4229,7 @@ void st_select_lex::update_correlated_cache()
while ((tl= ti++))
{
+ // is_correlated|= tl->is_with_table_recursive_reference();
if (tl->on_expr)
is_correlated|= MY_TEST(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
for (TABLE_LIST *embedding= tl->embedding ; embedding ;
@@ -4361,7 +4360,26 @@ void st_select_lex::set_explain_type(bool on_the_fly)
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
if (this == master_unit()->fake_select_lex)
type= "UNION RESULT";
-
+ /*
+ join below may be =NULL when this functions is called at an early
+ stage. It will be later called again and we will set the correct
+ value.
+ */
+ if (join)
+ {
+ bool uses_cte= false;
+ for (JOIN_TAB *tab= first_explain_order_tab(join); tab;
+ tab= next_explain_order_tab(join, tab))
+ {
+ if (tab->table && tab->table->pos_in_table_list->with)
+ {
+ uses_cte= true;
+ break;
+ }
+ }
+ if (uses_cte)
+ type= "RECURSIVE UNION";
+ }
}
}
}
@@ -4387,6 +4405,19 @@ void SELECT_LEX::increase_derived_records(ha_rows records)
SELECT_LEX_UNIT *unit= master_unit();
DBUG_ASSERT(unit->derived);
+ if (unit->with_element && unit->with_element->is_recursive)
+ {
+ st_select_lex *first_recursive= unit->with_element->first_recursive;
+ st_select_lex *sl= unit->first_select();
+ for ( ; sl != first_recursive; sl= sl->next_select())
+ {
+ if (sl == this)
+ break;
+ }
+ if (sl == first_recursive)
+ return;
+ }
+
select_union *result= (select_union*)unit->result;
result->records+= records;
}
@@ -4663,7 +4694,9 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
new (output->mem_root) Explain_union(output->mem_root,
thd->lex->analyze_stmt);
-
+ if (with_element && with_element->is_recursive)
+ eu->is_recursive_cte= true;
+
if (derived)
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
/*
@@ -4876,3 +4909,192 @@ void binlog_unsafe_map_init()
}
#endif
+
+/**
+ @brief
+ Finding fiels that are used in the GROUP BY of this st_select_lex
+
+ @param thd The thread handle
+
+ @details
+ This method looks through the fields which are used in the GROUP BY of this
+ st_select_lex and saves this fields.
+*/
+
+void st_select_lex::collect_grouping_fields(THD *thd)
+{
+ grouping_tmp_fields.empty();
+ List_iterator<Item> li(join->fields_list);
+ Item *item= li++;
+ for (uint i= 0; i < master_unit()->derived->table->s->fields; i++, (item=li++))
+ {
+ for (ORDER *ord= join->group_list; ord; ord= ord->next)
+ {
+ if ((*ord->item)->eq((Item*)item, 0))
+ {
+ Grouping_tmp_field *grouping_tmp_field=
+ new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
+ grouping_tmp_fields.push_back(grouping_tmp_field);
+ }
+ }
+ }
+}
+
+/**
+ @brief
+ For a condition check possibility of exraction a formula over grouping fields
+
+ @param cond The condition whose subformulas are to be analyzed
+
+ @details
+ This method traverses the AND-OR condition cond and for each subformula of
+ the condition it checks whether it can be usable for the extraction of a
+ condition over the grouping fields of this select. The method uses
+ the call-back parameter check_processor to ckeck whether a primary formula
+ depends only on grouping fields.
+ The subformulas that are not usable are marked with the flag NO_EXTRACTION_FL.
+ The subformulas that can be entierly extracted are marked with the flag
+ FULL_EXTRACTION_FL.
+ @note
+ This method is called before any call of extract_cond_for_grouping_fields.
+ The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
+ for the subformula when extracting the pushable condition.
+ The flag FULL_EXTRACTION_FL allows to delete later all top level conjuncts
+ from cond.
+*/
+
+void st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
+ Item_processor check_processor)
+{
+ cond->clear_extraction_flag();
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
+ List<Item> *arg_list= ((Item_cond*) cond)->argument_list();
+ List_iterator<Item> li(*arg_list);
+ uint count= 0; // to count items not containing NO_EXTRACTION_FL
+ uint count_full= 0; // to count items with FULL_EXTRACTION_FL
+ Item *item;
+ while ((item=li++))
+ {
+ check_cond_extraction_for_grouping_fields(item, check_processor);
+ if (item->get_extraction_flag() != NO_EXTRACTION_FL)
+ {
+ count++;
+ if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
+ count_full++;
+ }
+ else if (!and_cond)
+ break;
+ }
+ if ((and_cond && count == 0) || item)
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+ if (count_full == arg_list->elements)
+ cond->set_extraction_flag(FULL_EXTRACTION_FL);
+ if (cond->get_extraction_flag() != 0)
+ {
+ li.rewind();
+ while ((item=li++))
+ item->clear_extraction_flag();
+ }
+ }
+ else
+ cond->set_extraction_flag(cond->walk(check_processor,
+ 0, (uchar *) this) ?
+ NO_EXTRACTION_FL : FULL_EXTRACTION_FL);
+}
+
+
+/**
+ @brief
+ Build condition extractable from the given one depended on grouping fields
+
+ @param thd The thread handle
+ @param cond The condition from which the condition depended
+ on grouping fields is to be extracted
+ @param no_top_clones If it's true then no clones for the top fully
+ extractable conjuncts are built
+
+ @details
+ For the given condition cond this method finds out what condition depended
+ only on the grouping fields can be extracted from cond. If such condition C
+ exists the method builds the item for it.
+ This method uses the flags NO_EXTRACTION_FL and FULL_EXTRACTION_FL set by the
+ preliminary call of st_select_lex::check_cond_extraction_for_grouping_fields
+ to figure out whether a subformula depends only on these fields or not.
+ @note
+ The built condition C is always implied by the condition cond
+ (cond => C). The method tries to build the most restictive such
+ condition (i.e. for any other condition C' such that cond => C'
+ we have C => C').
+ @note
+ The build item is not ready for usage: substitution for the field items
+ has to be done and it has to be re-fixed.
+
+ @retval
+ the built condition depended only on grouping fields if such a condition exists
+ NULL if there is no such a condition
+*/
+
+Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
+ bool no_top_clones)
+{
+ if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ if (no_top_clones)
+ return cond;
+ cond->clear_extraction_flag();
+ return cond->build_clone(thd, thd->mem_root);
+ }
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool cond_and= false;
+ Item_cond *new_cond;
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ cond_and= true;
+ new_cond= new (thd->mem_root) Item_cond_and(thd);
+ }
+ else
+ new_cond= new (thd->mem_root) Item_cond_or(thd);
+ if (!new_cond)
+ return 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ DBUG_ASSERT(cond_and);
+ item->clear_extraction_flag();
+ continue;
+ }
+ Item *fix= build_cond_for_grouping_fields(thd, item,
+ no_top_clones & cond_and);
+ if (!fix)
+ {
+ if (cond_and)
+ continue;
+ break;
+ }
+ new_cond->argument_list()->push_back(fix, thd->mem_root);
+ }
+
+ if (!cond_and && item)
+ {
+ while((item= li++))
+ item->clear_extraction_flag();
+ return 0;
+ }
+ switch (new_cond->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ return new_cond;
+ }
+ }
+ return 0;
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 6d706e0eab3..e499cc794a1 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -30,6 +30,7 @@
#include "sql_alter.h" // Alter_info
#include "sql_window.h"
+
/* YACC and LEX Definitions */
/* These may not be declared yet */
@@ -530,7 +531,6 @@ public:
virtual st_select_lex* outer_select()= 0;
virtual st_select_lex* return_after_parsing()= 0;
- virtual bool set_braces(bool value);
virtual bool inc_in_sum_expr();
virtual uint get_in_sum_expr();
virtual TABLE_LIST* get_table_list();
@@ -546,6 +546,16 @@ public:
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
+ void move_node(st_select_lex_node *where_to_move)
+ {
+ if (where_to_move == this)
+ return;
+ if (next)
+ next->prev= prev;
+ *prev= next;
+ *where_to_move->prev= this;
+ next= where_to_move;
+ }
st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert,
st_select_lex_node *end_chain_node);
friend class st_select_lex_unit;
@@ -602,6 +612,8 @@ public:
executed, // already executed
cleaned;
+ bool optimize_started;
+
// list of fields which points to temporary table for union
List<Item> item_list;
/*
@@ -682,7 +694,7 @@ public:
{
return reinterpret_cast<st_select_lex*>(slave);
}
- void set_with_clause(With_clause *with_cl) { with_clause= with_cl; }
+ void set_with_clause(With_clause *with_cl);
st_select_lex_unit* next_unit()
{
return reinterpret_cast<st_select_lex_unit*>(next);
@@ -695,6 +707,7 @@ public:
bool prepare(THD *thd, select_result *result, ulong additional_options);
bool optimize();
bool exec();
+ bool exec_recursive();
bool cleanup();
inline void unclean() { cleaned= 0; }
void reinit_exec_mechanism();
@@ -726,6 +739,21 @@ public:
typedef class st_select_lex_unit SELECT_LEX_UNIT;
typedef Bounds_checked_array<Item*> Ref_ptr_array;
+
+/*
+ Structure which consists of the field and the item which
+ produces this field.
+*/
+
+class Grouping_tmp_field :public Sql_alloc
+{
+public:
+ Field *tmp_field;
+ Item *producing_item;
+ Grouping_tmp_field(Field *fld, Item *item)
+ :tmp_field(fld), producing_item(item) {}
+};
+
/*
SELECT_LEX - store information of parsed SELECT statment
*/
@@ -737,6 +765,8 @@ public:
Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
Item *prep_having;/* saved HAVING clause for prepared statement processing */
+ Item *cond_pushed_into_where; /* condition pushed into the select's WHERE */
+ Item *cond_pushed_into_having; /* condition pushed into the select's HAVING */
/* Saved values of the WHERE and HAVING clauses*/
Item::cond_result cond_value, having_value;
/* point on lex in which it was created, used in view subquery detection */
@@ -910,6 +940,12 @@ public:
/* namp of nesting SELECT visibility (for aggregate functions check) */
nesting_map name_visibility_map;
+
+ table_map with_dep;
+ List<Grouping_tmp_field> grouping_tmp_fields;
+
+ /* it is for correct printing SELECT options */
+ thr_lock_type lock_type;
void init_query();
void init_select();
@@ -936,7 +972,10 @@ public:
bool mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency);
- bool set_braces(bool value);
+ void set_braces(bool value)
+ {
+ braces= value;
+ }
bool inc_in_sum_expr();
uint get_in_sum_expr();
@@ -1084,10 +1123,7 @@ public:
void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; }
void set_agg_func_used(bool val) { m_agg_func_used= val; }
- void set_with_clause(With_clause *with_clause)
- {
- master_unit()->with_clause= with_clause;
- }
+ void set_with_clause(With_clause *with_clause);
With_clause *get_with_clause()
{
return master_unit()->with_clause;
@@ -1097,7 +1133,14 @@ public:
return master_unit()->with_element;
}
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
-
+ bool check_unrestricted_recursive(bool only_standards_compliant);
+ bool check_subqueries_with_recursive_references();
+ void collect_grouping_fields(THD *thd);
+ void check_cond_extraction_for_grouping_fields(Item *cond,
+ Item_processor processor);
+ Item *build_cond_for_grouping_fields(THD *thd, Item *cond,
+ bool no_to_clones);
+
List<Window_spec> window_specs;
void prepare_add_window_spec(THD *thd);
bool add_window_def(THD *thd, LEX_STRING *win_name, LEX_STRING *win_ref,
@@ -1116,6 +1159,9 @@ public:
bool have_window_funcs() const { return (window_funcs.elements !=0); }
+ bool cond_pushdown_is_allowed() const
+ { return !have_window_funcs() && !olap; }
+
private:
bool m_non_agg_field_used;
bool m_agg_func_used;
@@ -2472,7 +2518,7 @@ struct LEX: public Query_tables_list
SELECT_LEX *all_selects_list;
/* current with clause in parsing if any, otherwise 0*/
With_clause *curr_with_clause;
- /* pointer to the first with clause in the current statemant */
+ /* pointer to the first with clause in the current statement */
With_clause *with_clauses_list;
/*
(*with_clauses_list_last_next) contains a pointer to the last
@@ -2985,9 +3031,12 @@ public:
return false;
}
// Add a constraint as a part of CREATE TABLE or ALTER TABLE
- bool add_constraint(LEX_STRING *name, Virtual_column_info *constr)
+ bool add_constraint(LEX_STRING *name, Virtual_column_info *constr,
+ bool if_not_exists)
{
constr->name= *name;
+ constr->flags= if_not_exists ?
+ Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
alter_info.check_constraint_list.push_back(constr);
return false;
}
@@ -3208,6 +3257,7 @@ public:
}
};
+
extern sql_digest_state *
digest_add_token(sql_digest_state *state, uint token, LEX_YYSTYPE yylval);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 5d698486737..effc0230536 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -392,9 +392,9 @@ const LEX_STRING command_name[257]={
{ 0, 0 }, //248
{ 0, 0 }, //249
{ 0, 0 }, //250
- { 0, 0 }, //251
- { 0, 0 }, //252
- { 0, 0 }, //253
+ { C_STRING_WITH_LEN("Slave_worker") }, //251
+ { C_STRING_WITH_LEN("Slave_IO") }, //252
+ { C_STRING_WITH_LEN("Slave_SQL") }, //253
{ C_STRING_WITH_LEN("Com_multi") }, //254
{ C_STRING_WITH_LEN("Error") } // Last command number 255
};
@@ -768,6 +768,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
@@ -1081,9 +1082,20 @@ void do_handle_bootstrap(THD *thd)
end:
in_bootstrap= FALSE;
delete thd;
+ if (!opt_bootstrap)
+ {
+ /*
+ We need to wake up main thread in case of read_init_file().
+ This is not done by THD::~THD() when there are other threads running
+ (binlog background thread, for example). So do it here again.
+ */
+ mysql_mutex_lock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ }
#ifndef EMBEDDED_LIBRARY
- DBUG_ASSERT(thread_count == 0);
+ DBUG_ASSERT(!opt_bootstrap || thread_count == 0);
my_thread_end();
pthread_exit(0);
#endif
@@ -1476,13 +1488,16 @@ uint maria_multi_check(THD *thd, char *packet, uint packet_length)
DBUG_ENTER("maria_multi_check");
while (packet_length)
{
+ char *packet_start= packet;
+ size_t subpacket_length= net_field_length((uchar **)&packet_start);
+ uint length_length= packet_start - packet;
// length of command + 3 bytes where that length was stored
- uint subpacket_length= (uint3korr(packet) + 3);
- DBUG_PRINT("info", ("sub-packet length: %d command: %x",
- subpacket_length, packet[3]));
+ DBUG_PRINT("info", ("sub-packet length: %ld + %d command: %x",
+ (ulong)subpacket_length, length_length,
+ packet_start[3]));
- if (subpacket_length == 3 ||
- subpacket_length > packet_length)
+ if (subpacket_length == 0 ||
+ (subpacket_length + length_length) > packet_length)
{
my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
MYF(0));
@@ -1490,8 +1505,8 @@ uint maria_multi_check(THD *thd, char *packet, uint packet_length)
}
counter++;
- packet+= subpacket_length;
- packet_length-= subpacket_length;
+ packet= packet_start + subpacket_length;
+ packet_length-= (subpacket_length + length_length);
}
DBUG_RETURN(counter);
}
@@ -2231,8 +2246,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
{
+ char *packet_start= packet;
/* We have to store next length because it will be destroyed by '\0' */
- uint next_subpacket_length= uint3korr(packet);
+ size_t next_subpacket_length= net_field_length((uchar **)&packet_start);
+ uint next_length_length= packet_start - packet;
unsigned char *readbuff= net->buff;
if (net_allocate_new_packet(net, thd, MYF(0)))
@@ -2246,13 +2263,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
while (packet_length)
{
current_com++;
- uint subpacket_length= next_subpacket_length + 3;
+ size_t subpacket_length= next_subpacket_length + next_length_length;
+ uint length_length= next_length_length;
if (subpacket_length < packet_length)
- next_subpacket_length= uint3korr(packet + subpacket_length);
+ {
+ packet_start= packet + subpacket_length;
+ next_subpacket_length= net_field_length((uchar**)&packet_start);
+ next_length_length= packet_start - (packet + subpacket_length);
+ }
/* safety like in do_command() */
packet[subpacket_length]= '\0';
- enum enum_server_command subcommand= fetch_command(thd, (packet + 3));
+ enum enum_server_command subcommand=
+ fetch_command(thd, (packet + length_length));
if (server_command_flags[subcommand] & CF_NO_COM_MULTI)
{
@@ -2260,8 +2283,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
goto com_multi_end;
}
- if (dispatch_command(subcommand, thd, packet + (1 + 3),
- subpacket_length - (1 + 3), TRUE,
+ if (dispatch_command(subcommand, thd, packet + (1 + length_length),
+ subpacket_length - (1 + length_length), TRUE,
(current_com != counter)))
{
DBUG_ASSERT(thd->is_error());
@@ -3852,6 +3875,12 @@ mysql_execute_command(THD *thd)
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
if (create_info.tmp_table())
thd->variables.option_bits|= OPTION_KEEP_LOG;
+ /* in case of create temp tables if @@session_track_state_change is
+ ON then send session state notification in OK packet */
+ if(create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ {
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ }
my_ok(thd);
}
}
@@ -4608,6 +4637,13 @@ end_with_restore_list:
/* DDL and binlog write order are protected by metadata locks. */
res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table());
+
+ /* when dropping temporary tables if @@session_track_state_change is ON then
+ send the boolean tracker in the OK packet */
+ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ {
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ }
break;
}
case SQLCOM_SHOW_PROCESSLIST:
@@ -5419,8 +5455,7 @@ end_with_restore_list:
else
{
/* Reset the isolation level and access mode if no chaining transaction.*/
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -5467,8 +5502,7 @@ end_with_restore_list:
else
{
/* Reset the isolation level and access mode if no chaining transaction.*/
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -5953,8 +5987,7 @@ end_with_restore_list:
We've just done a commit, reset transaction
isolation level and access mode to the session default.
*/
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
my_ok(thd);
break;
}
@@ -5972,8 +6005,7 @@ end_with_restore_list:
We've just done a rollback, reset transaction
isolation level and access mode to the session default.
*/
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
my_ok(thd);
break;
}
@@ -6191,6 +6223,9 @@ finish:
{
thd->mdl_context.release_statement_locks();
}
+
+ TRANSACT_TRACKER(add_trx_state_from_thd(thd));
+
WSREP_TO_ISOLATION_END;
#ifdef WITH_WSREP
@@ -6324,7 +6359,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
}
}
/* Count number of empty select queries */
- if (!thd->get_sent_row_count())
+ if (!thd->get_sent_row_count() && !res)
status_var_increment(thd->status_var.empty_queries);
else
status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());
@@ -7209,6 +7244,7 @@ bool check_stack_overrun(THD *thd, long margin,
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
(long) (my_thread_stack_size - margin))
{
+ thd->is_fatal_error= 1;
/*
Do not use stack for the message buffer to ensure correct
behaviour in cases we have close to no stack left.
@@ -7297,10 +7333,13 @@ void THD::reset_for_next_command()
/*
Autoinc variables should be adjusted only for locally executed
transactions. Appliers and replayers are either processing ROW
- events or get autoinc variable values from Query_log_event.
+ events or get autoinc variable values from Query_log_event and
+ mysql slave may be processing STATEMENT format events, but he should
+ use autoinc values passed in binlog events, not the values forced by
+ the cluster.
*/
if (WSREP(thd) && thd->wsrep_exec_mode == LOCAL_STATE &&
- wsrep_auto_increment_control)
+ !thd->slave_thread && wsrep_auto_increment_control)
{
thd->variables.auto_increment_offset=
global_system_variables.auto_increment_offset;
@@ -7445,22 +7484,30 @@ mysql_new_select(LEX *lex, bool move_down)
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
DBUG_RETURN(TRUE);
}
+
+ /*
+ This type of query is not possible in the grammar:
+ SELECT 1 FROM t1 PROCEDURE ANALYSE() UNION ... ;
+
+ But this type of query is still possible:
+ (SELECT 1 FROM t1 PROCEDURE ANALYSE()) UNION ... ;
+ and it's not easy to disallow this grammatically,
+ because there can be any parenthesis nest level:
+ (((SELECT 1 FROM t1 PROCEDURE ANALYSE()))) UNION ... ;
+ */
if (lex->proc_list.elements!=0)
{
my_error(ER_WRONG_USAGE, MYF(0), "UNION",
"SELECT ... PROCEDURE ANALYSE()");
DBUG_RETURN(TRUE);
}
- if (lex->current_select->order_list.first && !lex->current_select->braces)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
- DBUG_RETURN(1);
- }
- if (lex->current_select->explicit_limit && !lex->current_select->braces)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "LIMIT");
- DBUG_RETURN(1);
- }
+ // SELECT 1 FROM t1 ORDER BY 1 UNION SELECT 1 FROM t1 -- not possible
+ DBUG_ASSERT(!lex->current_select->order_list.first ||
+ lex->current_select->braces);
+ // SELECT 1 FROM t1 LIMIT 1 UNION SELECT 1 FROM t1; -- not possible
+ DBUG_ASSERT(!lex->current_select->explicit_limit ||
+ lex->current_select->braces);
+
select_lex->include_neighbour(lex->current_select);
SELECT_LEX_UNIT *unit= select_lex->master_unit();
if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
@@ -7721,10 +7768,9 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
PROCESSLIST.
*/
if (found_semicolon && (ulong) (found_semicolon - thd->query()))
- thd->set_query_inner(thd->query(),
- (uint32) (found_semicolon -
- thd->query() - 1),
- thd->charset());
+ thd->set_query(thd->query(),
+ (uint32) (found_semicolon - thd->query() - 1),
+ thd->charset());
/* Actually execute the query */
if (found_semicolon)
{
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index b45f85528a6..324eef09854 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -2311,6 +2311,15 @@ static int add_partition_values(File fptr, partition_info *part_info,
{
uint i;
List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
+
+ if (p_elem->max_value)
+ {
+ DBUG_ASSERT(part_info->defined_max_value ||
+ current_thd->lex->sql_command == SQLCOM_ALTER_TABLE);
+ err+= add_string(fptr, " DEFAULT");
+ return err;
+ }
+
err+= add_string(fptr, " VALUES IN ");
uint num_items= p_elem->list_val_list.elements;
@@ -3070,6 +3079,11 @@ int get_partition_id_list_col(partition_info *part_info,
}
}
notfound:
+ if (part_info->defined_max_value)
+ {
+ *part_id= part_info->default_partition_id;
+ DBUG_RETURN(0);
+ }
*part_id= 0;
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
}
@@ -3123,6 +3137,11 @@ int get_partition_id_list(partition_info *part_info,
}
}
notfound:
+ if (part_info->defined_max_value)
+ {
+ *part_id= part_info->default_partition_id;
+ DBUG_RETURN(0);
+ }
*part_id= 0;
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
}
@@ -4691,9 +4710,26 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
DBUG_RETURN(TRUE);
}
- thd->work_part_info= thd->lex->part_info;
-
- if (thd->work_part_info &&
+ partition_info *alt_part_info= thd->lex->part_info;
+ /*
+ This variable is TRUE in very special case when we add only DEFAULT
+ partition to the existing table
+ */
+ bool only_default_value_added=
+ (alt_part_info &&
+ alt_part_info->current_partition &&
+ alt_part_info->current_partition->list_val_list.elements == 1 &&
+ alt_part_info->current_partition->list_val_list.head()->
+ added_items >= 1 &&
+ alt_part_info->current_partition->list_val_list.head()->
+ col_val_array[0].max_value) &&
+ alt_part_info->part_type == LIST_PARTITION &&
+ (alter_info->flags & Alter_info::ALTER_ADD_PARTITION);
+ if (only_default_value_added &&
+ !thd->lex->part_info->num_columns)
+ thd->lex->part_info->num_columns= 1; // to make correct clone
+
+ if ((thd->work_part_info= thd->lex->part_info) &&
!(thd->work_part_info= thd->lex->part_info->get_clone(thd)))
DBUG_RETURN(TRUE);
@@ -4709,12 +4745,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
Alter_info::ALTER_REBUILD_PARTITION))
{
partition_info *tab_part_info;
- partition_info *alt_part_info= thd->work_part_info;
uint flags= 0;
bool is_last_partition_reorged= FALSE;
part_elem_value *tab_max_elem_val= NULL;
part_elem_value *alt_max_elem_val= NULL;
longlong tab_max_range= 0, alt_max_range= 0;
+ alt_part_info= thd->work_part_info;
if (!table->part_info)
{
@@ -4805,7 +4841,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
goto err;
}
- if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0)
+ if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0 &&
+ !tab_part_info->has_default_partititon())
{
/*
"Fast" change of partitioning is supported in this case.
@@ -4879,14 +4916,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
}
}
if ((tab_part_info->column_list &&
- alt_part_info->num_columns != tab_part_info->num_columns) ||
+ alt_part_info->num_columns != tab_part_info->num_columns &&
+ !only_default_value_added) ||
(!tab_part_info->column_list &&
(tab_part_info->part_type == RANGE_PARTITION ||
tab_part_info->part_type == LIST_PARTITION) &&
- alt_part_info->num_columns != 1U) ||
+ alt_part_info->num_columns != 1U &&
+ !only_default_value_added) ||
(!tab_part_info->column_list &&
tab_part_info->part_type == HASH_PARTITION &&
- alt_part_info->num_columns != 0))
+ (alt_part_info->num_columns != 0)))
{
my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
goto err;
@@ -4919,9 +4958,13 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_NO_BINLOG_ERROR, MYF(0));
goto err;
}
- if (tab_part_info->defined_max_value)
+ if (tab_part_info->defined_max_value &&
+ (tab_part_info->part_type == RANGE_PARTITION ||
+ alt_part_info->defined_max_value))
{
- my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ my_error((tab_part_info->part_type == RANGE_PARTITION ?
+ ER_PARTITION_MAXVALUE_ERROR :
+ ER_PARTITION_DEFAULT_ERROR), MYF(0));
goto err;
}
if (num_new_partitions == 0)
@@ -7677,8 +7720,10 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
uint flags,
PARTITION_ITERATOR *part_iter)
{
+ bool can_match_multiple_values;
uint32 nparts;
get_col_endpoint_func UNINIT_VAR(get_col_endpoint);
+ uint full_length= 0;
DBUG_ENTER("get_part_iter_for_interval_cols_via_map");
if (part_info->part_type == RANGE_PARTITION)
@@ -7696,6 +7741,19 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
else
assert(0);
+ for (uint32 i= 0; i < part_info->num_columns; i++)
+ full_length+= store_length_array[i];
+
+ can_match_multiple_values= ((flags &
+ (NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN |
+ NEAR_MAX)) ||
+ (min_len != max_len) ||
+ (min_len != full_length) ||
+ memcmp(min_value, max_value, min_len));
+ DBUG_ASSERT(can_match_multiple_values || (flags & EQ_RANGE) || flags == 0);
+ if (can_match_multiple_values && part_info->has_default_partititon())
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
+
if (flags & NO_MIN_RANGE)
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
else
@@ -7731,7 +7789,15 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info,
nparts);
}
if (part_iter->part_nums.start == part_iter->part_nums.end)
+ {
+ // No matching partition found.
+ if (part_info->has_default_partititon())
+ {
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
+ DBUG_RETURN(1);
+ }
DBUG_RETURN(0);
+ }
DBUG_RETURN(1);
}
@@ -7792,6 +7858,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
(void)min_len;
(void)max_len;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
if (part_info->part_type == RANGE_PARTITION)
{
@@ -7828,8 +7895,13 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
else
MY_ASSERT_UNREACHABLE();
- can_match_multiple_values= (flags || !min_value || !max_value ||
+ can_match_multiple_values= ((flags &
+ (NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN |
+ NEAR_MAX)) ||
memcmp(min_value, max_value, field_len));
+ DBUG_ASSERT(can_match_multiple_values || (flags & EQ_RANGE) || flags == 0);
+ if (can_match_multiple_values && part_info->has_default_partititon())
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
if (can_match_multiple_values &&
(part_info->part_type == RANGE_PARTITION ||
part_info->has_null_value))
@@ -7859,6 +7931,12 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
{
/* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
part_iter->part_nums.end= 0;
+ /*
+ It is something like select * from tbl where col IS NULL
+ and we have partition with NULL to catch it, so we do not need
+ DEFAULT partition
+ */
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
DBUG_RETURN(1);
}
}
@@ -7882,8 +7960,19 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
/* col = x and F(x) = NULL -> only search NULL partition */
part_iter->part_nums.cur= part_iter->part_nums.start= 0;
part_iter->part_nums.end= 0;
- part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
- DBUG_RETURN(1);
+ /*
+ if NULL partition exists:
+ for RANGE it is the first partition (always exists);
+ for LIST should be indicator that it is present
+ */
+ if (part_info->part_type == RANGE_PARTITION ||
+ part_info->has_null_value)
+ {
+ part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
+ DBUG_RETURN(1);
+ }
+ // If no NULL partition look up in DEFAULT or there is no such value
+ goto not_found;
}
part_iter->part_nums.cur= part_iter->part_nums.start;
if (check_zero_dates && !part_info->part_expr->null_value)
@@ -7900,7 +7989,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
}
if (part_iter->part_nums.start == max_endpoint_val)
- DBUG_RETURN(0); /* No partitions */
+ goto not_found;
}
}
@@ -7937,9 +8026,17 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
if (part_iter->part_nums.start >= part_iter->part_nums.end &&
!part_iter->ret_null_part)
- DBUG_RETURN(0); /* No partitions */
+ goto not_found;
}
DBUG_RETURN(1); /* Ok, iterator initialized */
+
+not_found:
+ if (part_info->has_default_partititon())
+ {
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= TRUE;
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0); /* No partitions */
}
@@ -8003,6 +8100,8 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
(void)max_len;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
+ part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE;
+
if (is_subpart)
{
field= part_info->subpart_field_array[0];
@@ -8134,6 +8233,9 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
part_iter->ret_null_part= FALSE;
return 0; /* NULL always in first range partition */
}
+ // we do not have default partition in RANGE partitioning
+ DBUG_ASSERT(!part_iter->ret_default_part);
+
part_iter->part_nums.cur= part_iter->part_nums.start;
part_iter->ret_null_part= part_iter->ret_null_part_orig;
return NOT_A_PARTITION_ID;
@@ -8171,8 +8273,15 @@ uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
part_iter->ret_null_part= FALSE;
return part_iter->part_info->has_null_part_id;
}
+ if (part_iter->ret_default_part)
+ {
+ part_iter->ret_default_part= FALSE;
+ return part_iter->part_info->default_partition_id;
+ }
+ /* Reset partition for next read */
part_iter->part_nums.cur= part_iter->part_nums.start;
part_iter->ret_null_part= part_iter->ret_null_part_orig;
+ part_iter->ret_default_part= part_iter->ret_default_part_orig;
return NOT_A_PARTITION_ID;
}
else
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index dd352b60120..b225c14fc53 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -177,6 +177,10 @@ typedef struct st_partition_iter
iterator also produce id of the partition that contains NULL value.
*/
bool ret_null_part, ret_null_part_orig;
+ /*
+ We should return DEFAULT partition.
+ */
+ bool ret_default_part, ret_default_part_orig;
struct st_part_num_range
{
uint32 start;
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 848358e517a..ce46a76d103 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -269,6 +269,7 @@ struct st_bookmark
uint name_len;
int offset;
uint version;
+ bool loaded;
char key[1];
};
@@ -1175,6 +1176,13 @@ err:
DBUG_RETURN(errs > 0 || oks + dupes == 0);
}
+static void plugin_variables_deinit(struct st_plugin_int *plugin)
+{
+
+ for (sys_var *var= plugin->system_vars; var; var= var->next)
+ (*var->test_load)= FALSE;
+ mysql_del_sys_var_chain(plugin->system_vars);
+}
static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
{
@@ -1226,8 +1234,7 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
if (ref_check && plugin->ref_count)
sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
plugin->name.str, plugin->ref_count);
-
- mysql_del_sys_var_chain(plugin->system_vars);
+ plugin_variables_deinit(plugin);
}
static void plugin_del(struct st_plugin_int *plugin)
@@ -1447,7 +1454,7 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
err:
if (ret)
- mysql_del_sys_var_chain(plugin->system_vars);
+ plugin_variables_deinit(plugin);
mysql_mutex_lock(&LOCK_plugin);
plugin->state= state;
@@ -2780,15 +2787,17 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
System Variables support
****************************************************************************/
-
-sys_var *find_sys_var(THD *thd, const char *str, uint length)
+sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
+ bool throw_error, bool locked)
{
sys_var *var;
sys_var_pluginvar *pi= NULL;
plugin_ref plugin;
- DBUG_ENTER("find_sys_var");
+ DBUG_ENTER("find_sys_var_ex");
+ DBUG_PRINT("enter", ("var '%.*s'", (int)length, str));
- mysql_mutex_lock(&LOCK_plugin);
+ if (!locked)
+ mysql_mutex_lock(&LOCK_plugin);
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar()))
@@ -2807,14 +2816,20 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length)
}
else
mysql_rwlock_unlock(&LOCK_system_variables_hash);
- mysql_mutex_unlock(&LOCK_plugin);
+ if (!locked)
+ mysql_mutex_unlock(&LOCK_plugin);
- if (!var)
- my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
+ if (!throw_error && !var)
+ my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str);
DBUG_RETURN(var);
}
+sys_var *find_sys_var(THD *thd, const char *str, size_t length)
+{
+ return find_sys_var_ex(thd, str, length, false, false);
+}
+
/*
called by register_var, construct_options and test_plugin_options.
Returns the 'bookmark' for the named variable.
@@ -2962,6 +2977,70 @@ static st_bookmark *register_var(const char *plugin, const char *name,
return result;
}
+
+void sync_dynamic_session_variables(THD* thd, bool global_lock)
+{
+ uint idx;
+
+ thd->variables.dynamic_variables_ptr= (char*)
+ my_realloc(thd->variables.dynamic_variables_ptr,
+ global_variables_dynamic_size,
+ MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+
+ if (global_lock)
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
+
+ memcpy(thd->variables.dynamic_variables_ptr +
+ thd->variables.dynamic_variables_size,
+ global_system_variables.dynamic_variables_ptr +
+ thd->variables.dynamic_variables_size,
+ global_system_variables.dynamic_variables_size -
+ thd->variables.dynamic_variables_size);
+
+ /*
+ now we need to iterate through any newly copied 'defaults'
+ and if it is a string type with MEMALLOC flag, we need to strdup
+ */
+ for (idx= 0; idx < bookmark_hash.records; idx++)
+ {
+ sys_var_pluginvar *pi;
+ sys_var *var;
+ st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);
+
+ if (v->version <= thd->variables.dynamic_variables_version)
+ continue; /* already in thd->variables */
+
+ if (!(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
+ !(pi= var->cast_pluginvar()) ||
+ v->key[0] != plugin_var_bookmark_key(pi->plugin_var->flags))
+ continue;
+
+ /* Here we do anything special that may be required of the data types */
+
+ if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
+ {
+ int offset= ((thdvar_str_t *)(pi->plugin_var))->offset;
+ char **pp= (char**) (thd->variables.dynamic_variables_ptr + offset);
+ if (*pp)
+ *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
+ }
+ }
+
+ if (global_lock)
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ thd->variables.dynamic_variables_version=
+ global_system_variables.dynamic_variables_version;
+ thd->variables.dynamic_variables_head=
+ global_system_variables.dynamic_variables_head;
+ thd->variables.dynamic_variables_size=
+ global_system_variables.dynamic_variables_size;
+}
+
+
/*
returns a pointer to the memory which holds the thd-local variable or
a pointer to the global variable if thd==null.
@@ -2983,67 +3062,8 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
if (!thd->variables.dynamic_variables_ptr ||
(uint)offset > thd->variables.dynamic_variables_head)
{
- uint idx;
-
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
-
- thd->variables.dynamic_variables_ptr= (char*)
- my_realloc(thd->variables.dynamic_variables_ptr,
- global_variables_dynamic_size,
- MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
-
- if (global_lock)
- mysql_mutex_lock(&LOCK_global_system_variables);
-
- mysql_mutex_assert_owner(&LOCK_global_system_variables);
-
- memcpy(thd->variables.dynamic_variables_ptr +
- thd->variables.dynamic_variables_size,
- global_system_variables.dynamic_variables_ptr +
- thd->variables.dynamic_variables_size,
- global_system_variables.dynamic_variables_size -
- thd->variables.dynamic_variables_size);
-
- /*
- now we need to iterate through any newly copied 'defaults'
- and if it is a string type with MEMALLOC flag, we need to strdup
- */
- for (idx= 0; idx < bookmark_hash.records; idx++)
- {
- sys_var_pluginvar *pi;
- sys_var *var;
- st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);
-
- if (v->version <= thd->variables.dynamic_variables_version)
- continue; /* already in thd->variables */
-
- if (!(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
- !(pi= var->cast_pluginvar()) ||
- v->key[0] != plugin_var_bookmark_key(pi->plugin_var->flags))
- continue;
-
- /* Here we do anything special that may be required of the data types */
-
- if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
- pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
- {
- int offset= ((thdvar_str_t *)(pi->plugin_var))->offset;
- char **pp= (char**) (thd->variables.dynamic_variables_ptr + offset);
- if (*pp)
- *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
- }
- }
-
- if (global_lock)
- mysql_mutex_unlock(&LOCK_global_system_variables);
-
- thd->variables.dynamic_variables_version=
- global_system_variables.dynamic_variables_version;
- thd->variables.dynamic_variables_head=
- global_system_variables.dynamic_variables_head;
- thd->variables.dynamic_variables_size=
- global_system_variables.dynamic_variables_size;
-
+ sync_dynamic_session_variables(thd, global_lock);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
}
DBUG_RETURN((uchar*)thd->variables.dynamic_variables_ptr + offset);
@@ -3577,6 +3597,7 @@ void plugin_opt_set_limits(struct my_option *options,
case PLUGIN_VAR_BOOL:
options->var_type= GET_BOOL;
options->def_value= ((sysvar_bool_t*) opt)->def_val;
+ options->typelib= &bool_typelib;
break;
case PLUGIN_VAR_STR:
options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
@@ -3625,6 +3646,7 @@ void plugin_opt_set_limits(struct my_option *options,
case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
options->var_type= GET_BOOL;
options->def_value= ((thdvar_bool_t*) opt)->def_val;
+ options->typelib= &bool_typelib;
break;
case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
@@ -3941,6 +3963,14 @@ my_bool mark_changed(int, const struct my_option *opt, char *)
}
/**
+ It is always false to mark global plugin variable unloaded just to be
+ safe because we have no way now to know truth about them.
+
+ TODO: make correct mechanism for global plugin variables
+*/
+static bool static_unload= FALSE;
+
+/**
Create and register system variables supplied from the plugin and
assigns initial values from corresponding command line arguments.
@@ -4017,9 +4047,13 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
tmp_backup[tmp->nbackups++].save(&o->name);
if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
+ {
varname= var->key + 1;
+ var->loaded= TRUE;
+ }
else
{
+ var= NULL;
len= tmp->name.length + strlen(o->name) + 2;
varname= (char*) alloc_root(mem_root, len);
strxmov(varname, tmp->name.str, "-", o->name, NullS);
@@ -4027,6 +4061,9 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
convert_dash_to_underscore(varname, len-1);
}
v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o);
+ v->test_load= (var ? &var->loaded : &static_unload);
+ DBUG_ASSERT(static_unload == FALSE);
+
if (!(o->flags & PLUGIN_VAR_NOCMDOPT))
{
// update app_type, used for I_S.SYSTEM_VARIABLES
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index efa48b22ce8..317ccf482b6 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -120,6 +120,8 @@ struct st_plugin_int
};
+extern mysql_mutex_t LOCK_plugin;
+
/*
See intern_plugin_lock() for the explanation for the
conditionally defined plugin_ref type
@@ -187,7 +189,13 @@ typedef my_bool (plugin_foreach_func)(THD *thd,
#define plugin_foreach(A,B,C,D) plugin_foreach_with_mask(A,B,C,PLUGIN_IS_READY,D)
extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
int type, uint state_mask, void *arg);
+extern void sync_dynamic_session_variables(THD* thd, bool global_lock);
+
extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
plugin_foreach_func *func, void *arg);
+sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
+ bool throw_error, bool locked);
+
+extern void sync_dynamic_session_variables(THD* thd, bool global_lock);
#endif
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index e8a7dce5771..eab2863588d 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2758,7 +2758,10 @@ void mysql_sql_stmt_prepare(THD *thd)
thd->stmt_map.erase(stmt);
}
else
+ {
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
my_ok(thd, 0L, 0L, "Statement prepared");
+ }
DBUG_VOID_RETURN;
}
@@ -3208,6 +3211,7 @@ void mysql_sql_stmt_close(THD *thd)
else
{
stmt->deallocate();
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
my_ok(thd);
}
}
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 40349d3802a..4bf93040dc8 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -226,6 +226,7 @@
#define OPTIMIZER_SWITCH_EXTENDED_KEYS (1ULL << 27)
#define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28)
#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29)
+#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -249,7 +250,8 @@
OPTIMIZER_SWITCH_SEMIJOIN | \
OPTIMIZER_SWITCH_FIRSTMATCH | \
OPTIMIZER_SWITCH_LOOSE_SCAN | \
- OPTIMIZER_SWITCH_EXISTS_TO_IN)
+ OPTIMIZER_SWITCH_EXISTS_TO_IN | \
+ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)
/*
Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
use strictly more than 64 bits by adding one more define above, you should
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index 2fef615831b..2d72d1052d2 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -155,6 +155,12 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
{
if (mysql_bin_log.rotate_and_purge(true))
*write_to_binlog= -1;
+
+ if (WSREP_ON)
+ {
+ /* Wait for last binlog checkpoint event to be logged. */
+ mysql_bin_log.wait_for_last_checkpoint_event();
+ }
}
}
if (options & REFRESH_RELAY_LOG)
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 17b297f63bd..1588644f0e1 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -24,10 +24,9 @@
#include "unireg.h"
#include "sql_rename.h"
#include "sql_cache.h" // query_cache_*
-#include "sql_table.h" // build_table_filename
+#include "sql_table.h" // write_bin_log
#include "sql_view.h" // mysql_frm_type, mysql_rename_view
#include "sql_trigger.h"
-#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
#include "sql_base.h" // tdc_remove_table, lock_table_names,
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_statistics.h"
@@ -212,6 +211,28 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list)
}
+static bool
+do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
+ bool skip_error)
+{
+ const char *new_alias;
+ DBUG_ENTER("do_rename_temporary");
+
+ new_alias= (lower_case_table_names == 2) ? new_table->alias :
+ new_table->table_name;
+
+ if (is_temporary_table(new_table))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ DBUG_RETURN(1); // This can't be skipped
+ }
+
+
+ DBUG_RETURN(thd->rename_temporary_table(ren_table->table,
+ new_table->db, new_alias));
+}
+
+
/*
Rename a single table or a view
@@ -317,6 +338,8 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
DBUG_RETURN(0);
}
+
+
/*
Rename all tables in list; Return pointer to wrong entry if something goes
wrong. Note that the table_list may be empty!
@@ -351,8 +374,11 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
{
new_table= ren_table->next_local;
- if (do_rename(thd, ren_table, new_table->db, new_table->table_name,
- new_table->alias, skip_error))
+
+ if (is_temporary_table(ren_table) ?
+ do_rename_temporary(thd, ren_table, new_table, skip_error) :
+ do_rename(thd, ren_table, new_table->db, new_table->table_name,
+ new_table->alias, skip_error))
DBUG_RETURN(ren_table);
}
DBUG_RETURN(0);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 36f0cd84cbf..6ece9b793c9 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -2116,12 +2116,6 @@ static int init_binlog_sender(binlog_send_info *info,
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return 1;
}
- if (!server_id_supplied)
- {
- info->errmsg= "Misconfigured master - server id was not set";
- info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
- return 1;
- }
char search_file_name[FN_REFLEN];
const char *name=search_file_name;
@@ -3072,12 +3066,6 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
if (init_master_info(mi,master_info_file_tmp,relay_log_info_file_tmp, 0,
thread_mask))
slave_errno=ER_MASTER_INFO;
- else if (!server_id_supplied)
- {
- slave_errno= ER_BAD_SLAVE; net_report= 0;
- my_message(slave_errno, "Misconfigured slave: server_id was not set; Fix in config file",
- MYF(0));
- }
else if (!*mi->host)
{
slave_errno= ER_BAD_SLAVE; net_report= 0;
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index e2000bbca73..4105bdddf4e 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -36,7 +36,6 @@ struct slave_connection_state;
extern my_bool opt_show_slave_auth_info;
extern char *master_host, *master_info_file;
-extern bool server_id_supplied;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 5cc7798fde9..aa08420931f 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -165,10 +165,6 @@ static COND *optimize_cond(JOIN *join, COND *conds,
int flags= 0);
bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static int do_select(JOIN *join, Procedure *procedure);
-static bool instantiate_tmp_table(TABLE *table, KEY *keyinfo,
- MARIA_COLUMNDEF *start_recinfo,
- MARIA_COLUMNDEF **recinfo,
- ulonglong options);
static enum_nested_loop_state evaluate_join_record(JOIN *, JOIN_TAB *, int);
static enum_nested_loop_state
@@ -699,7 +695,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
DBUG_ENTER("JOIN::prepare");
// to prevent double initialization on EXPLAIN
- if (optimized)
+ if (optimization_state != JOIN::NOT_OPTIMIZED)
DBUG_RETURN(0);
conds= conds_init;
@@ -858,6 +854,13 @@ JOIN::prepare(TABLE_LIST *tables_init,
With_clause *with_clause=select_lex->get_with_clause();
if (with_clause && with_clause->prepare_unreferenced_elements(thd))
DBUG_RETURN(1);
+
+ With_element *with_elem= select_lex->get_with_element();
+ if (with_elem &&
+ select_lex->check_unrestricted_recursive(
+ thd->variables.only_standards_compliant_cte))
+ DBUG_RETURN(-1);
+ select_lex->check_subqueries_with_recursive_references();
int res= check_and_do_in_subquery_rewrites(this);
@@ -1065,24 +1068,13 @@ err:
int JOIN::optimize()
{
- bool was_optimized= optimized;
+ // to prevent double initialization on EXPLAIN
+ if (optimization_state != JOIN::NOT_OPTIMIZED)
+ return FALSE;
+ optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS;
+
int res= optimize_inner();
- /*
- If we're inside a non-correlated subquery, this function may be
- called for the second time after the subquery has been executed
- and deleted. The second call will not produce a valid query plan, it will
- short-circuit because optimized==TRUE.
-
- "was_optimized != optimized" is here to handle this case:
- - first optimization starts, gets an error (from a const. cheap
- subquery), returns 1
- - another JOIN::optimize() call made, and now join->optimize() will
- return 0, even though we never had a query plan.
-
- Can have QEP_NOT_PRESENT_YET for degenerate queries (for example,
- SELECT * FROM tbl LIMIT 0)
- */
- if (was_optimized != optimized && !res && have_query_plan != QEP_DELETED)
+ if (!res && have_query_plan != QEP_DELETED)
{
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
have_query_plan= QEP_AVAILABLE;
@@ -1109,6 +1101,7 @@ int JOIN::optimize()
}
}
+ optimization_state= JOIN::OPTIMIZATION_DONE;
return res;
}
@@ -1128,16 +1121,15 @@ int JOIN::optimize()
int
JOIN::optimize_inner()
{
+/*
+ if (conds) { Item *it_clone= conds->build_clone(thd,thd->mem_root); }
+*/
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
JOIN_TAB *tab;
DBUG_ENTER("JOIN::optimize");
-
do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
- // to prevent double initialization on EXPLAIN
- if (optimized)
- DBUG_RETURN(0);
- optimized= 1;
+
DEBUG_SYNC(thd, "before_join_optimize");
THD_STAGE_INFO(thd, stage_optimizing);
@@ -1145,10 +1137,6 @@ JOIN::optimize_inner()
set_allowed_join_cache_types();
need_distinct= TRUE;
- /* Run optimize phase for all derived tables/views used in this SELECT. */
- if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
- DBUG_RETURN(1);
-
if (select_lex->first_cond_optimization)
{
//Do it only for the first execution
@@ -1261,8 +1249,49 @@ JOIN::optimize_inner()
if (setup_jtbm_semi_joins(this, join_list, &conds))
DBUG_RETURN(1);
+ if (select_lex->cond_pushed_into_where)
+ {
+ conds= and_conds(thd, conds, select_lex->cond_pushed_into_where);
+ if (conds && conds->fix_fields(thd, &conds))
+ DBUG_RETURN(1);
+ }
+ if (select_lex->cond_pushed_into_having)
+ {
+ having= and_conds(thd, having, select_lex->cond_pushed_into_having);
+ if (having)
+ {
+ select_lex->having_fix_field= 1;
+ if (having->fix_fields(thd, &having))
+ DBUG_RETURN(1);
+ select_lex->having_fix_field= 0;
+ }
+ }
+
conds= optimize_cond(this, conds, join_list, FALSE,
&cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
+
+ if (thd->lex->sql_command == SQLCOM_SELECT &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED))
+ {
+ TABLE_LIST *tbl;
+ List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
+ while ((tbl= li++))
+ {
+ if (tbl->is_materialized_derived())
+ {
+ if (pushdown_cond_for_derived(thd, conds, tbl))
+ DBUG_RETURN(1);
+ if (mysql_handle_single_derived(thd->lex, tbl, DT_OPTIMIZE))
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ else
+ {
+ /* Run optimize phase for all derived tables/views used in this SELECT. */
+ if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
+ DBUG_RETURN(1);
+ }
if (thd->is_error())
{
@@ -3166,6 +3195,7 @@ void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
DBUG_ENTER("JOIN::exec_inner");
+ DBUG_ASSERT(optimization_state == JOIN::OPTIMIZATION_DONE);
THD_STAGE_INFO(thd, stage_executing);
@@ -3668,6 +3698,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->checked_keys.init();
s->needed_reg.init();
table_vector[i]=s->table=table=tables->table;
+ s->tab_list= tables;
table->pos_in_table_list= tables;
error= tables->fetch_number_of_rows();
set_statistics_for_table(join->thd, table);
@@ -8357,6 +8388,8 @@ JOIN_TAB *first_explain_order_tab(JOIN* join)
{
JOIN_TAB* tab;
tab= join->join_tab;
+ if (!tab)
+ return NULL; /* Can happen when when the tables were optimized away */
return (tab->bush_children) ? tab->bush_children->start : tab;
}
@@ -11419,6 +11452,11 @@ bool error_if_full_join(JOIN *join)
void JOIN_TAB::cleanup()
{
DBUG_ENTER("JOIN_TAB::cleanup");
+
+ if (tab_list && tab_list->is_with_table_recursive_reference() &&
+ tab_list->with->is_cleaned())
+ DBUG_VOID_RETURN;
+
DBUG_PRINT("enter", ("tab: %p table %s.%s",
this,
(table ? table->s->db.str : "?"),
@@ -11588,7 +11626,8 @@ bool JOIN_TAB::preread_init()
}
/* Materialize derived table/view. */
- if (!derived->get_unit()->executed &&
+ if ((!derived->get_unit()->executed ||
+ derived->is_recursive_with_table()) &&
mysql_handle_single_derived(join->thd->lex,
derived, DT_CREATE | DT_FILL))
return TRUE;
@@ -15745,7 +15784,7 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
Field_double(max_length, maybe_null, name, decimals, TRUE);
break;
case INT_RESULT:
- /*
+ /*
Select an integer type with the minimal fit precision.
convert_int_length is sign inclusive, don't consider the sign.
*/
@@ -15761,7 +15800,6 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
break;
case STRING_RESULT:
DBUG_ASSERT(collation.collation);
-
/*
GEOMETRY fields have STRING_RESULT result type.
To preserve type they needed to be handled separately.
@@ -17943,7 +17981,6 @@ int rr_sequential_and_unpack(READ_RECORD *info)
TRUE - Error
*/
-static
bool instantiate_tmp_table(TABLE *table, KEY *keyinfo,
MARIA_COLUMNDEF *start_recinfo,
MARIA_COLUMNDEF **recinfo,
@@ -19218,7 +19255,7 @@ int join_init_read_record(JOIN_TAB *tab)
report_error(tab->table, error);
return 1;
}
- if (!tab->preread_init_done && tab->preread_init())
+ if (!tab->preread_init_done && tab->preread_init())
return 1;
if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select, tab->filesort_result, 1,1, FALSE))
@@ -19451,8 +19488,6 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!end_of_records)
{
-#if 0
-#endif
if (join->table_count &&
join->join_tab->is_using_loose_index_scan())
{
@@ -24321,7 +24356,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
In case this is a derived table, here we remember the number of
subselect that used to produce it.
*/
- eta->derived_select_number= table->derived_select_number;
+ if (!(table_list && table_list->is_with_table_recursive_reference()))
+ eta->derived_select_number= table->derived_select_number;
/* The same for non-merged semi-joins */
eta->non_merged_sjm_number = get_non_merged_semijoin_select();
@@ -24537,11 +24573,14 @@ int JOIN::save_explain_data_intern(Explain_query *output,
(1) they are not parts of ON clauses that were eliminated by table
elimination.
(2) they are not merged derived tables
+ (3) they are not unreferenced CTE
*/
if (!(tmp_unit->item && tmp_unit->item->eliminated) && // (1)
(!tmp_unit->derived ||
- tmp_unit->derived->is_materialized_derived())) // (2)
- {
+ tmp_unit->derived->is_materialized_derived()) && // (2)
+ !(tmp_unit->with_element &&
+ !tmp_unit->with_element->is_referenced())) // (3)
+ {
explain->add_child(tmp_unit->first_select()->select_number);
}
}
@@ -24601,9 +24640,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
Save plans for child subqueries, when
(1) they are not parts of eliminated WHERE/ON clauses.
(2) they are not VIEWs that were "merged for INSERT".
+ (3) they are not unreferenced CTE.
*/
- if (!(unit->item && unit->item->eliminated) && // (1)
- !(unit->derived && unit->derived->merged_for_insert)) // (2)
+ if (!(unit->item && unit->item->eliminated) && // (1)
+ !(unit->derived && unit->derived->merged_for_insert) && // (2)
+ !(unit->with_element && !unit->with_element->is_referenced())) // (3)
{
if (mysql_explain_union(thd, unit, result))
DBUG_VOID_RETURN;
@@ -24627,7 +24668,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
if (unit->is_union())
{
- if (unit->union_needs_tmp_table())
+ if (unit->union_needs_tmp_table() && unit->fake_select_lex)
{
unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization
unit->fake_select_lex->type= "UNION RESULT";
@@ -25147,6 +25188,12 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
// limit
print_limit(thd, str, query_type);
+ // lock type
+ if (lock_type == TL_READ_WITH_SHARED_LOCKS)
+ str->append(" lock in share mode");
+ else if (lock_type == TL_WRITE)
+ str->append(" for update");
+
// PROCEDURE unsupported here
}
@@ -26193,5 +26240,60 @@ AGGR_OP::end_send()
/**
+ @brief
+ Remove marked top conjuncts of a condition
+
+ @param thd The thread handle
+ @param cond The condition which subformulas are to be removed
+
+ @details
+ The function removes all top conjuncts marked with the flag
+ FULL_EXTRACTION_FL from the condition 'cond'. The resulting
+ formula is returned a the result of the function
+ If 'cond' s marked with such flag the function returns 0.
+ The function clear the extraction flags for the removed
+ formulas
+
+ @retval
+ condition without removed subformulas
+ 0 if the whole 'cond' is removed
+*/
+
+Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
+{
+ if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ cond->clear_extraction_flag();
+ return 0;
+ }
+ if (cond->type() == Item::COND_ITEM)
+ {
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ item->clear_extraction_flag();
+ li.remove();
+ }
+ }
+ switch (((Item_cond*) cond)->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return ((Item_cond*) cond)->argument_list()->head();
+ default:
+ return cond;
+ }
+ }
+ }
+ return cond;
+}
+
+/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 92ba74fac35..fb3f71c0769 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -188,7 +188,7 @@ typedef enum_nested_loop_state
Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab);
int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
-
+Item *remove_pushed_top_conjuncts(THD *thd, Item *cond);
#include "sql_explain.h"
@@ -205,6 +205,7 @@ class Filesort;
typedef struct st_join_table {
st_join_table() {}
TABLE *table;
+ TABLE_LIST *tab_list;
KEYUSE *keyuse; /**< pointer to first used key */
KEY *hj_key; /**< descriptor of the used best hash join key
not supported by any index */
@@ -1369,7 +1370,8 @@ public:
enum join_optimization_state { NOT_OPTIMIZED=0,
OPTIMIZATION_IN_PROGRESS=1,
OPTIMIZATION_DONE=2};
- bool optimized; ///< flag to avoid double optimization in EXPLAIN
+ // state of JOIN optimization
+ enum join_optimization_state optimization_state;
bool initialized; ///< flag to avoid double init_execution calls
Explain_select *explain;
@@ -1449,7 +1451,7 @@ public:
items2.reset();
items3.reset();
zero_result_cause= 0;
- optimized= 0;
+ optimization_state= JOIN::NOT_OPTIMIZED;
have_query_plan= QEP_NOT_PRESENT_YET;
initialized= 0;
cleaned= 0;
@@ -2232,6 +2234,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
TMP_ENGINE_COLUMNDEF *start_recinfo,
TMP_ENGINE_COLUMNDEF **recinfo,
ulonglong options);
+bool instantiate_tmp_table(TABLE *table, KEY *keyinfo,
+ MARIA_COLUMNDEF *start_recinfo,
+ MARIA_COLUMNDEF **recinfo,
+ ulonglong options);
bool open_tmp_table(TABLE *table);
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps);
double prev_record_reads(POSITION *positions, uint idx, table_map found_ref);
@@ -2271,4 +2277,8 @@ public:
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
int test_if_group_changed(List<Cached_item> &list);
int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
+
+JOIN_TAB *first_explain_order_tab(JOIN* join);
+JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab);
+
#endif /* SQL_SELECT_INCLUDED */
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 49287728a90..c4ca23cba78 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1634,20 +1634,11 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
bool quoted)
{
bool has_default;
- bool has_now_default;
enum enum_field_types field_type= field->type();
- /*
- We are using CURRENT_TIMESTAMP instead of NOW because it is
- more standard
- */
- has_now_default= field->has_insert_default_function();
-
has_default= (field->default_value ||
(!(field->flags & NO_DEFAULT_VALUE_FLAG) &&
- field->unireg_check != Field::NEXT_NUMBER &&
- !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
- && has_now_default)));
+ field->unireg_check != Field::NEXT_NUMBER));
def_value->length(0);
if (has_default)
@@ -1662,17 +1653,14 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
field->default_value->expr_str.length);
def_value->append(')');
}
+ else if (field->unireg_check)
+ def_value->append(field->default_value->expr_str.str,
+ field->default_value->expr_str.length);
else
def_value->set(field->default_value->expr_str.str,
field->default_value->expr_str.length,
&my_charset_utf8mb4_general_ci);
}
- else if (has_now_default)
- {
- def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP"));
- if (field->decimals() > 0)
- def_value->append_parenthesized(field->decimals());
- }
else if (!field->is_null())
{ // Not null by default
char tmp[MAX_FIELD_WIDTH];
@@ -1689,7 +1677,11 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
quoted= 0;
}
else
+ {
field->val_str(&type);
+ if (!field->str_needs_quotes())
+ quoted= 0;
+ }
if (type.length())
{
String def_val;
@@ -1700,13 +1692,13 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
if (quoted)
append_unescaped(def_value, def_val.ptr(), def_val.length());
else
- def_value->append(def_val.ptr(), def_val.length());
+ def_value->move(def_val);
}
else if (quoted)
- def_value->append(STRING_WITH_LEN("''"));
+ def_value->set(STRING_WITH_LEN("''"), system_charset_info);
}
else if (field->maybe_null() && quoted)
- def_value->append(STRING_WITH_LEN("NULL")); // Null as default
+ def_value->set(STRING_WITH_LEN("NULL"), system_charset_info); // Null as default
else
return 0;
@@ -1793,8 +1785,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
List<Item> field_list;
char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH];
const char *alias;
- String type(tmp, sizeof(tmp), system_charset_info);
- String def_value(def_value_buf, sizeof(def_value_buf), system_charset_info);
+ String type;
+ String def_value;
Field **ptr,*field;
uint primary_key;
KEY *key_info;
@@ -1887,12 +1879,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" "));
append_identifier(thd,packet,field->field_name, strlen(field->field_name));
packet->append(' ');
- // check for surprises from the previous call to Field::sql_type()
- if (type.ptr() != tmp)
- type.set(tmp, sizeof(tmp), system_charset_info);
- else
- type.set_charset(system_charset_info);
+ type.set(tmp, sizeof(tmp), system_charset_info);
field->sql_type(type);
packet->append(type.ptr(), type.length(), system_charset_info);
@@ -1939,6 +1927,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" NULL"));
}
+ def_value.set(def_value_buf, sizeof(def_value_buf), system_charset_info);
if (get_field_default_value(thd, field, &def_value, 1))
{
packet->append(STRING_WITH_LEN(" DEFAULT "));
@@ -3223,6 +3212,132 @@ void remove_status_vars(SHOW_VAR *list)
}
+/**
+ @brief Returns the value of a system or a status variable.
+
+ @param thd [in] The handle of the current THD.
+ @param variable [in] Details of the variable.
+ @param value_type [in] Variable type.
+ @param show_type [in] Variable show type.
+ @param charset [out] Character set of the value.
+ @param buff [in,out] Buffer to store the value.
+ (Needs to have enough memory
+ to hold the value of variable.)
+ @param length [out] Length of the value.
+
+ @return Pointer to the value buffer.
+*/
+
+const char* get_one_variable(THD *thd,
+ const SHOW_VAR *variable,
+ enum_var_type value_type, SHOW_TYPE show_type,
+ system_status_var *status_var,
+ const CHARSET_INFO **charset, char *buff,
+ size_t *length)
+{
+ void *value= variable->value;
+ const char *pos= buff;
+ const char *end= buff;
+
+
+ if (show_type == SHOW_SYS)
+ {
+ sys_var *var= (sys_var *) value;
+ show_type= var->show_type();
+ value= var->value_ptr(thd, value_type, &null_lex_str);
+ *charset= var->charset(thd);
+ }
+
+ /*
+ note that value may be == buff. All SHOW_xxx code below
+ should still work in this case
+ */
+ switch (show_type) {
+ case SHOW_DOUBLE_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_DOUBLE:
+ /* 6 is the default precision for '%f' in sprintf() */
+ end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
+ break;
+ case SHOW_LONG_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_ULONG:
+ case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
+ end= int10_to_str(*(long*) value, buff, 10);
+ break;
+ case SHOW_LONGLONG_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_ULONGLONG:
+ end= longlong10_to_str(*(longlong*) value, buff, 10);
+ break;
+ case SHOW_HA_ROWS:
+ end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
+ break;
+ case SHOW_BOOL:
+ end= strmov(buff, *(bool*) value ? "ON" : "OFF");
+ break;
+ case SHOW_MY_BOOL:
+ end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
+ break;
+ case SHOW_UINT:
+ end= int10_to_str((long) *(uint*) value, buff, 10);
+ break;
+ case SHOW_SINT:
+ end= int10_to_str((long) *(int*) value, buff, -10);
+ break;
+ case SHOW_SLONG:
+ end= int10_to_str(*(long*) value, buff, -10);
+ break;
+ case SHOW_SLONGLONG:
+ end= longlong10_to_str(*(longlong*) value, buff, -10);
+ break;
+ case SHOW_HAVE:
+ {
+ SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
+ pos= show_comp_option_name[(int) tmp];
+ end= strend(pos);
+ break;
+ }
+ case SHOW_CHAR:
+ {
+ if (!(pos= (char*)value))
+ pos= "";
+ end= strend(pos);
+ break;
+ }
+ case SHOW_CHAR_PTR:
+ {
+ if (!(pos= *(char**) value))
+ pos= "";
+
+ end= strend(pos);
+ break;
+ }
+ case SHOW_LEX_STRING:
+ {
+ LEX_STRING *ls=(LEX_STRING*)value;
+ if (!(pos= ls->str))
+ end= pos= "";
+ else
+ end= pos + ls->length;
+ break;
+ }
+ case SHOW_UNDEF:
+ break; // Return empty string
+ case SHOW_SYS: // Cannot happen
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+
+ *length= (size_t) (end - pos);
+ return pos;
+}
+
+
static bool show_status_array(THD *thd, const char *wild,
SHOW_VAR *variables,
enum enum_var_type scope,
@@ -3335,109 +3450,21 @@ static bool show_status_array(THD *thd, const char *wild,
name_buffer, wild))) &&
(!cond || cond->val_int()))
{
- void *value=var->value;
- const char *pos, *end; // We assign a lot of const's
+ const char *pos; // We assign a lot of const's
+ size_t length;
if (show_type == SHOW_SYS)
- {
- sys_var *var= (sys_var *) value;
- show_type= var->show_type();
mysql_mutex_lock(&LOCK_global_system_variables);
- value= var->value_ptr(thd, scope, &null_lex_str);
- charset= var->charset(thd);
- }
-
- pos= end= buff;
- /*
- note that value may be == buff. All SHOW_xxx code below
- should still work in this case
- */
- switch (show_type) {
- case SHOW_DOUBLE_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_DOUBLE:
- /* 6 is the default precision for '%f' in sprintf() */
- end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
- break;
- case SHOW_LONG_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_ULONG:
- case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
- end= int10_to_str(*(long*) value, buff, 10);
- break;
- case SHOW_LONGLONG_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_ULONGLONG:
- end= longlong10_to_str(*(longlong*) value, buff, 10);
- break;
- case SHOW_HA_ROWS:
- end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
- break;
- case SHOW_BOOL:
- end= strmov(buff, *(bool*) value ? "ON" : "OFF");
- break;
- case SHOW_MY_BOOL:
- end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
- break;
- case SHOW_UINT:
- end= int10_to_str((long) *(uint*) value, buff, 10);
- break;
- case SHOW_SINT:
- end= int10_to_str((long) *(int*) value, buff, -10);
- break;
- case SHOW_SLONG:
- end= int10_to_str(*(long*) value, buff, -10);
- break;
- case SHOW_SLONGLONG:
- end= longlong10_to_str(*(longlong*) value, buff, -10);
- break;
- case SHOW_HAVE:
- {
- SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
- pos= show_comp_option_name[(int) tmp];
- end= strend(pos);
- break;
- }
- case SHOW_CHAR:
- {
- if (!(pos= (char*)value))
- pos= "";
- end= strend(pos);
- break;
- }
- case SHOW_CHAR_PTR:
- {
- if (!(pos= *(char**) value))
- pos= "";
+ pos= get_one_variable(thd, var, scope, show_type, status_var,
+ &charset, buff, &length);
- end= strend(pos);
- break;
- }
- case SHOW_LEX_STRING:
- {
- LEX_STRING *ls=(LEX_STRING*)value;
- if (!(pos= ls->str))
- end= pos= "";
- else
- end= pos + ls->length;
- break;
- }
- case SHOW_UNDEF:
- break; // Return empty string
- case SHOW_SYS: // Cannot happen
- default:
- DBUG_ASSERT(0);
- break;
- }
- table->field[1]->store(pos, (uint32) (end - pos), charset);
+ table->field[1]->store(pos, (uint32) length, charset);
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
table->field[1]->set_notnull();
-
- if (var->type == SHOW_SYS)
+ if (show_type == SHOW_SYS)
mysql_mutex_unlock(&LOCK_global_system_variables);
+
if (schema_table_store_record(thd, table))
{
res= TRUE;
@@ -7234,6 +7261,17 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
COND *partial_cond= make_cond_for_info_schema(thd, cond, tables);
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
+
+ /*
+ Avoid recursive LOCK_system_variables_hash acquisition in
+ intern_sys_var_ptr() by pre-syncing dynamic session variables.
+ */
+ if (scope == OPT_SESSION &&
+ (!thd->variables.dynamic_variables_ptr ||
+ global_system_variables.dynamic_variables_head >
+ thd->variables.dynamic_variables_head))
+ sync_dynamic_session_variables(thd, true);
+
res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars, scope),
scope, NULL, "", tables->table,
upper_case_names, partial_cond);
diff --git a/sql/sql_show.h b/sql/sql_show.h
index dbae2a42b39..e93b855450c 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -131,6 +131,12 @@ bool get_schema_tables_result(JOIN *join,
enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list);
+const char* get_one_variable(THD *thd, const SHOW_VAR *variable,
+ enum_var_type value_type, SHOW_TYPE show_type,
+ system_status_var *status_var,
+ const CHARSET_INFO **charset, char *buff,
+ size_t *length);
+
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
THD *find_thread_by_id(longlong id, bool query_id= false);
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 767154e019d..a5f266b2d2c 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1157,3 +1158,18 @@ uint convert_to_printable(char *to, size_t to_len,
*t= '\0';
return t - to;
}
+
+void String::q_net_store_length(ulonglong length)
+{
+ DBUG_ASSERT(Alloced_length >= (str_length + net_length_size(length)));
+ char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length);
+ str_length= pos - Ptr;
+}
+
+void String::q_net_store_data(const uchar *from, size_t length)
+{
+ DBUG_ASSERT(Alloced_length >= (str_length + length +
+ net_length_size(length)));
+ q_net_store_length(length);
+ q_append((const char *)from, length);
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 51a11c7a4ff..f53015fbd6b 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -359,7 +359,9 @@ public:
if (ALIGN_SIZE(arg_length+1) < Alloced_length)
{
char *new_ptr;
- if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0))))
+ if (!(new_ptr=(char*)
+ my_realloc(Ptr, arg_length,MYF((thread_specific ?
+ MY_THREAD_SPECIFIC : 0)))))
{
Alloced_length = 0;
real_alloc(arg_length);
@@ -495,6 +497,11 @@ public:
{
Ptr[str_length++] = c;
}
+ void q_append2b(const uint32 n)
+ {
+ int2store(Ptr + str_length, n);
+ str_length += 2;
+ }
void q_append(const uint32 n)
{
int4store(Ptr + str_length, n);
@@ -559,6 +566,7 @@ public:
return Ptr+ old_length; /* Area to use */
}
+
inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
{
uint32 new_length= arg_length + str_length;
@@ -623,6 +631,8 @@ public:
{
return !sortcmp(this, other, cs);
}
+ void q_net_store_length(ulonglong length);
+ void q_net_store_data(const uchar *from, size_t length);
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 34b30f29a96..0cee0dc6ad2 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -55,6 +55,7 @@
#include "transaction.h"
#include "sql_audit.h"
+
#ifdef __WIN__
#include <io.h>
#endif
@@ -2686,14 +2687,15 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
*/
bool quick_rm_table(THD *thd, handlerton *base, const char *db,
- const char *table_name, uint flags)
+ const char *table_name, uint flags, const char *table_path)
{
char path[FN_REFLEN + 1];
bool error= 0;
DBUG_ENTER("quick_rm_table");
- uint path_length= build_table_filename(path, sizeof(path) - 1,
- db, table_name, reg_ext, flags);
+ uint path_length= table_path ?
+ (strxnmov(path, sizeof(path) - 1, table_path, reg_ext, NullS) - path) :
+ build_table_filename(path, sizeof(path)-1, db, table_name, reg_ext, flags);
if (mysql_file_delete(key_file_frm, path, MYF(0)))
error= 1; /* purecov: inspected */
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
@@ -3255,35 +3257,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
!(sql_field->charset= find_bin_collation(sql_field->charset)))
DBUG_RETURN(TRUE);
- /*
- Convert the default value from client character
- set into the column character set if necessary.
- We can only do this for constants as we have not yet run fix_fields.
- */
- if (sql_field->default_value &&
- sql_field->default_value->expr_item->basic_const_item() &&
- save_cs != sql_field->default_value->expr_item->collation.collation &&
- (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
- sql_field->sql_type == MYSQL_TYPE_STRING ||
- sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_BLOB ||
- sql_field->sql_type == MYSQL_TYPE_ENUM))
- {
- Item *item;
- if (!(item= sql_field->default_value->expr_item->
- safe_charset_converter(thd, save_cs)))
- {
- /* Could not convert */
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- /* Fix for prepare statement */
- thd->change_item_tree(&sql_field->default_value->expr_item, item);
- }
-
if (sql_field->sql_type == MYSQL_TYPE_SET ||
sql_field->sql_type == MYSQL_TYPE_ENUM)
{
@@ -3349,37 +3322,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (sql_field->sql_type == MYSQL_TYPE_SET)
{
uint32 field_length;
- if (sql_field->default_value &&
- sql_field->default_value->expr_item->basic_const_item())
- {
- char *not_used;
- uint not_used2;
- bool not_found= 0;
- String str, *def= sql_field->default_value->expr_item->val_str(&str);
- if (def == NULL) /* SQL "NULL" maps to NULL */
- {
- if ((sql_field->flags & NOT_NULL_FLAG) != 0)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
-
- /* else, NULL is an allowed value */
- (void) find_set(interval, NULL, 0,
- cs, &not_used, &not_used2, &not_found);
- }
- else /* not NULL */
- {
- (void) find_set(interval, def->ptr(), def->length(),
- cs, &not_used, &not_used2, &not_found);
- }
-
- if (not_found)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
calculate_interval_lengths(cs, interval, &dummy, &field_length);
sql_field->length= field_length + (interval->count - 1);
}
@@ -3387,30 +3329,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
uint32 field_length;
DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
- if (sql_field->default_value &&
- sql_field->default_value->expr_item->basic_const_item())
- {
- String str, *def= sql_field->default_value->expr_item->val_str(&str);
- if (def == NULL) /* SQL "NULL" maps to NULL */
- {
- if ((sql_field->flags & NOT_NULL_FLAG) != 0)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
-
- /* else, the defaults yield the correct length for NULLs. */
- }
- else /* not NULL */
- {
- def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
- if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
- }
calculate_interval_lengths(cs, interval, &field_length, &dummy);
sql_field->length= field_length;
}
@@ -3430,6 +3348,112 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (prepare_blob_field(thd, sql_field))
DBUG_RETURN(TRUE);
+ if (sql_field->default_value)
+ {
+ Virtual_column_info *def= sql_field->default_value;
+
+ if (!sql_field->has_default_expression())
+ def->expr_str= null_lex_str;
+
+ if (!def->expr_item->basic_const_item() && !def->flags)
+ {
+ Item *expr= def->expr_item;
+ int err= !expr->fixed && // may be already fixed if ALTER TABLE
+ expr->fix_fields(thd, &expr);
+ if (!err)
+ {
+ if (expr->result_type() == REAL_RESULT)
+ { // don't convert floats to string and back, it can be lossy
+ double res= expr->val_real();
+ if (expr->null_value)
+ expr= new (thd->mem_root) Item_null(thd);
+ else
+ expr= new (thd->mem_root) Item_float(thd, res, expr->decimals);
+ }
+ else
+ {
+ StringBuffer<MAX_FIELD_WIDTH> buf;
+ String *res= expr->val_str(&buf);
+ if (expr->null_value)
+ expr= new (thd->mem_root) Item_null(thd);
+ else
+ {
+ char *str= (char*) thd->strmake(res->ptr(), res->length());
+ expr= new (thd->mem_root) Item_string(thd, str, res->length(), res->charset());
+ }
+ }
+ thd->change_item_tree(&def->expr_item, expr);
+ }
+ }
+ }
+
+ /*
+ Convert the default value from client character
+ set into the column character set if necessary.
+ We can only do this for constants as we have not yet run fix_fields.
+ */
+ if (sql_field->default_value &&
+ sql_field->default_value->expr_item->basic_const_item() &&
+ save_cs != sql_field->default_value->expr_item->collation.collation &&
+ (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM))
+ {
+ Item *item;
+ if (!(item= sql_field->default_value->expr_item->
+ safe_charset_converter(thd, save_cs)))
+ {
+ /* Could not convert */
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ /* Fix for prepare statement */
+ thd->change_item_tree(&sql_field->default_value->expr_item, item);
+ }
+
+ if (sql_field->default_value &&
+ sql_field->default_value->expr_item->basic_const_item() &&
+ (sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM))
+ {
+ StringBuffer<MAX_FIELD_WIDTH> str;
+ String *def= sql_field->default_value->expr_item->val_str(&str);
+ bool not_found;
+ if (def == NULL) /* SQL "NULL" maps to NULL */
+ {
+ not_found= sql_field->flags & NOT_NULL_FLAG;
+ }
+ else
+ {
+ not_found= false;
+ if (sql_field->sql_type == MYSQL_TYPE_SET)
+ {
+ char *not_used;
+ uint not_used2;
+ find_set(sql_field->interval, def->ptr(), def->length(),
+ sql_field->charset, &not_used, &not_used2, &not_found);
+ }
+ else /* MYSQL_TYPE_ENUM */
+ {
+ def->length(sql_field->charset->cset->lengthsp(sql_field->charset,
+ def->ptr(), def->length()));
+ not_found= !find_type2(sql_field->interval, def->ptr(),
+ def->length(), sql_field->charset);
+ }
+ }
+
+ if (not_found)
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ }
+
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields++;
@@ -4178,6 +4202,22 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
make_unique_constraint_name(thd, &check->name,
&alter_info->check_constraint_list,
&nr);
+ {
+ /* Check that there's no repeating constraint names. */
+ List_iterator_fast<Virtual_column_info>
+ dup_it(alter_info->check_constraint_list);
+ Virtual_column_info *dup_check;
+ while ((dup_check= dup_it++) && dup_check != check)
+ {
+ if (check->name.length == dup_check->name.length &&
+ my_strcasecmp(system_charset_info,
+ check->name.str, dup_check->name.str) == 0)
+ {
+ my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN,
system_charset_info, 1))
@@ -6130,6 +6170,39 @@ remove_key:
}
#endif /*WITH_PARTITION_STORAGE_ENGINE*/
+ /* ADD CONSTRAINT IF NOT EXISTS. */
+ {
+ List_iterator<Virtual_column_info> it(alter_info->check_constraint_list);
+ Virtual_column_info *check;
+ TABLE_SHARE *share= table->s;
+ uint c;
+ while ((check=it++))
+ {
+ if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
+ check->name.length)
+ continue;
+ check->flags= 0;
+ for (c= share->field_check_constraints;
+ c < share->table_check_constraints ; c++)
+ {
+ Virtual_column_info *dup= table->check_constraints[c];
+ if (dup->name.length == check->name.length &&
+ my_strcasecmp(system_charset_info,
+ check->name.str, dup->name.str) == 0)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_CONSTRAINT_NAME, ER_THD(thd, ER_DUP_CONSTRAINT_NAME),
+ "CHECK", check->name.str);
+ it.remove();
+ if (alter_info->check_constraint_list.elements == 0)
+ alter_info->flags&= ~Alter_info::ALTER_ADD_CHECK_CONSTRAINT;
+
+ break;
+ }
+ }
+ }
+ }
+
DBUG_VOID_RETURN;
}
@@ -8257,6 +8330,72 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
DBUG_RETURN(false);
}
+/**
+ Rename temporary table and/or turn indexes on/off without touching .FRM.
+ Its a variant of simple_rename_or_index_change() to be used exclusively
+ for temporary tables.
+
+ @param thd Thread handler
+ @param table_list TABLE_LIST for the table to change
+ @param keys_onoff ENABLE or DISABLE KEYS?
+ @param alter_ctx ALTER TABLE runtime context.
+
+ @return Operation status
+ @retval false Success
+ @retval true Failure
+*/
+static bool
+simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
+ Alter_info::enum_enable_or_disable keys_onoff,
+ Alter_table_ctx *alter_ctx)
+{
+ DBUG_ENTER("simple_tmp_rename_or_index_change");
+
+ TABLE *table= table_list->table;
+ bool error= false;
+
+ DBUG_ASSERT(table->s->tmp_table);
+
+ if (keys_onoff != Alter_info::LEAVE_AS_IS)
+ {
+ THD_STAGE_INFO(thd, stage_manage_keys);
+ error= alter_table_manage_keys(table, table->file->indexes_are_disabled(),
+ keys_onoff);
+ }
+
+ if (!error && alter_ctx->is_table_renamed())
+ {
+ THD_STAGE_INFO(thd, stage_rename);
+
+ /*
+ If THD::rename_temporary_table() fails, there is no need to rename it
+ back to the original name (unlike the case for non-temporary tables),
+ as it was an allocation error and the table was not renamed.
+ */
+ error= thd->rename_temporary_table(table, alter_ctx->new_db,
+ alter_ctx->new_alias);
+ }
+
+ if (!error)
+ {
+ int res= 0;
+ /*
+ We do not replicate alter table statement on temporary tables under
+ ROW-based replication.
+ */
+ if (!thd->is_current_stmt_binlog_format_row())
+ {
+ res= write_bin_log(thd, true, thd->query(), thd->query_length());
+ }
+ if (res != 0)
+ error= true;
+ else
+ my_ok(thd);
+ }
+
+ DBUG_RETURN(error);
+}
+
/**
Rename table and/or turn indexes on/off without touching .FRM
@@ -8293,6 +8432,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
DBUG_RETURN(true);
+ THD_STAGE_INFO(thd, stage_manage_keys);
error= alter_table_manage_keys(table,
table->file->indexes_are_disabled(),
keys_onoff);
@@ -8671,29 +8811,48 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
- if (write_bin_log(thd, true, thd->query(), thd->query_length()))
- DBUG_RETURN(true);
+ /* We don't replicate alter table statement on temporary tables */
+ if (table->s->tmp_table == NO_TMP_TABLE ||
+ !thd->is_current_stmt_binlog_format_row())
+ {
+ if (write_bin_log(thd, true, thd->query(), thd->query_length()))
+ DBUG_RETURN(true);
+ }
DBUG_RETURN(false);
}
+ /*
+ Test if we are only doing RENAME or KEYS ON/OFF. This works
+ as we are testing if flags == 0 above.
+ */
if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
Alter_info::ALTER_KEYS_ONOFF)) &&
alter_info->requested_algorithm !=
- Alter_info::ALTER_TABLE_ALGORITHM_COPY &&
- !table->s->tmp_table) // no need to touch frm
+ Alter_info::ALTER_TABLE_ALGORITHM_COPY) // No need to touch frm.
{
- // This requires X-lock, no other lock levels supported.
- if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
- alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ bool res;
+
+ if (!table->s->tmp_table)
{
- my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
- "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
- DBUG_RETURN(true);
+ // This requires X-lock, no other lock levels supported.
+ if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
+ alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ {
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
+ DBUG_RETURN(true);
+ }
+ res= simple_rename_or_index_change(thd, table_list,
+ alter_info->keys_onoff,
+ &alter_ctx);
+ }
+ else
+ {
+ res= simple_tmp_rename_or_index_change(thd, table_list,
+ alter_info->keys_onoff,
+ &alter_ctx);
}
- bool res= simple_rename_or_index_change(thd, table_list,
- alter_info->keys_onoff,
- &alter_ctx);
DBUG_RETURN(res);
}
@@ -9123,6 +9282,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
goto err_new_table_cleanup;
}
+ /* in case of alter temp table send the tracker in OK packet */
+ SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
}
@@ -9383,7 +9544,8 @@ err_new_table_cleanup:
else
(void) quick_rm_table(thd, new_db_type,
alter_ctx.new_db, alter_ctx.tmp_name,
- (FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)));
+ (FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)),
+ alter_ctx.get_tmp_path());
/*
No default value was provided for a DATE/DATETIME field, the
@@ -9424,6 +9586,9 @@ err_new_table_cleanup:
err_with_mdl_after_alter:
/* the table was altered. binlog the operation */
+ DBUG_ASSERT(!(mysql_bin_log.is_open() &&
+ thd->is_current_stmt_binlog_format_row() &&
+ (create_info->tmp_table())));
write_bin_log(thd, true, thd->query(), thd->query_length());
err_with_mdl:
@@ -9562,8 +9727,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
Old fields keep their current values, and therefore should not be
present in the set of autoupdate fields.
*/
- if ((*ptr)->default_value ||
- ((*ptr)->has_insert_default_function()))
+ if ((*ptr)->default_value)
{
*(dfield_ptr++)= *ptr;
++to->s->default_fields;
@@ -9740,7 +9904,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
THD_STAGE_INFO(thd, stage_enabling_keys);
thd_progress_next_stage(thd);
- if (error > 0)
+ if (error > 0 && !from->s->tmp_table)
{
/* We are going to drop the temporary table */
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
@@ -9769,7 +9933,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->ha_release_auto_increment();
if (to->file->ha_external_lock(thd,F_UNLCK))
error=1;
- if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
+ if (error < 0 && !from->s->tmp_table &&
+ to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
error= 1;
thd_progress_end(thd);
DBUG_RETURN(error > 0 ? -1 : 0);
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 109da541a28..628c51f678f 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -248,7 +248,8 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
const char *table_name, size_t table_name_length,
bool temporary_table);
bool quick_rm_table(THD *thd, handlerton *base, const char *db,
- const char *table_name, uint flags);
+ const char *table_name, uint flags,
+ const char *table_path=0);
void close_cached_table(THD *thd, TABLE *table);
void sp_prepare_create_field(THD *thd, Column_definition *sql_field);
int prepare_create_field(Column_definition *sql_field,
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 406c95fa755..854ebb99ef2 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -28,6 +28,8 @@
#include "sql_cursor.h"
#include "sql_base.h" // fill_record
#include "filesort.h" // filesort_free_buffers
+#include "sql_view.h"
+#include "sql_cte.h"
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option)
@@ -99,6 +101,27 @@ int select_union::send_data(List<Item> &values)
}
+int select_union_recursive::send_data(List<Item> &values)
+{
+ int rc= select_union::send_data(values);
+
+ if (!write_err)
+ {
+ int err;
+ if ((err= incr_table->file->ha_write_tmp_row(table->record[0])))
+ {
+ bool is_duplicate;
+ rc= create_internal_tmp_table_from_heap(thd, incr_table,
+ tmp_table_param.start_recinfo,
+ &tmp_table_param.recinfo,
+ err, 1, &is_duplicate);
+ }
+ }
+
+ return rc;
+}
+
+
bool select_union::send_eof()
{
return 0;
@@ -171,6 +194,55 @@ select_union::create_result_table(THD *thd_arg, List<Item> *column_types,
return FALSE;
}
+bool
+select_union_recursive::create_result_table(THD *thd_arg,
+ List<Item> *column_types,
+ bool is_union_distinct,
+ ulonglong options,
+ const char *alias,
+ bool bit_fields_as_long,
+ bool create_table,
+ bool keep_row_order)
+{
+ if (select_union::create_result_table(thd_arg, column_types,
+ is_union_distinct, options,
+ "", bit_fields_as_long,
+ create_table, keep_row_order))
+ return true;
+
+ if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
+ (ORDER*) 0, false, 1,
+ options, HA_POS_ERROR, "",
+ !create_table, keep_row_order)))
+ return true;
+
+ incr_table->keys_in_use_for_query.clear_all();
+ for (uint i=0; i < table->s->fields; i++)
+ incr_table->field[i]->flags &= ~PART_KEY_FLAG;
+
+ if (create_table)
+ {
+ incr_table->file->extra(HA_EXTRA_WRITE_CACHE);
+ incr_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ }
+
+ TABLE *rec_table= 0;
+ if (! (rec_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
+ (ORDER*) 0, false, 1,
+ options, HA_POS_ERROR, alias,
+ true, keep_row_order)))
+ return true;
+
+ rec_table->keys_in_use_for_query.clear_all();
+ for (uint i=0; i < table->s->fields; i++)
+ rec_table->field[i]->flags &= ~PART_KEY_FLAG;
+
+ if (rec_tables.push_back(rec_table))
+ return true;
+
+ return false;
+}
+
/**
Reset and empty the temporary table that stores the materialized query
@@ -187,6 +259,34 @@ void select_union::cleanup()
}
+void select_union_recursive::cleanup()
+{
+ if (table)
+ {
+ select_union::cleanup();
+ free_tmp_table(thd, table);
+ }
+
+ if (incr_table)
+ {
+ incr_table->file->extra(HA_EXTRA_RESET_STATE);
+ incr_table->file->ha_delete_all_rows();
+ free_tmp_table(thd, incr_table);
+ }
+
+ List_iterator<TABLE> it(rec_tables);
+ TABLE *tab;
+ while ((tab= it++))
+ {
+ if (tab->is_created())
+ {
+ tab->file->extra(HA_EXTRA_RESET_STATE);
+ tab->file->ha_delete_all_rows();
+ }
+ free_tmp_table(thd, tab);
+ }
+}
+
/**
Replace the current result with new_result and prepare it.
@@ -332,11 +432,15 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
}
+
+
bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
ulong additional_options)
{
SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
+ bool is_recursive= with_element && with_element->is_recursive;
+ bool is_rec_result_table_created= false;
select_result *tmp_result;
bool is_union_select;
bool instantiate_tmp_table= false;
@@ -388,7 +492,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
/* Global option */
- if (is_union_select)
+ if (is_union_select || is_recursive)
{
if (is_union() && !union_needs_tmp_table())
{
@@ -404,8 +508,16 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
else
{
- if (!(tmp_result= union_result=
- new (thd_arg->mem_root) select_union(thd_arg)))
+ if (!is_recursive)
+ union_result= new (thd_arg->mem_root) select_union(thd_arg);
+ else
+ {
+ with_element->rec_result=
+ new (thd_arg->mem_root) select_union_recursive(thd_arg);
+ union_result= with_element->rec_result;
+ fake_select_lex= NULL;
+ }
+ if (!(tmp_result= union_result))
goto err; /* purecov: inspected */
instantiate_tmp_table= true;
}
@@ -414,9 +526,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
tmp_result= sel_result;
sl->context.resolve_in_select_list= TRUE;
-
+
for (;sl; sl= sl->next_select())
- {
+ {
bool can_skip_order_by;
sl->options|= SELECT_NO_UNLOCK;
JOIN *join= new JOIN(thd_arg, sl->item_list,
@@ -473,10 +585,17 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Use items list of underlaid select for derived tables to preserve
information about fields lengths and exact types
*/
- if (!is_union_select)
+ if (!is_union_select && !is_recursive)
types= first_sl->item_list;
else if (sl == first_sl)
{
+ if (is_recursive)
+ {
+ if (derived->with->rename_columns_of_derived_unit(thd, this))
+ goto err;
+ if (check_duplicate_names(thd, sl->item_list, 0))
+ goto err;
+ }
types.empty();
List_iterator_fast<Item> it(sl->item_list);
Item *item_tmp;
@@ -498,15 +617,42 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
goto err;
}
- List_iterator_fast<Item> it(sl->item_list);
- List_iterator_fast<Item> tp(types);
- Item *type, *item_tmp;
- while ((type= tp++, item_tmp= it++))
+ if (!is_rec_result_table_created)
{
- if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
- DBUG_RETURN(TRUE);
+ List_iterator_fast<Item> it(sl->item_list);
+ List_iterator_fast<Item> tp(types);
+ Item *type, *item_tmp;
+ while ((type= tp++, item_tmp= it++))
+ {
+ if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
+ DBUG_RETURN(TRUE);
+ }
}
}
+ if (is_recursive)
+ {
+ if (!with_element->is_anchor(sl))
+ sl->uncacheable|= UNCACHEABLE_UNITED;
+ if(!is_rec_result_table_created &&
+ (!sl->next_select() ||
+ sl->next_select() == with_element->first_recursive))
+ {
+ ulonglong create_options;
+ create_options= (first_sl->options | thd_arg->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS);
+ if (union_result->create_result_table(thd, &types,
+ MY_TEST(union_distinct),
+ create_options, derived->alias,
+ false,
+ instantiate_tmp_table, false))
+ goto err;
+ if (!derived->table)
+ derived->table= derived->derived_result->table=
+ with_element->rec_result->rec_tables.head();
+ with_element->mark_as_with_prepared_anchor();
+ is_rec_result_table_created= true;
+ }
+ }
}
/*
@@ -579,9 +725,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
- if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
- create_options, "", false,
- instantiate_tmp_table))
+
+ if (!is_recursive &&
+ union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
+ create_options, "", false,
+ instantiate_tmp_table, false))
goto err;
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
@@ -689,6 +837,10 @@ bool st_select_lex_unit::optimize()
if (optimized && !uncacheable && !describe)
DBUG_RETURN(FALSE);
+ if (with_element && with_element->is_recursive && optimize_started)
+ DBUG_RETURN(FALSE);
+ optimize_started= true;
+
if (uncacheable || !item || !item->assigned() || describe)
{
if (item)
@@ -785,7 +937,7 @@ bool st_select_lex_unit::exec()
if (uncacheable || !item || !item->assigned() || describe)
{
- if (!fake_select_lex)
+ if (!fake_select_lex && !(with_element && with_element->is_recursive))
union_result->cleanup();
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
@@ -826,7 +978,7 @@ bool st_select_lex_unit::exec()
{
records_at_start= table->file->stats.records;
sl->join->exec();
- if (sl == union_distinct)
+ if (sl == union_distinct && !(with_element && with_element->is_recursive))
{
// This is UNION DISTINCT, so there should be a fake_select_lex
DBUG_ASSERT(fake_select_lex != NULL);
@@ -1008,6 +1160,106 @@ err:
}
+/**
+ @brief
+ Execute the union of the specification of a recursive with table
+
+ @details
+ The method is performed only for the units that are specifications
+ if recursive with table T. If the specification contains an anchor
+ part then the first call of this method executes only this part
+ while the following calls execute the recursive part. If there are
+ no anchors each call executes the whole unit.
+ Before the excution the method cleans up the temporary table
+ to where the new rows of the recursive table are sent.
+ After the execution the unit these rows are copied to the
+ temporary tables created for recursive references of T.
+ If the specification if T is restricted (standards compliant)
+ then these temporary tables are cleaned up before new rows
+ are copied into them.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool st_select_lex_unit::exec_recursive()
+{
+ st_select_lex *lex_select_save= thd->lex->current_select;
+ st_select_lex *start= with_element->first_recursive;
+ TABLE *incr_table= with_element->rec_result->incr_table;
+ st_select_lex *end= NULL;
+ bool is_unrestricted= with_element->is_unrestricted();
+ List_iterator_fast<TABLE> li(with_element->rec_result->rec_tables);
+ ha_rows examined_rows= 0;
+ bool was_executed= executed;
+ TABLE *rec_table;
+
+ DBUG_ENTER("st_select_lex_unit::exec_recursive");
+
+ executed= 1;
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ if (!was_executed)
+ save_union_explain(thd->lex->explain);
+
+ if ((saved_error= incr_table->file->ha_delete_all_rows()))
+ goto err;
+
+ if (with_element->level == 0)
+ {
+ start= first_select();
+ if (with_element->with_anchor)
+ end= with_element->first_recursive;
+ }
+
+ for (st_select_lex *sl= start ; sl != end; sl= sl->next_select())
+ {
+ thd->lex->current_select= sl;
+ sl->join->exec();
+ saved_error= sl->join->error;
+ if (!saved_error)
+ {
+ examined_rows+= thd->get_examined_row_count();
+ thd->set_examined_row_count(0);
+ if (union_result->flush())
+ {
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(1);
+ }
+ }
+ if (saved_error)
+ {
+ thd->lex->current_select= lex_select_save;
+ goto err;
+
+ }
+ }
+
+ thd->inc_examined_row_count(examined_rows);
+
+ incr_table->file->info(HA_STATUS_VARIABLE);
+ if (incr_table->file->stats.records == 0)
+ with_element->set_as_stabilized();
+ else
+ with_element->level++;
+
+ while ((rec_table= li++))
+ {
+ saved_error=
+ incr_table->insert_all_rows_into(thd, rec_table, !is_unrestricted);
+ if (!with_element->rec_result->first_rec_table_to_update)
+ with_element->rec_result->first_rec_table_to_update= rec_table;
+ if (with_element->level == 1)
+ rec_table->reginfo.join_tab->preread_init_done= true;
+ }
+
+ thd->lex->current_select= lex_select_save;
+err:
+ thd->lex->set_limit_rows_examined();
+ DBUG_RETURN(saved_error);
+}
+
+
bool st_select_lex_unit::cleanup()
{
int error= 0;
@@ -1021,7 +1273,7 @@ bool st_select_lex_unit::cleanup()
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
error|= sl->cleanup();
-
+
if (fake_select_lex)
{
error|= fake_select_lex->cleanup();
@@ -1045,13 +1297,26 @@ bool st_select_lex_unit::cleanup()
}
}
- if (union_result)
+ if (with_element && with_element->is_recursive)
{
- delete union_result;
- union_result=0; // Safety
- if (table)
- free_tmp_table(thd, table);
- table= 0; // Safety
+ if (union_result )
+ {
+ ((select_union_recursive *) union_result)->cleanup();
+ delete union_result;
+ union_result= 0;
+ }
+ with_element->mark_as_cleaned();
+ }
+ else
+ {
+ if (union_result)
+ {
+ delete union_result;
+ union_result=0; // Safety
+ if (table)
+ free_tmp_table(thd, table);
+ table= 0; // Safety
+ }
}
DBUG_RETURN(error);
@@ -1061,6 +1326,7 @@ bool st_select_lex_unit::cleanup()
void st_select_lex_unit::reinit_exec_mechanism()
{
prepared= optimized= executed= 0;
+ optimize_started= 0;
#ifndef DBUG_OFF
if (is_union())
{
@@ -1077,6 +1343,8 @@ void st_select_lex_unit::reinit_exec_mechanism()
}
}
#endif
+ if (with_element && with_element->is_recursive)
+ with_element->reset_recursive_for_exec();
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index d59b8b7e048..b452e4fe6ae 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -526,6 +526,9 @@ int mysql_update(THD *thd,
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
dbug_serve_apcs(thd, 1););
+ if (!(select && select->quick))
+ status_var_increment(thd->status_var.update_scan_count);
+
if (query_plan.using_filesort || query_plan.using_io_buffer)
{
/*
@@ -583,6 +586,7 @@ int mysql_update(THD *thd,
close_cached_file(&tempfile);
goto err;
}
+
table->file->try_semi_consistent_read(1);
/*
@@ -621,7 +625,7 @@ int mysql_update(THD *thd,
thd->inc_examined_row_count(1);
if (!select || (error= select->skip_record(thd)) > 0)
{
- if (table->file->was_semi_consistent_read())
+ if (table->file->ha_was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */
explain->buf_tracker.on_record_after_where();
@@ -740,7 +744,7 @@ int mysql_update(THD *thd,
thd->inc_examined_row_count(1);
if (!select || select->skip_record(thd) > 0)
{
- if (table->file->was_semi_consistent_read())
+ if (table->file->ha_was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */
explain->tracker.on_record_after_where();
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index b33590637b9..c0d9ba48121 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -35,6 +35,7 @@
#include "sp_cache.h"
#include "datadict.h" // dd_frm_is_view()
#include "sql_derived.h"
+#include "sql_cte.h" // check_dependencies_in_with_clauses()
#define MD5_BUFF_LENGTH 33
@@ -429,6 +430,21 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local);
view->open_type= OT_BASE_ONLY;
+ if (check_dependencies_in_with_clauses(lex->with_clauses_list))
+ {
+ res= TRUE;
+ goto err;
+ }
+
+ /*
+ ignore lock specs for CREATE statement
+ */
+ if (lex->current_select->lock_type != TL_READ_DEFAULT)
+ {
+ lex->current_select->set_lock_for_tables(TL_READ_DEFAULT);
+ view->mdl_request.set_type(MDL_EXCLUSIVE);
+ }
+
if (thd->open_temporary_tables(lex->query_tables) ||
open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
{
@@ -1383,6 +1399,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
TABLE_LIST *tbl;
Security_context *security_ctx= 0;
+ if (check_dependencies_in_with_clauses(thd->lex->with_clauses_list))
+ goto err;
+
/*
Check rights to run commands (ANALYZE SELECT, EXPLAIN SELECT &
SHOW CREATE) which show underlying tables.
@@ -1612,7 +1631,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
sl->context.error_processor_data= (void *)table;
}
- table->select_lex->master_unit()->is_view= true;
+ view_select->master_unit()->is_view= true;
/*
check MERGE algorithm ability
diff --git a/sql/sql_window.cc b/sql/sql_window.cc
index 4c5ef53d854..e720c39eb8f 100644
--- a/sql/sql_window.cc
+++ b/sql/sql_window.cc
@@ -4,6 +4,7 @@
#include "filesort.h"
#include "sql_base.h"
#include "sql_window.h"
+#include "my_dbug.h"
bool
@@ -37,12 +38,12 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
if (win_spec->order_list->elements && order_list->elements)
{
my_error(ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC, MYF(0), ref_name);
- return true;
+ return true;
}
- if (win_spec->window_frame)
+ if (win_spec->window_frame)
{
my_error(ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC, MYF(0), ref_name);
- return true;
+ return true;
}
referenced_win_spec= win_spec;
if (partition_list->elements == 0)
@@ -54,7 +55,7 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
if (ref_name && !referenced_win_spec)
{
my_error(ER_WRONG_WINDOW_SPEC_NAME, MYF(0), ref_name);
- return true;
+ return true;
}
window_names_are_checked= true;
return false;
@@ -73,7 +74,7 @@ Window_frame::check_frame_bounds()
top_bound->precedence_type == Window_frame_bound::FOLLOWING))
{
my_error(ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS, MYF(0));
- return true;
+ return true;
}
return false;
@@ -86,7 +87,7 @@ Window_frame::check_frame_bounds()
int
setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
- List<Item> &fields, List<Item> &all_fields,
+ List<Item> &fields, List<Item> &all_fields,
List<Window_spec> &win_specs, List<Item_window_func> &win_funcs)
{
Window_spec *win_spec;
@@ -116,7 +117,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
it.rewind();
List_iterator_fast<Window_spec> itp(win_specs);
-
+
while ((win_spec= it++))
{
bool hidden_group_fields;
@@ -131,7 +132,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
{
DBUG_RETURN(1);
}
-
+
if (win_spec->window_frame &&
win_spec->window_frame->exclusion != Window_frame::EXCL_NONE)
{
@@ -188,7 +189,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
}
}
}
-
+
/* "ROWS PRECEDING|FOLLOWING $n" must have a numeric $n */
if (win_spec->window_frame &&
win_spec->window_frame->units == Window_frame::UNITS_ROWS)
@@ -219,7 +220,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
{
win_func_item->update_used_tables();
}
-
+
DBUG_RETURN(0);
}
@@ -445,7 +446,7 @@ typedef int (*Item_window_func_cmp)(Item_window_func *f1,
@brief
Sort window functions so that those that can be computed together are
adjacent.
-
+
@detail
Sort window functions by their
- required sorting order,
@@ -498,48 +499,15 @@ void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list)
}
else if (win_spec_prev->window_frame != win_spec_curr->window_frame)
curr->marker|= FRAME_CHANGE_FLAG;
-
- prev= curr;
- }
+
+ prev= curr;
+ }
}
/////////////////////////////////////////////////////////////////////////////
-/*
- Do a pass over sorted table and compute window function values.
-
- This function is for handling window functions that can be computed on the
- fly. Examples are RANK() and ROW_NUMBER().
-*/
-bool compute_window_func_values(Item_window_func *item_win,
- TABLE *tbl, READ_RECORD *info)
-{
- int err;
- while (!(err=info->read_record(info)))
- {
- store_record(tbl,record[1]);
-
- /*
- This will cause window function to compute its value for the
- current row :
- */
- item_win->advance_window();
-
- /*
- Put the new value into temptable's field
- TODO: Should this use item_win->update_field() call?
- Regular aggegate function implementations seem to implement it.
- */
- item_win->save_in_field(item_win->result_field, true);
- err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);
- if (err && err != HA_ERR_RECORD_IS_THE_SAME)
- return true;
- }
- return false;
-}
-
/////////////////////////////////////////////////////////////////////////////
// Window Frames support
/////////////////////////////////////////////////////////////////////////////
@@ -571,11 +539,6 @@ bool clone_read_record(const READ_RECORD *src, READ_RECORD *dst)
class Rowid_seq_cursor
{
- uchar *cache_start;
- uchar *cache_pos;
- uchar *cache_end;
- uint ref_length;
-
public:
virtual ~Rowid_seq_cursor() {}
@@ -587,29 +550,44 @@ public:
ref_length= info->ref_length;
}
- virtual int get_next()
+ virtual int next()
{
- /* Allow multiple get_next() calls in EOF state*/
+ /* Allow multiple next() calls in EOF state. */
if (cache_pos == cache_end)
return -1;
+
cache_pos+= ref_length;
+ DBUG_ASSERT(cache_pos <= cache_end);
+
return 0;
}
-
- ha_rows get_rownum()
+
+ virtual int prev()
+ {
+ /* Allow multiple prev() calls when positioned at the start. */
+ if (cache_pos == cache_start)
+ return -1;
+ cache_pos-= ref_length;
+ DBUG_ASSERT(cache_pos >= cache_start);
+
+ return 0;
+ }
+
+ ha_rows get_rownum() const
{
return (cache_pos - cache_start) / ref_length;
}
- // will be called by ROWS n FOLLOWING to catch up.
void move_to(ha_rows row_number)
{
- cache_pos= cache_start + row_number * ref_length;
+ cache_pos= MY_MIN(cache_end, cache_start + row_number * ref_length);
+ DBUG_ASSERT(cache_pos <= cache_end);
}
+
protected:
bool at_eof() { return (cache_pos == cache_end); }
- uchar *get_last_rowid()
+ uchar *get_prev_rowid()
{
if (cache_pos == cache_start)
return NULL;
@@ -618,6 +596,12 @@ protected:
}
uchar *get_curr_rowid() { return cache_pos; }
+
+private:
+ uchar *cache_start;
+ uchar *cache_pos;
+ uchar *cache_end;
+ uint ref_length;
};
@@ -627,66 +611,64 @@ protected:
class Table_read_cursor : public Rowid_seq_cursor
{
- /*
- Note: we don't own *read_record, somebody else is using it.
- We only look at the constant part of it, e.g. table, record buffer, etc.
- */
- READ_RECORD *read_record;
public:
virtual ~Table_read_cursor() {}
void init(READ_RECORD *info)
{
Rowid_seq_cursor::init(info);
- read_record= info;
+ table= info->table;
+ record= info->record;
}
- virtual int get_next()
+ virtual int fetch()
{
if (at_eof())
return -1;
uchar* curr_rowid= get_curr_rowid();
- int res= Rowid_seq_cursor::get_next();
- if (!res)
- {
- res= read_record->table->file->ha_rnd_pos(read_record->record,
- curr_rowid);
- }
- return res;
+ return table->file->ha_rnd_pos(record, curr_rowid);
}
- bool restore_last_row()
+ bool fetch_prev_row()
{
uchar *p;
- if ((p= get_last_rowid()))
+ if ((p= get_prev_rowid()))
{
- int rc= read_record->table->file->ha_rnd_pos(read_record->record, p);
+ int rc= table->file->ha_rnd_pos(record, p);
if (!rc)
return true; // restored ok
}
return false; // didn't restore
}
- // todo: should move_to() also read row here?
+private:
+ /* The table that is acccesed by this cursor. */
+ TABLE *table;
+ /* Buffer where to store the table's record data. */
+ uchar *record;
+
+ // TODO(spetrunia): should move_to() also read row here?
};
/*
A cursor which only moves within a partition. The scan stops at the partition
end, and it needs an explicit command to move to the next partition.
+
+ This cursor can not move backwards.
*/
-class Partition_read_cursor
+class Partition_read_cursor : public Table_read_cursor
{
- Table_read_cursor tbl_cursor;
- Group_bound_tracker bound_tracker;
- bool end_of_partition;
public:
- void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list)
+ Partition_read_cursor(THD *thd, SQL_I_List<ORDER> *partition_list) :
+ bound_tracker(thd, partition_list) {}
+
+ void init(READ_RECORD *info)
{
- tbl_cursor.init(info);
- bound_tracker.init(thd, partition_list);
+ Table_read_cursor::init(info);
+ bound_tracker.init();
end_of_partition= false;
}
@@ -699,46 +681,46 @@ public:
void on_next_partition(ha_rows rownum)
{
/* Remember the sort key value from the new partition */
+ move_to(rownum);
bound_tracker.check_if_next_group();
end_of_partition= false;
- }
- /*
- Moves to a new row. The row is assumed to be within the current partition
- */
- void move_to(ha_rows rownum) { tbl_cursor.move_to(rownum); }
+ }
/*
This returns -1 when end of partition was reached.
*/
- int get_next()
+ int next()
{
int res;
if (end_of_partition)
return -1;
- if ((res= tbl_cursor.get_next()))
+
+ if ((res= Table_read_cursor::next()) ||
+ (res= fetch()))
return res;
if (bound_tracker.compare_with_cache())
{
+ /* This row is part of a new partition, don't move
+ forward any more untill we get informed of a new partition. */
+ Table_read_cursor::prev();
end_of_partition= true;
return -1;
}
return 0;
}
- bool restore_last_row()
- {
- return tbl_cursor.restore_last_row();
- }
+private:
+ Group_bound_tracker bound_tracker;
+ bool end_of_partition;
};
/////////////////////////////////////////////////////////////////////////////
-
/*
Window frame bound cursor. Abstract interface.
-
+
@detail
The cursor moves within the partition that the current row is in.
It may be ahead or behind the current row.
@@ -775,11 +757,14 @@ public:
class Frame_cursor : public Sql_alloc
{
public:
- virtual void init(THD *thd, READ_RECORD *info,
- SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
- {}
+ Frame_cursor() : sum_functions(), perform_no_action(false) {}
+ virtual void init(READ_RECORD *info) {};
+
+ bool add_sum_func(Item_sum* item)
+ {
+ return sum_functions.push_back(item);
+ }
/*
Current row has moved to the next partition and is positioned on the first
row there. Position the frame bound accordingly.
@@ -796,20 +781,120 @@ public:
- The callee may move tbl->file and tbl->record[0] to point to some other
row.
*/
- virtual void pre_next_partition(ha_rows rownum, Item_sum* item){};
- virtual void next_partition(ha_rows rownum, Item_sum* item)=0;
-
+ virtual void pre_next_partition(ha_rows rownum) {};
+ virtual void next_partition(ha_rows rownum)=0;
+
/*
The current row has moved one row forward.
Move this frame bound accordingly, and update the value of aggregate
function as necessary.
*/
- virtual void pre_next_row(Item_sum* item){};
- virtual void next_row(Item_sum* item)=0;
-
- virtual ~Frame_cursor(){}
+ virtual void pre_next_row() {};
+ virtual void next_row()=0;
+
+ virtual bool is_outside_computation_bounds() const { return false; };
+
+ virtual ~Frame_cursor() {}
+
+ /*
+ Regular frame cursors add or remove values from the sum functions they
+ manage. By calling this method, they will only perform the required
+ movement within the table, but no adding/removing will happen.
+ */
+ void set_no_action()
+ {
+ perform_no_action= true;
+ }
+
+ /* Retrieves the row number that this cursor currently points at. */
+ virtual ha_rows get_curr_rownum() const= 0;
+
+protected:
+ inline void add_value_to_items()
+ {
+ if (perform_no_action)
+ return;
+
+ List_iterator_fast<Item_sum> it(sum_functions);
+ Item_sum *item_sum;
+ while ((item_sum= it++))
+ {
+ item_sum->add();
+ }
+ }
+
+ inline void remove_value_from_items()
+ {
+ if (perform_no_action)
+ return;
+
+ List_iterator_fast<Item_sum> it(sum_functions);
+ Item_sum *item_sum;
+ while ((item_sum= it++))
+ {
+ item_sum->remove();
+ }
+ }
+
+ /* Sum functions that this cursor handles. */
+ List<Item_sum> sum_functions;
+
+private:
+ bool perform_no_action;
+};
+
+/*
+ A class that owns cursor objects associated with a specific window function.
+*/
+class Cursor_manager
+{
+public:
+ bool add_cursor(Frame_cursor *cursor)
+ {
+ return cursors.push_back(cursor);
+ }
+
+ void initialize_cursors(READ_RECORD *info)
+ {
+ List_iterator_fast<Frame_cursor> iter(cursors);
+ Frame_cursor *fc;
+ while ((fc= iter++))
+ fc->init(info);
+ }
+
+ void notify_cursors_partition_changed(ha_rows rownum)
+ {
+ List_iterator_fast<Frame_cursor> iter(cursors);
+ Frame_cursor *cursor;
+ while ((cursor= iter++))
+ cursor->pre_next_partition(rownum);
+
+ iter.rewind();
+ while ((cursor= iter++))
+ cursor->next_partition(rownum);
+ }
+
+ void notify_cursors_next_row()
+ {
+ List_iterator_fast<Frame_cursor> iter(cursors);
+ Frame_cursor *cursor;
+ while ((cursor= iter++))
+ cursor->pre_next_row();
+
+ iter.rewind();
+ while ((cursor= iter++))
+ cursor->next_row();
+ }
+
+ ~Cursor_manager() { cursors.delete_elements(); }
+
+private:
+ /* List of the cursors that this manager owns. */
+ List<Frame_cursor> cursors;
};
+
+
//////////////////////////////////////////////////////////////////////////////
// RANGE-type frames
//////////////////////////////////////////////////////////////////////////////
@@ -827,7 +912,7 @@ public:
class Frame_range_n_top : public Frame_cursor
{
- Table_read_cursor cursor;
+ Partition_read_cursor cursor;
Cached_item_item *range_expr;
@@ -835,22 +920,22 @@ class Frame_range_n_top : public Frame_cursor
Item *item_add;
const bool is_preceding;
+
+ bool end_of_partition;
+
/*
1 when order_list uses ASC ordering
-1 when order_list uses DESC ordering
*/
int order_direction;
public:
- Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) :
- n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
- {}
-
- void init(THD *thd, READ_RECORD *info,
- SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ Frame_range_n_top(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list,
+ bool is_preceding_arg, Item *n_val_arg) :
+ cursor(thd, partition_list), n_val(n_val_arg), item_add(NULL),
+ is_preceding(is_preceding_arg)
{
- cursor.init(info);
-
DBUG_ASSERT(order_list->elements == 1);
Item *src_expr= order_list->first->item[0];
if (order_list->first->direction == ORDER::ORDER_ASC)
@@ -872,49 +957,82 @@ public:
item_add->fix_fields(thd, &item_add);
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void init(READ_RECORD *info)
+ {
+ cursor.init(info);
+ }
+
+ void pre_next_partition(ha_rows rownum)
{
// Save the value of FUNC(current_row)
range_expr->fetch_value_from(item_add);
+
+ cursor.on_next_partition(rownum);
+ end_of_partition= false;
}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
- cursor.move_to(rownum);
- walk_till_non_peer(item);
+ walk_till_non_peer();
}
- void pre_next_row(Item_sum* item)
+ void pre_next_row()
{
+ if (end_of_partition)
+ return;
range_expr->fetch_value_from(item_add);
}
- void next_row(Item_sum* item)
+ void next_row()
{
+ if (end_of_partition)
+ return;
/*
Ok, our cursor is at the first row R where
(prev_row + n) >= R
We need to check about the current row.
*/
- if (cursor.restore_last_row())
- {
- if (order_direction * range_expr->cmp_read_only() <= 0)
- return;
- item->remove();
- }
- walk_till_non_peer(item);
+ walk_till_non_peer();
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
+
+ bool is_outside_computation_bounds() const
+ {
+ if (end_of_partition)
+ return true;
+ return false;
}
private:
- void walk_till_non_peer(Item_sum* item)
+ void walk_till_non_peer()
{
- while (!cursor.get_next())
+ if (cursor.fetch()) // ERROR
+ return;
+ // Current row is not a peer.
+ if (order_direction * range_expr->cmp_read_only() <= 0)
+ return;
+ remove_value_from_items();
+
+ int res;
+ while (!(res= cursor.next()))
{
+ /* Note, no need to fetch the value explicitly here. The partition
+ read cursor will fetch it to check if the partition has changed.
+ TODO(cvicentiu) make this piece of information not necessary by
+ reimplementing Partition_read_cursor.
+ */
if (order_direction * range_expr->cmp_read_only() <= 0)
break;
- item->remove();
+ remove_value_from_items();
}
+ if (res)
+ end_of_partition= true;
}
+
};
@@ -950,16 +1068,13 @@ class Frame_range_n_bottom: public Frame_cursor
*/
int order_direction;
public:
- Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) :
- n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
- {}
-
- void init(THD *thd, READ_RECORD *info,
- SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ Frame_range_n_bottom(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list,
+ bool is_preceding_arg, Item *n_val_arg) :
+ cursor(thd, partition_list), n_val(n_val_arg), item_add(NULL),
+ is_preceding(is_preceding_arg), added_values(false)
{
- cursor.init(thd, info, partition_list);
-
DBUG_ASSERT(order_list->elements == 1);
Item *src_expr= order_list->first->item[0];
@@ -982,29 +1097,35 @@ public:
item_add->fix_fields(thd, &item_add);
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void init(READ_RECORD *info)
+ {
+ cursor.init(info);
+ }
+
+ void pre_next_partition(ha_rows rownum)
{
// Save the value of FUNC(current_row)
range_expr->fetch_value_from(item_add);
cursor.on_next_partition(rownum);
end_of_partition= false;
+ added_values= false;
}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
cursor.move_to(rownum);
- walk_till_non_peer(item);
+ walk_till_non_peer();
}
- void pre_next_row(Item_sum* item)
+ void pre_next_row()
{
if (end_of_partition)
return;
range_expr->fetch_value_from(item_add);
}
- void next_row(Item_sum* item)
+ void next_row()
{
if (end_of_partition)
return;
@@ -1013,24 +1134,42 @@ public:
(prev_row + n) >= R
We need to check about the current row.
*/
- if (cursor.restore_last_row())
- {
- if (order_direction * range_expr->cmp_read_only() < 0)
- return;
- item->add();
- }
- walk_till_non_peer(item);
+ walk_till_non_peer();
+ }
+
+ bool is_outside_computation_bounds() const
+ {
+ if (!added_values)
+ return true;
+ return false;
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ if (end_of_partition)
+ return cursor.get_rownum(); // Cursor does not pass over partition bound.
+ else
+ return cursor.get_rownum() - 1; // Cursor is placed on first non peer.
}
private:
- void walk_till_non_peer(Item_sum* item)
+ bool added_values;
+
+ void walk_till_non_peer()
{
+ cursor.fetch();
+ // Current row is not a peer.
+ if (order_direction * range_expr->cmp_read_only() < 0)
+ return;
+
+ add_value_to_items(); // Add current row.
+ added_values= true;
int res;
- while (!(res= cursor.get_next()))
+ while (!(res= cursor.next()))
{
if (order_direction * range_expr->cmp_read_only() < 0)
break;
- item->add();
+ add_value_to_items();
}
if (res)
end_of_partition= true;
@@ -1043,11 +1182,11 @@ private:
...
| peer1
| peer2 <----- current_row
- | peer3
+ | peer3
+-peer4 <----- the cursor points here. peer4 itself is included.
nonpeer1
nonpeer2
-
+
This bound moves in front of the current_row. It should be a the first row
that is still a peer of the current row.
*/
@@ -1060,39 +1199,39 @@ class Frame_range_current_row_bottom: public Frame_cursor
bool dont_move;
public:
- void init(THD *thd, READ_RECORD *info,
- SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ Frame_range_current_row_bottom(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list) :
+ cursor(thd, partition_list), peer_tracker(thd, order_list)
{
- cursor.init(thd, info, partition_list);
- peer_tracker.init(thd, order_list);
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void init(READ_RECORD *info)
+ {
+ cursor.init(info);
+ peer_tracker.init();
+ }
+
+ void pre_next_partition(ha_rows rownum)
{
// Save the value of the current_row
peer_tracker.check_if_next_group();
cursor.on_next_partition(rownum);
- if (rownum != 0)
- {
- // Add the current row now because our cursor has already seen it
- item->add();
- }
+ // Add the current row now because our cursor has already seen it
+ add_value_to_items();
}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
- walk_till_non_peer(item);
+ walk_till_non_peer();
}
- void pre_next_row(Item_sum* item)
+ void pre_next_row()
{
dont_move= !peer_tracker.check_if_next_group();
- if (!dont_move)
- item->add();
}
- void next_row(Item_sum* item)
+ void next_row()
{
// Check if our cursor is pointing at a peer of the current row.
// If not, move forward until that becomes true
@@ -1104,21 +1243,30 @@ public:
*/
return;
}
- walk_till_non_peer(item);
+ walk_till_non_peer();
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
}
private:
- void walk_till_non_peer(Item_sum* item)
+ void walk_till_non_peer()
{
/*
Walk forward until we've met first row that's not a peer of the current
row
*/
- while (!cursor.get_next())
+ while (!cursor.next())
{
if (peer_tracker.compare_with_cache())
+ {
+ cursor.prev(); // Move to our peer.
break;
- item->add();
+ }
+
+ add_value_to_items();
}
}
};
@@ -1148,33 +1296,38 @@ class Frame_range_current_row_top : public Frame_cursor
bool move;
public:
- void init(THD *thd, READ_RECORD *info,
- SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ Frame_range_current_row_top(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list) :
+ bound_tracker(thd, partition_list), cursor(), peer_tracker(thd, order_list),
+ move(false)
+ {}
+
+ void init(READ_RECORD *info)
{
- bound_tracker.init(thd, partition_list);
+ bound_tracker.init();
cursor.init(info);
- peer_tracker.init(thd, order_list);
+ peer_tracker.init();
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void pre_next_partition(ha_rows rownum)
{
// Fetch the value from the first row
peer_tracker.check_if_next_group();
- cursor.move_to(rownum+1);
+ cursor.move_to(rownum);
}
- void next_partition(ha_rows rownum, Item_sum* item) {}
+ void next_partition(ha_rows rownum) {}
- void pre_next_row(Item_sum* item)
+ void pre_next_row()
{
// Check if the new current_row is a peer of the row that our cursor is
// pointing to.
move= peer_tracker.check_if_next_group();
}
- void next_row(Item_sum* item)
+ void next_row()
{
if (move)
{
@@ -1182,25 +1335,30 @@ public:
Our cursor is pointing at the first row that was a peer of the previous
current row. Or, it was the first row in the partition.
*/
- if (cursor.restore_last_row())
- {
- // todo: need the following check ?
- if (!peer_tracker.compare_with_cache())
- return;
- item->remove();
- }
+ if (cursor.fetch())
+ return;
+
+ // todo: need the following check ?
+ if (!peer_tracker.compare_with_cache())
+ return;
+ remove_value_from_items();
do
{
- if (cursor.get_next())
+ if (cursor.next() || cursor.fetch())
return;
if (!peer_tracker.compare_with_cache())
return;
- item->remove();
+ remove_value_from_items();
}
while (1);
}
}
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
};
@@ -1214,18 +1372,35 @@ public:
class Frame_unbounded_preceding : public Frame_cursor
{
public:
- void next_partition(ha_rows rownum, Item_sum* item)
+ Frame_unbounded_preceding(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list)
+ {}
+
+ void init(READ_RECORD *info) {}
+
+ void next_partition(ha_rows rownum)
{
/*
- UNBOUNDED PRECEDING frame end just stays on the first row.
- We are top of the frame, so we don't need to update the sum function.
+ UNBOUNDED PRECEDING frame end just stays on the first row of the
+ partition. We are top of the frame, so we don't need to update the sum
+ function.
*/
+ curr_rownum= rownum;
}
- void next_row(Item_sum* item)
+ void next_row()
{
/* Do nothing, UNBOUNDED PRECEDING frame end doesn't move. */
}
+
+ ha_rows get_curr_rownum() const
+ {
+ return curr_rownum;
+ }
+
+private:
+ ha_rows curr_rownum;
};
@@ -1239,66 +1414,78 @@ protected:
Partition_read_cursor cursor;
public:
- void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ Frame_unbounded_following(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list) :
+ cursor(thd, partition_list) {}
+
+ void init(READ_RECORD *info)
{
- cursor.init(thd, info, partition_list);
+ cursor.init(info);
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void pre_next_partition(ha_rows rownum)
{
cursor.on_next_partition(rownum);
}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
- if (!rownum)
- {
- /* Read the first row */
- if (cursor.get_next())
- return;
- }
- item->add();
+ /* Activate the first row */
+ cursor.fetch();
+ add_value_to_items();
/* Walk to the end of the partition, updating the SUM function */
- while (!cursor.get_next())
+ while (!cursor.next())
{
- item->add();
+ add_value_to_items();
}
}
- void next_row(Item_sum* item)
+ void next_row()
{
/* Do nothing, UNBOUNDED FOLLOWING frame end doesn't move */
}
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
};
class Frame_unbounded_following_set_count : public Frame_unbounded_following
{
public:
- // pre_next_partition is inherited
+ Frame_unbounded_following_set_count(
+ THD *thd,
+ SQL_I_List<ORDER> *partition_list, SQL_I_List<ORDER> *order_list) :
+ Frame_unbounded_following(thd, partition_list, order_list) {}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
ha_rows num_rows_in_partition= 0;
- if (!rownum)
- {
- /* Read the first row */
- if (cursor.get_next())
- return;
- }
+ if (cursor.fetch())
+ return;
num_rows_in_partition++;
/* Walk to the end of the partition, find how many rows there are. */
- while (!cursor.get_next())
- {
+ while (!cursor.next())
num_rows_in_partition++;
+
+ List_iterator_fast<Item_sum> it(sum_functions);
+ Item_sum* item;
+ while ((item= it++))
+ {
+ Item_sum_window_with_row_count* item_with_row_count =
+ static_cast<Item_sum_window_with_row_count *>(item);
+ item_with_row_count->set_row_count(num_rows_in_partition);
}
+ }
- Item_sum_window_with_row_count* item_with_row_count =
- static_cast<Item_sum_window_with_row_count *>(item);
- item_with_row_count->set_row_count(num_rows_in_partition);
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
}
};
@@ -1316,28 +1503,28 @@ class Frame_n_rows_preceding : public Frame_cursor
const ha_rows n_rows;
/* Number of rows that we need to skip before our cursor starts moving */
- ha_rows n_rows_to_skip;
+ ha_rows n_rows_behind;
Table_read_cursor cursor;
public:
Frame_n_rows_preceding(bool is_top_bound_arg, ha_rows n_rows_arg) :
- is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)
+ is_top_bound(is_top_bound_arg), n_rows(n_rows_arg), n_rows_behind(0)
{}
- void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ void init(READ_RECORD *info)
{
cursor.init(info);
}
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_partition(ha_rows rownum)
{
/*
Position our cursor to point at the first row in the new partition
(for rownum=0, it is already there, otherwise, it lags behind)
*/
- if (rownum != 0)
- cursor.move_to(rownum);
+ cursor.move_to(rownum);
+ /* Cursor is in the same spot as current row. */
+ n_rows_behind= 0;
/*
Suppose the bound is ROWS 2 PRECEDING, and current row is row#n:
@@ -1351,32 +1538,65 @@ public:
- bottom bound should add row #(n-2) into the window function
- top bound should remove row (#n-3) from the window function.
*/
- n_rows_to_skip= n_rows + (is_top_bound? 1:0) - 1;
+ move_cursor_if_possible();
- /* Bottom bound "ROWS 0 PRECEDING" is a special case: */
- if (n_rows_to_skip == ha_rows(-1))
- {
- cursor.get_next();
- item->add();
- n_rows_to_skip= 0;
- }
}
- void next_row(Item_sum* item)
+ void next_row()
+ {
+ n_rows_behind++;
+ move_cursor_if_possible();
+ }
+
+ bool is_outside_computation_bounds() const
{
- if (n_rows_to_skip)
+ /* As a bottom boundary, rows have not yet been added. */
+ if (!is_top_bound && n_rows - n_rows_behind)
+ return true;
+ return false;
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
+
+private:
+ void move_cursor_if_possible()
+ {
+ int rows_difference= n_rows - n_rows_behind;
+ if (rows_difference > 0) /* We still have to wait. */
+ return;
+
+ /* The cursor points to the first row in the frame. */
+ if (rows_difference == 0)
{
- n_rows_to_skip--;
+ if (!is_top_bound)
+ {
+ cursor.fetch();
+ add_value_to_items();
+ }
+ /* For top bound we don't have to remove anything as nothing was added. */
return;
}
- if (cursor.get_next())
- return; // this is not expected to happen.
+ /* We need to catch up by one row. */
+ DBUG_ASSERT(rows_difference == -1);
- if (is_top_bound) // this is frame start endpoint
- item->remove();
+ if (is_top_bound)
+ {
+ cursor.fetch();
+ remove_value_from_items();
+ cursor.next();
+ }
else
- item->add();
+ {
+ cursor.next();
+ cursor.fetch();
+ add_value_to_items();
+ }
+ /* We've advanced one row. We are no longer behind. */
+ n_rows_behind--;
}
};
@@ -1391,17 +1611,35 @@ public:
class Frame_rows_current_row_bottom : public Frame_cursor
{
public:
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+
+ Frame_rows_current_row_bottom() : curr_rownum(0) {}
+
+ void pre_next_partition(ha_rows rownum)
{
- item->add();
+ add_value_to_items();
+ curr_rownum= rownum;
}
- void next_partition(ha_rows rownum, Item_sum* item) {}
- void pre_next_row(Item_sum* item)
+
+ void next_partition(ha_rows rownum) {}
+
+ void pre_next_row()
{
/* Temp table's current row is current_row. Add it to the window func */
- item->add();
+ add_value_to_items();
}
- void next_row(Item_sum* item) {};
+
+ void next_row()
+ {
+ curr_rownum++;
+ };
+
+ ha_rows get_curr_rownum() const
+ {
+ return curr_rownum;
+ }
+
+private:
+ ha_rows curr_rownum;
};
@@ -1443,69 +1681,218 @@ class Frame_n_rows_following : public Frame_cursor
Partition_read_cursor cursor;
bool at_partition_end;
public:
- Frame_n_rows_following(bool is_top_bound_arg, ha_rows n_rows_arg) :
- is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)
+ Frame_n_rows_following(THD *thd,
+ SQL_I_List<ORDER> *partition_list,
+ SQL_I_List<ORDER> *order_list,
+ bool is_top_bound_arg, ha_rows n_rows_arg) :
+ is_top_bound(is_top_bound_arg), n_rows(n_rows_arg),
+ cursor(thd, partition_list)
{
DBUG_ASSERT(n_rows > 0);
}
- void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
- SQL_I_List<ORDER> *order_list)
+ void init(READ_RECORD *info)
{
- cursor.init(thd, info, partition_list);
+ cursor.init(info);
at_partition_end= false;
}
- void pre_next_partition(ha_rows rownum, Item_sum* item)
+ void pre_next_partition(ha_rows rownum)
{
at_partition_end= false;
cursor.on_next_partition(rownum);
+ }
- if (rownum != 0)
- {
- // This is only needed for "FOLLOWING 1". It is one row behind
- cursor.move_to(rownum+1);
+ /* Move our cursor to be n_rows ahead. */
+ void next_partition(ha_rows rownum)
+ {
+ if (is_top_bound)
+ next_part_top(rownum);
+ else
+ next_part_bottom(rownum);
+ }
- // Current row points at the first row in the partition
- if (is_top_bound) // this is frame top endpoint
- item->remove();
- else
- item->add();
+ void next_row()
+ {
+ if (is_top_bound)
+ next_row_top();
+ else
+ next_row_bottom();
+ }
+
+ bool is_outside_computation_bounds() const
+ {
+ /*
+ The top bound can go over the current partition. In this case,
+ the sum function has 0 values added to it.
+ */
+ if (at_partition_end && is_top_bound)
+ return true;
+ return false;
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ return cursor.get_rownum();
+ }
+
+private:
+ void next_part_top(ha_rows rownum)
+ {
+ for (ha_rows i= 0; i < n_rows; i++)
+ {
+ if (cursor.fetch())
+ break;
+ remove_value_from_items();
+ if (cursor.next())
+ at_partition_end= true;
}
}
- /* Move our cursor to be n_rows ahead. */
- void next_partition(ha_rows rownum, Item_sum* item)
+ void next_part_bottom(ha_rows rownum)
{
- ha_rows i_end= n_rows + ((rownum==0)?1:0)- is_top_bound;
- for (ha_rows i= 0; i < i_end; i++)
+ if (cursor.fetch())
+ return;
+ add_value_to_items();
+
+ for (ha_rows i= 0; i < n_rows; i++)
{
- if (next_row_intern(item))
+ if (cursor.next())
+ {
+ at_partition_end= true;
break;
+ }
+ add_value_to_items();
+ }
+ return;
+ }
+
+ void next_row_top()
+ {
+ if (cursor.fetch()) // PART END OR FAILURE
+ {
+ at_partition_end= true;
+ return;
+ }
+ remove_value_from_items();
+ if (cursor.next())
+ {
+ at_partition_end= true;
+ return;
}
}
- void next_row(Item_sum* item)
+ void next_row_bottom()
{
if (at_partition_end)
return;
- next_row_intern(item);
+
+ if (cursor.next())
+ {
+ at_partition_end= true;
+ return;
+ }
+
+ add_value_to_items();
+
+ }
+};
+
+/*
+ A cursor that performs a table scan between two indices. The indices
+ are provided by the two cursors representing the top and bottom bound
+ of the window function's frame definition.
+
+ Each scan clears the sum function.
+
+ NOTE:
+ The cursor does not alter the top and bottom cursors.
+ This type of cursor is expensive computational wise. This is only to be
+ used when the sum functions do not support removal.
+*/
+class Frame_scan_cursor : public Frame_cursor
+{
+public:
+ Frame_scan_cursor(const Frame_cursor &top_bound,
+ const Frame_cursor &bottom_bound) :
+ top_bound(top_bound), bottom_bound(bottom_bound) {}
+
+ void init(READ_RECORD *info)
+ {
+ cursor.init(info);
+ }
+
+ void pre_next_partition(ha_rows rownum)
+ {
+ /* TODO(cvicentiu) Sum functions get cleared on next partition anyway during
+ the window function computation algorithm. Either perform this only in
+ cursors, or remove it from pre_next_partition.
+ */
+ curr_rownum= rownum;
+ clear_sum_functions();
+ }
+
+ void next_partition(ha_rows rownum)
+ {
+ compute_values_for_current_row();
+ }
+
+ void pre_next_row()
+ {
+ clear_sum_functions();
+ }
+
+ void next_row()
+ {
+ curr_rownum++;
+ compute_values_for_current_row();
+ }
+
+ ha_rows get_curr_rownum() const
+ {
+ return curr_rownum;
}
private:
- bool next_row_intern(Item_sum *item)
+ const Frame_cursor &top_bound;
+ const Frame_cursor &bottom_bound;
+ Table_read_cursor cursor;
+ ha_rows curr_rownum;
+
+ /* Clear all sum functions handled by this cursor. */
+ void clear_sum_functions()
{
- if (!cursor.get_next())
+ List_iterator_fast<Item_sum> iter_sum_func(sum_functions);
+ Item_sum *sum_func;
+ while ((sum_func= iter_sum_func++))
{
- if (is_top_bound) // this is frame start endpoint
- item->remove();
- else
- item->add();
+ sum_func->clear();
+ }
+ }
+
+ /* Scan the rows between the top bound and bottom bound. Add all the values
+ between them, top bound row and bottom bound row inclusive. */
+ void compute_values_for_current_row()
+ {
+ if (top_bound.is_outside_computation_bounds() ||
+ bottom_bound.is_outside_computation_bounds())
+ return;
+
+ ha_rows start_rownum= top_bound.get_curr_rownum();
+ ha_rows bottom_rownum= bottom_bound.get_curr_rownum();
+ DBUG_PRINT("info", ("COMPUTING (%llu %llu)", start_rownum, bottom_rownum));
+
+ cursor.move_to(start_rownum);
+
+ for (ha_rows idx= start_rownum; idx <= bottom_rownum; idx++)
+ {
+ if (cursor.fetch()) //EOF
+ break;
+ add_value_to_items();
+ if (cursor.next()) // EOF
+ break;
}
- else
- at_partition_end= true;
- return at_partition_end;
}
};
@@ -1513,8 +1900,9 @@ private:
/*
Get a Frame_cursor for a frame bound. This is a "factory function".
*/
-Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
+Frame_cursor *get_frame_cursor(THD *thd, Window_spec *spec, bool is_top_bound)
{
+ Window_frame *frame= spec->window_frame;
if (!frame)
{
/*
@@ -1536,9 +1924,13 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
so again the same frame bounds can be used.
*/
if (is_top_bound)
- return new Frame_unbounded_preceding;
+ return new Frame_unbounded_preceding(thd,
+ spec->partition_list,
+ spec->order_list);
else
- return new Frame_range_current_row_bottom;
+ return new Frame_range_current_row_bottom(thd,
+ spec->partition_list,
+ spec->order_list);
}
Window_frame_bound *bound= is_top_bound? frame->top_bound :
@@ -1554,9 +1946,13 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
{
/* The following serve both RANGE and ROWS: */
if (is_preceding)
- return new Frame_unbounded_preceding;
- else
- return new Frame_unbounded_following;
+ return new Frame_unbounded_preceding(thd,
+ spec->partition_list,
+ spec->order_list);
+
+ return new Frame_unbounded_following(thd,
+ spec->partition_list,
+ spec->order_list);
}
if (frame->units == Window_frame::UNITS_ROWS)
@@ -1567,15 +1963,21 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
DBUG_ASSERT((longlong) n_rows >= 0);
if (is_preceding)
return new Frame_n_rows_preceding(is_top_bound, n_rows);
- else
- return new Frame_n_rows_following(is_top_bound, n_rows);
+
+ return new Frame_n_rows_following(
+ thd, spec->partition_list, spec->order_list,
+ is_top_bound, n_rows);
}
else
{
if (is_top_bound)
- return new Frame_range_n_top(is_preceding, bound->offset);
- else
- return new Frame_range_n_bottom(is_preceding, bound->offset);
+ return new Frame_range_n_top(
+ thd, spec->partition_list, spec->order_list,
+ is_preceding, bound->offset);
+
+ return new Frame_range_n_bottom(thd,
+ spec->partition_list, spec->order_list,
+ is_preceding, bound->offset);
}
}
@@ -1585,67 +1987,179 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
{
if (is_top_bound)
return new Frame_rows_current_row_top;
- else
- return new Frame_rows_current_row_bottom;
+
+ return new Frame_rows_current_row_bottom;
}
else
{
if (is_top_bound)
- return new Frame_range_current_row_top;
- else
- return new Frame_range_current_row_bottom;
+ return new Frame_range_current_row_top(
+ thd, spec->partition_list, spec->order_list);
+
+ return new Frame_range_current_row_bottom(
+ thd, spec->partition_list, spec->order_list);
}
}
return NULL;
}
-void add_extra_frame_cursors(List<Frame_cursor> *cursors,
- const Item_sum *window_func)
+void add_extra_frame_cursors(THD *thd, Cursor_manager *cursor_manager,
+ Item_window_func *window_func)
{
- switch (window_func->sum_func())
+ Window_spec *spec= window_func->window_spec;
+ Item_sum *item_sum= window_func->window_func();
+ Frame_cursor *fc;
+ switch (item_sum->sum_func())
{
case Item_sum::CUME_DIST_FUNC:
- cursors->push_back(new Frame_unbounded_preceding);
- cursors->push_back(new Frame_range_current_row_bottom);
+ fc= new Frame_unbounded_preceding(thd,
+ spec->partition_list,
+ spec->order_list);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+ fc= new Frame_range_current_row_bottom(thd,
+ spec->partition_list,
+ spec->order_list);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
break;
default:
- cursors->push_back(new Frame_unbounded_preceding);
- cursors->push_back(new Frame_rows_current_row_bottom);
+ fc= new Frame_unbounded_preceding(
+ thd, spec->partition_list, spec->order_list);
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
+
+ fc= new Frame_rows_current_row_bottom;
+ fc->add_sum_func(item_sum);
+ cursor_manager->add_cursor(fc);
}
}
-void get_window_func_required_cursors(
- List<Frame_cursor> *result, const Item_window_func* item_win)
+
+static bool is_computed_with_remove(Item_sum::Sumfunctype sum_func)
+{
+ switch (sum_func)
+ {
+ case Item_sum::CUME_DIST_FUNC:
+ case Item_sum::ROW_NUMBER_FUNC:
+ case Item_sum::RANK_FUNC:
+ case Item_sum::DENSE_RANK_FUNC:
+ case Item_sum::NTILE_FUNC:
+ return false;
+ default:
+ return true;
+ }
+}
+/*
+ Create required frame cursors for the list of window functions.
+ Register all functions to their appropriate cursors.
+ If the window functions share the same frame specification,
+ those window functions will be registered to the same cursor.
+*/
+void get_window_functions_required_cursors(
+ THD *thd,
+ List<Item_window_func>& window_functions,
+ List<Cursor_manager> *cursor_managers)
{
- if (item_win->requires_partition_size())
- result->push_back(new Frame_unbounded_following_set_count);
+ List_iterator_fast<Item_window_func> it(window_functions);
+ Item_window_func* item_win_func;
+ Item_sum *sum_func;
+ while ((item_win_func= it++))
+ {
+ Cursor_manager *cursor_manager = new Cursor_manager();
+ sum_func = item_win_func->window_func();
+ Frame_cursor *fc;
+ /*
+ Some window functions require the partition size for computing values.
+ Add a cursor that retrieves it as the first one in the list if necessary.
+ */
+ if (item_win_func->requires_partition_size())
+ {
+ fc= new Frame_unbounded_following_set_count(thd,
+ item_win_func->window_spec->partition_list,
+ item_win_func->window_spec->order_list);
+ fc->add_sum_func(sum_func);
+ cursor_manager->add_cursor(fc);
+ }
- /*
- If it is not a regular window function that follows frame specifications,
- specific cursors are required.
- */
- if (item_win->is_frame_prohibited())
+ /*
+ If it is not a regular window function that follows frame specifications,
+ specific cursors are required. ROW_NUM, RANK, NTILE and others follow
+ such rules. Check is_frame_prohibited check for the full list.
+ */
+ if (item_win_func->is_frame_prohibited())
+ {
+ add_extra_frame_cursors(thd, cursor_manager, item_win_func);
+ cursor_managers->push_back(cursor_manager);
+ continue;
+ }
+
+ Frame_cursor *frame_bottom= get_frame_cursor(thd,
+ item_win_func->window_spec, false);
+ Frame_cursor *frame_top= get_frame_cursor(thd,
+ item_win_func->window_spec, true);
+
+ frame_bottom->add_sum_func(sum_func);
+ frame_top->add_sum_func(sum_func);
+
+ /*
+ The order of these cursors is important. A sum function
+ must first add values (via frame_bottom) then remove them via
+ frame_top. Removing items first doesn't make sense in the case of all
+ window functions.
+ */
+ cursor_manager->add_cursor(frame_bottom);
+ cursor_manager->add_cursor(frame_top);
+ if (is_computed_with_remove(sum_func->sum_func()) &&
+ !sum_func->supports_removal())
+ {
+ frame_bottom->set_no_action();
+ frame_top->set_no_action();
+ Frame_cursor *scan_cursor= new Frame_scan_cursor(*frame_top,
+ *frame_bottom);
+ scan_cursor->add_sum_func(sum_func);
+ cursor_manager->add_cursor(scan_cursor);
+
+ }
+ cursor_managers->push_back(cursor_manager);
+ }
+}
+
+/**
+ Helper function that takes a list of window functions and writes
+ their values in the current table record.
+*/
+static
+bool save_window_function_values(List<Item_window_func>& window_functions,
+ TABLE *tbl, uchar *rowid_buf)
+{
+ List_iterator_fast<Item_window_func> iter(window_functions);
+ tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);
+ store_record(tbl, record[1]);
+ while (Item_window_func *item_win= iter++)
{
- add_extra_frame_cursors(result, item_win->window_func());
- return;
+ int err;
+ item_win->save_in_field(item_win->result_field, true);
+ // TODO check if this can be placed outside the loop.
+ err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);
+ if (err && err != HA_ERR_RECORD_IS_THE_SAME)
+ return true;
}
- /* A regular window function follows the frame specification. */
- result->push_back(get_frame_cursor(item_win->window_spec->window_frame,
- false));
- result->push_back(get_frame_cursor(item_win->window_spec->window_frame,
- true));
+ return false;
}
/*
+ TODO(cvicentiu) update this comment to reflect the new execution.
+
Streamed window function computation with window frames.
We make a single pass over the ordered temp.table, but we're using three
- cursors:
+ cursors:
- current row - the row that we're computing window func value for)
- start_bound - the start of the frame
- bottom_bound - the end of the frame
-
+
All three cursors move together.
@todo
@@ -1655,7 +2169,7 @@ void get_window_func_required_cursors(
@detail
ROWS BETWEEN 3 PRECEDING -- frame start
AND 3 FOLLOWING -- frame end
-
+
/------ frame end (aka BOTTOM)
Dataset start |
--------====*=======[*]========*========-------->> dataset end
@@ -1663,7 +2177,7 @@ void get_window_func_required_cursors(
| +-------- current row
|
\-------- frame start ("TOP")
-
+
- frame_end moves forward and adds rows into the aggregate function.
- frame_start follows behind and removes rows from the aggregate function.
- current_row is the row where the value of aggregate function is stored.
@@ -1672,97 +2186,90 @@ void get_window_func_required_cursors(
condition (Others can catch up by counting rows?)
*/
-
-bool compute_window_func_with_frames(Item_window_func *item_win,
- TABLE *tbl, READ_RECORD *info)
+bool compute_window_func(THD *thd,
+ List<Item_window_func>& window_functions,
+ List<Cursor_manager>& cursor_managers,
+ TABLE *tbl,
+ SORT_INFO *filesort_result)
{
- THD *thd= tbl->in_use;
- int err= 0;
+ List_iterator_fast<Item_window_func> iter_win_funcs(window_functions);
+ List_iterator_fast<Cursor_manager> iter_cursor_managers(cursor_managers);
+ uint err;
- Item_sum *sum_func= item_win->window_func();
- /* This algorithm doesn't support DISTINCT aggregator */
- sum_func->set_aggregator(Aggregator::SIMPLE_AGGREGATOR);
+ READ_RECORD info;
- List<Frame_cursor> cursors;
- get_window_func_required_cursors(&cursors, item_win);
+ if (init_read_record(&info, current_thd, tbl, NULL/*select*/, filesort_result,
+ 0, 1, FALSE))
+ return true;
- List_iterator_fast<Frame_cursor> it(cursors);
- Frame_cursor *c;
- while((c= it++))
+ Cursor_manager *cursor_manager;
+ while ((cursor_manager= iter_cursor_managers++))
+ cursor_manager->initialize_cursors(&info);
+
+ /* One partition tracker for each window function. */
+ List<Group_bound_tracker> partition_trackers;
+ Item_window_func *win_func;
+ while ((win_func= iter_win_funcs++))
{
- c->init(thd, info, item_win->window_spec->partition_list,
- item_win->window_spec->order_list);
+ Group_bound_tracker *tracker= new Group_bound_tracker(thd,
+ win_func->window_spec->partition_list);
+ // TODO(cvicentiu) This should be removed and placed in constructor.
+ tracker->init();
+ partition_trackers.push_back(tracker);
}
- bool is_error= false;
+ List_iterator_fast<Group_bound_tracker> iter_part_trackers(partition_trackers);
ha_rows rownum= 0;
uchar *rowid_buf= (uchar*) my_malloc(tbl->file->ref_length, MYF(0));
while (true)
{
- /* Move the current_row */
- if ((err=info->read_record(info)))
- {
- break; /* End of file */
- }
- bool partition_changed= item_win->check_if_partition_changed();
+ if ((err= info.read_record(&info)))
+ break; // End of file.
+ /* Remember current row so that we can restore it before computing
+ each window function. */
tbl->file->position(tbl->record[0]);
memcpy(rowid_buf, tbl->file->ref, tbl->file->ref_length);
- if (partition_changed || (rownum == 0))
- {
- sum_func->clear();
- /*
- pre_XXX functions assume that tbl->record[0] contains current_row, and
- they may not change it.
- */
- it.rewind();
- while ((c= it++))
- c->pre_next_partition(rownum, sum_func);
- /*
- We move bottom_bound first, because we want rows to be added into the
- aggregate before top_bound attempts to remove them.
- */
- it.rewind();
- while ((c= it++))
- c->next_partition(rownum, sum_func);
- }
- else
- {
- /* Again, both pre_XXX function can find current_row in tbl->record[0] */
- it.rewind();
- while ((c= it++))
- c->pre_next_row(sum_func);
-
- /* These make no assumptions about tbl->record[0] and may change it */
- it.rewind();
- while ((c= it++))
- c->next_row(sum_func);
- }
- rownum++;
+ iter_win_funcs.rewind();
+ iter_part_trackers.rewind();
+ iter_cursor_managers.rewind();
- /*
- Frame cursors may have made tbl->record[0] to point to some record other
- than current_row. This applies to tbl->file's internal state, too.
- Fix this by reading the current row again.
- */
- tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);
- store_record(tbl,record[1]);
- item_win->save_in_field(item_win->result_field, true);
- err= tbl->file->ha_update_row(tbl->record[1], tbl->record[0]);
- if (err && err != HA_ERR_RECORD_IS_THE_SAME)
+ Group_bound_tracker *tracker;
+ while ((win_func= iter_win_funcs++) &&
+ (tracker= iter_part_trackers++) &&
+ (cursor_manager= iter_cursor_managers++))
{
- is_error= true;
- break;
+ if (tracker->check_if_next_group() || (rownum == 0))
+ {
+ /* TODO(cvicentiu)
+ Clearing window functions should happen through cursors. */
+ win_func->window_func()->clear();
+ cursor_manager->notify_cursors_partition_changed(rownum);
+ }
+ else
+ {
+ cursor_manager->notify_cursors_next_row();
+ }
+ /* Return to current row after notifying cursors for each window
+ function. */
+ tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf);
}
+
+ /* We now have computed values for each window function. They can now
+ be saved in the current row. */
+ save_window_function_values(window_functions, tbl, rowid_buf);
+
+ rownum++;
}
my_free(rowid_buf);
- cursors.delete_elements();
- return is_error? true: false;
-}
+ partition_trackers.delete_elements();
+ end_read_record(&info);
+ return false;
+}
/* Make a list that is a concation of two lists of ORDER elements */
@@ -1799,69 +2306,62 @@ static ORDER* concat_order_lists(MEM_ROOT *mem_root, ORDER *list1, ORDER *list2)
return res;
}
-
-bool Window_func_runner::setup(THD *thd)
+bool Window_func_runner::add_function_to_run(Item_window_func *win_func)
{
- win_func->setup_partition_border_check(thd);
+
+ Item_sum *sum_func= win_func->window_func();
+ sum_func->setup_window_func(current_thd, win_func->window_spec);
Item_sum::Sumfunctype type= win_func->window_func()->sum_func();
- switch (type)
+
+ switch (type)
{
- case Item_sum::ROW_NUMBER_FUNC:
- case Item_sum::RANK_FUNC:
- case Item_sum::DENSE_RANK_FUNC:
- {
- /*
- One-pass window function computation, walk through the rows and
- assign values.
- */
- compute_func= compute_window_func_values;
- break;
- }
- case Item_sum::COUNT_FUNC:
- case Item_sum::SUM_BIT_FUNC:
- case Item_sum::SUM_FUNC:
- case Item_sum::AVG_FUNC:
- case Item_sum::PERCENT_RANK_FUNC:
- case Item_sum::CUME_DIST_FUNC:
- case Item_sum::NTILE_FUNC:
- {
- /*
- Frame-aware window function computation. It does one pass, but
- uses three cursors -frame_start, current_row, and frame_end.
- */
- compute_func= compute_window_func_with_frames;
- break;
- }
- default:
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "This aggregate as window function");
+ /* Distinct is not yet supported. */
+ case Item_sum::GROUP_CONCAT_FUNC:
+ case Item_sum::SUM_DISTINCT_FUNC:
+ case Item_sum::AVG_DISTINCT_FUNC:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "This aggregate as window function");
return true;
+ default:
+ break;
}
- return false;
+ return window_functions.push_back(win_func);
}
/*
Compute the value of window function for all rows.
*/
-bool Window_func_runner::exec(TABLE *tbl, SORT_INFO *filesort_result)
+bool Window_func_runner::exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result)
{
- THD *thd= current_thd;
- win_func->set_phase_to_computation();
-
- /* Go through the sorted array and compute the window function */
- READ_RECORD info;
-
- if (init_read_record(&info, thd, tbl, NULL/*select*/, filesort_result,
- 0, 1, FALSE))
- return true;
+ List_iterator_fast<Item_window_func> it(window_functions);
+ Item_window_func *win_func;
+ while ((win_func= it++))
+ {
+ win_func->set_phase_to_computation();
+ // TODO(cvicentiu) Setting the aggregator should probably be done during
+ // setup of Window_funcs_sort.
+ win_func->window_func()->set_aggregator(Aggregator::SIMPLE_AGGREGATOR);
+ }
+ it.rewind();
- bool is_error= compute_func(win_func, tbl, &info);
+ List<Cursor_manager> cursor_managers;
+ get_window_functions_required_cursors(thd, window_functions,
+ &cursor_managers);
- win_func->set_phase_to_retrieval();
+ /* Go through the sorted array and compute the window function */
+ bool is_error= compute_window_func(thd,
+ window_functions,
+ cursor_managers,
+ tbl, filesort_result);
+ while ((win_func= it++))
+ {
+ win_func->set_phase_to_retrieval();
+ }
- end_read_record(&info);
+ cursor_managers.delete_elements();
return is_error;
}
@@ -1872,21 +2372,15 @@ bool Window_funcs_sort::exec(JOIN *join)
THD *thd= join->thd;
JOIN_TAB *join_tab= &join->join_tab[join->top_join_tab_count];
+ /* Sort the table based on the most specific sorting criteria of
+ the window functions. */
if (create_sort_index(thd, join, join_tab, filesort))
return true;
TABLE *tbl= join_tab->table;
SORT_INFO *filesort_result= join_tab->filesort_result;
- bool is_error= false;
- List_iterator<Window_func_runner> it(runners);
- Window_func_runner *runner;
-
- while ((runner= it++))
- {
- if ((is_error= runner->exec(tbl, filesort_result)))
- break;
- }
+ bool is_error= runner.exec(thd, tbl, filesort_result);
delete join_tab->filesort_result;
join_tab->filesort_result= NULL;
@@ -1894,30 +2388,32 @@ bool Window_funcs_sort::exec(JOIN *join)
}
-bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,
- List_iterator<Item_window_func> &it)
+bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,
+ List_iterator<Item_window_func> &it)
{
Item_window_func *win_func= it.peek();
Item_window_func *prev_win_func;
+ /* The iterator should point to a valid function at the start of execution. */
+ DBUG_ASSERT(win_func);
do
{
- Window_func_runner *runner;
- if (!(runner= new Window_func_runner(win_func)) ||
- runner->setup(thd))
- {
+ if (runner.add_function_to_run(win_func))
return true;
- }
- runners.push_back(runner);
it++;
prev_win_func= win_func;
- } while ((win_func= it.peek()) && !(win_func->marker & SORTORDER_CHANGE_FLAG));
-
+ } while ((win_func= it.peek()) &&
+ !(win_func->marker & SORTORDER_CHANGE_FLAG));
+
/*
The sort criteria must be taken from the last win_func in the group of
- adjacent win_funcs that do not have SORTORDER_CHANGE_FLAG.
+ adjacent win_funcs that do not have SORTORDER_CHANGE_FLAG. This is
+ because the sort order must be the most specific sorting criteria defined
+ within the window function group. This ensures that we sort the table
+ in a way that the result is valid for all window functions belonging to
+ this Window_funcs_sort.
*/
- Window_spec *spec = prev_win_func->window_spec;
+ Window_spec *spec= prev_win_func->window_spec;
ORDER* sort_order= concat_order_lists(thd->mem_root,
spec->partition_list->first,
@@ -1932,8 +2428,8 @@ bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel,
bool Window_funcs_computation::setup(THD *thd,
- List<Item_window_func> *window_funcs,
- JOIN_TAB *tab)
+ List<Item_window_func> *window_funcs,
+ JOIN_TAB *tab)
{
order_window_funcs_by_window_specs(window_funcs);
diff --git a/sql/sql_window.h b/sql/sql_window.h
index 54e39d827fe..c3847240e9a 100644
--- a/sql/sql_window.h
+++ b/sql/sql_window.h
@@ -154,9 +154,7 @@ int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
// Classes that make window functions computation a part of SELECT's query plan
//////////////////////////////////////////////////////////////////////////////
-typedef bool (*window_compute_func_t)(Item_window_func *item_win,
- TABLE *tbl, READ_RECORD *info);
-
+class Frame_cursor;
/*
This handles computation of one window function.
@@ -165,21 +163,17 @@ typedef bool (*window_compute_func_t)(Item_window_func *item_win,
class Window_func_runner : public Sql_alloc
{
- Item_window_func *win_func;
-
- /* The function to use for computation*/
- window_compute_func_t compute_func;
-
public:
- Window_func_runner(Item_window_func *win_func_arg) :
- win_func(win_func_arg)
- {}
+ /* Add the function to be computed during the execution pass */
+ bool add_function_to_run(Item_window_func *win_func);
- // Set things up. Create filesort structures, etc
- bool setup(THD *thd);
-
- // This sorts and runs the window function.
- bool exec(TABLE *tbl, SORT_INFO *filesort_result);
+ /* Compute and fill the fields in the table. */
+ bool exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result);
+
+private:
+ /* A list of window functions for which this Window_func_runner will compute
+ values during the execution phase. */
+ List<Item_window_func> window_functions;
};
@@ -191,21 +185,24 @@ public:
class Window_funcs_sort : public Sql_alloc
{
- List<Window_func_runner> runners;
-
- /* Window functions can be computed over this sorting */
- Filesort *filesort;
public:
bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it);
bool exec(JOIN *join);
void cleanup() { delete filesort; }
friend class Window_funcs_computation;
+
+private:
+ Window_func_runner runner;
+
+ /* Window functions can be computed over this sorting */
+ Filesort *filesort;
};
struct st_join_table;
class Explain_aggr_window_funcs;
+
/*
This is a "window function computation phase": a single object of this class
takes care of computing all window functions in a SELECT.
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 9d7e735fc78..551a86e4a41 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -714,34 +714,6 @@ bool add_select_to_union_list(LEX *lex, bool is_union_distinct,
return FALSE;
}
-/**
- @brief Initializes a SELECT_LEX for a query within parentheses (aka
- braces).
-
- @return false if successful, true if an error was reported. In the latter
- case parsing should stop.
- */
-bool setup_select_in_parentheses(LEX *lex)
-{
- SELECT_LEX * sel= lex->current_select;
- /*
- if (sel->set_braces(1))
- {
- my_parse_error(lex->thd, ER_SYNTAX_ERROR);
- return TRUE;
- }
- */
- DBUG_ASSERT(sel->braces);
- if (sel->linkage == UNION_TYPE &&
- !sel->master_unit()->first_select()->braces &&
- sel->master_unit()->first_select()->linkage ==
- UNION_TYPE)
- {
- my_parse_error(lex->thd, ER_SYNTAX_ERROR);
- return TRUE;
- }
- return FALSE;
-}
static bool add_create_index_prepare(LEX *lex, Table_ident *table)
{
@@ -1000,6 +972,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, const char *txt,
List<Item> *item_list;
List<Statement_information_item> *stmt_info_list;
List<String> *string_list;
+ List<LEX_STRING> *lex_str_list;
Statement_information_item *stmt_info_item;
String *string;
TABLE_LIST *table_list;
@@ -2081,6 +2054,8 @@ END_OF_INPUT
%type <lex_str_ptr> query_name
+%type <lex_str_list> opt_with_column_list
+
%%
@@ -5242,6 +5217,27 @@ opt_part_values:
part_info->part_type= LIST_PARTITION;
}
part_values_in {}
+ | DEFAULT
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ if (! lex->is_partition_management())
+ {
+ if (part_info->part_type != LIST_PARTITION)
+ my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
+ "LIST", "DEFAULT"));
+ }
+ else
+ part_info->part_type= LIST_PARTITION;
+ if (part_info->init_column_part(thd))
+ {
+ MYSQL_YYABORT;
+ }
+ if (part_info->add_max_value(thd))
+ {
+ MYSQL_YYABORT;
+ }
+ }
;
part_func_max:
@@ -6070,7 +6066,7 @@ key_def:
constraint_def:
opt_constraint check_constraint
{
- Lex->add_constraint(&$1, $2);
+ Lex->add_constraint(&$1, $2, FALSE);
}
;
@@ -7557,6 +7553,11 @@ alter_list_item:
{
Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT;
}
+ | ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint
+ {
+ Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT;
+ Lex->add_constraint(&$6, $7, TRUE);
+ }
| CHANGE opt_column opt_if_exists_table_element field_ident
field_spec opt_place
{
@@ -8390,8 +8391,7 @@ select_paren:
SELECT_SYM select_options_and_item_list select_part3
opt_select_lock_type
{
- if (setup_select_in_parentheses(Lex))
- MYSQL_YYABORT;
+ DBUG_ASSERT(Lex->current_select->braces);
}
| '(' select_paren ')'
;
@@ -8407,8 +8407,7 @@ select_paren_union_query_term:
SELECT_SYM select_options_and_item_list select_part3_union_query_term
opt_select_lock_type
{
- if (setup_select_in_parentheses(Lex))
- MYSQL_YYABORT;
+ DBUG_ASSERT(Lex->current_select->braces);
}
| '(' select_paren_union_query_term ')'
;
@@ -8424,8 +8423,7 @@ select_paren_view:
SELECT_SYM select_options_and_item_list select_part3_view
opt_select_lock_type
{
- if (setup_select_in_parentheses(Lex))
- MYSQL_YYABORT;
+ DBUG_ASSERT(Lex->current_select->braces);
}
| '(' select_paren_view ')'
;
@@ -8441,8 +8439,7 @@ select_paren_derived:
opt_limit_clause
opt_select_lock_type
{
- if (setup_select_in_parentheses(Lex))
- MYSQL_YYABORT;
+ DBUG_ASSERT(Lex->current_select->braces);
$$= Lex->current_select->master_unit()->first_select();
}
| '(' select_paren_derived ')' { $$= $2; }
@@ -8642,12 +8639,14 @@ opt_select_lock_type:
| FOR_SYM UPDATE_SYM
{
LEX *lex=Lex;
+ lex->current_select->lock_type= TL_WRITE;
lex->current_select->set_lock_for_tables(TL_WRITE);
lex->safe_to_cache_query=0;
}
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{
LEX *lex=Lex;
+ lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS;
lex->current_select->
set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS);
lex->safe_to_cache_query=0;
@@ -11066,12 +11065,7 @@ union_list_derived:
select_init2_derived:
select_part2_derived
{
- LEX *lex= Lex;
- if (lex->current_select->set_braces(0))
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
- MYSQL_YYABORT;
- }
+ Select->set_braces(0);
}
;
@@ -11116,16 +11110,8 @@ select_derived:
derived_query_specification:
SELECT_SYM select_derived_init select_derived2
{
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
if ($2)
- {
- if (sel->set_braces(1))
- {
- my_parse_error(thd, ER_SYNTAX_ERROR);
- MYSQL_YYABORT;
- }
- }
+ Select->set_braces(1);
$$= NULL;
}
;
@@ -13924,13 +13910,18 @@ with_list:
with_list_element:
query_name
opt_with_column_list
+ {
+ $2= new List<LEX_STRING> (Lex->with_column_list);
+ if ($2 == NULL)
+ MYSQL_YYABORT;
+ Lex->with_column_list.empty();
+ }
AS '(' remember_name subselect remember_end ')'
{
- With_element *elem= new With_element($1, Lex->with_column_list, $6->master_unit());
+ With_element *elem= new With_element($1, *$2, $7->master_unit());
if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
MYSQL_YYABORT;
- Lex->with_column_list.empty();
- if (elem->set_unparsed_spec(thd, $5+1, $7))
+ if (elem->set_unparsed_spec(thd, $6+1, $8))
MYSQL_YYABORT;
}
;
@@ -13938,8 +13929,9 @@ with_list_element:
opt_with_column_list:
/* empty */
- {}
+ { $$= NULL; }
| '(' with_column_list ')'
+ { $$= NULL; }
;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index db054a635af..55f2864a93e 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1594,7 +1594,8 @@ Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var)
}
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
if (running)
return true;
@@ -1634,7 +1635,7 @@ Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var)
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- if (master_info_index->give_error_if_slave_running())
+ if (!master_info_index || master_info_index->give_error_if_slave_running())
err= true;
else
err= rpl_gtid_pos_update(thd, var->save_result.string_value.str,
@@ -1823,7 +1824,8 @@ check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var)
bool running;
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
if (running)
return true;
@@ -1838,7 +1840,8 @@ fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- err= master_info_index->give_error_if_slave_running();
+ err= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -1874,7 +1877,8 @@ check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var)
bool running;
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
if (running)
return true;
@@ -1889,7 +1893,8 @@ fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -2039,7 +2044,8 @@ check_gtid_ignore_duplicates(sys_var *self, THD *thd, set_var *var)
bool running;
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
if (running)
return true;
@@ -2054,7 +2060,8 @@ fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type)
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- running= master_info_index->give_error_if_slave_running();
+ running= (!master_info_index ||
+ master_info_index->give_error_if_slave_running());
mysql_mutex_unlock(&LOCK_active_mi);
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -2146,6 +2153,12 @@ static Sys_var_ulong Sys_max_prepared_stmt_count(
VALID_RANGE(0, 1024*1024), DEFAULT(16382), BLOCK_SIZE(1),
&PLock_prepared_stmt_count);
+static Sys_var_ulong Sys_max_recursive_iterations(
+ "max_recursive_iterations",
+ "Maximum number of iterations when executing recursive queries",
+ SESSION_VAR(max_recursive_iterations), CMD_LINE(OPT_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(UINT_MAX), BLOCK_SIZE(1));
+
static Sys_var_ulong Sys_max_sort_length(
"max_sort_length",
"The number of bytes to use when sorting BLOB or TEXT values (only "
@@ -2384,6 +2397,7 @@ export const char *optimizer_switch_names[]=
"extended_keys",
"exists_to_in",
"orderby_uses_equalities",
+ "condition_pushdown_for_derived",
"default",
NullS
};
@@ -2850,7 +2864,6 @@ static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
{
- server_id_supplied = 1;
thd->variables.server_id= global_system_variables.server_id;
/*
Historically, server_id was a global variable that is exported to
@@ -2867,7 +2880,7 @@ static Sys_var_ulong Sys_server_id(
"Uniquely identifies the server instance in the community of "
"replication partners",
SESSION_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
- VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ VALID_RANGE(1, UINT_MAX32), DEFAULT(1), BLOCK_SIZE(1), NO_MUTEX_GUARD,
NOT_IN_BINLOG, ON_CHECK(check_has_super), ON_UPDATE(fix_server_id));
static Sys_var_mybool Sys_slave_compressed_protocol(
@@ -2954,7 +2967,7 @@ Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- if (!master_info_index->give_error_if_slave_running())
+ if (master_info_index && !master_info_index->give_error_if_slave_running())
result= Sys_var_enum::global_update(thd, var);
mysql_mutex_unlock(&LOCK_active_mi);
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -3159,6 +3172,12 @@ static Sys_var_charptr Sys_ssl_crlpath(
READ_ONLY GLOBAL_VAR(opt_ssl_crlpath), SSL_OPT(OPT_SSL_CRLPATH),
IN_FS_CHARSET, DEFAULT(0));
+static Sys_var_mybool Sys_standards_compliant_cte(
+ "standards_compliant_cte",
+ "Allow only standards compiant CTE",
+ SESSION_VAR(only_standards_compliant_cte), CMD_LINE(OPT_ARG),
+ DEFAULT(TRUE));
+
// why ENUM and not BOOL ?
static const char *updatable_views_with_limit_names[]= {"NO", "YES", 0};
@@ -3208,6 +3227,11 @@ static Sys_var_ulong Sys_table_cache_size(
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_table_open_cache));
+static Sys_var_uint Sys_table_cache_instances(
+ "table_open_cache_instances", "Maximum number of table cache instances",
+ READ_ONLY GLOBAL_VAR(tc_instances), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 64), DEFAULT(8), BLOCK_SIZE(1));
+
static Sys_var_ulong Sys_thread_cache_size(
"thread_cache_size",
"How many threads we should keep in a cache for reuse. These are freed after 5 minutes of idle time",
@@ -3364,6 +3388,20 @@ bool Sys_var_tx_read_only::session_update(THD *thd, set_var *var)
{
// @see Sys_var_tx_isolation::session_update() above for the rules.
thd->tx_read_only= var->save_result.ulonglong_value;
+
+#ifndef EMBEDDED_LIBRARY
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ {
+ Transaction_state_tracker *tst= (Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
+
+ if (var->type == OPT_DEFAULT)
+ tst->set_read_flags(thd,
+ thd->tx_read_only ? TX_READ_ONLY : TX_READ_WRITE);
+ else
+ tst->set_read_flags(thd, TX_READ_INHERIT);
+ }
+#endif //EMBEDDED_LIBRARY
}
return false;
}
@@ -3985,14 +4023,16 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
if (!var->save_result.string_value.str)
return true;
- if (var->save_result.string_value.length > FN_REFLEN)
+ LEX_STRING *val= &var->save_result.string_value;
+
+ if (val->length > FN_REFLEN)
{ // path is too long
my_error(ER_PATH_LENGTH, MYF(0), self->name.str);
return true;
}
char path[FN_REFLEN];
- size_t path_length= unpack_filename(path, var->save_result.string_value.str);
+ size_t path_length= unpack_filename(path, val->str);
if (!path_length)
return true;
@@ -4005,6 +4045,17 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
return true;
}
+ static const LEX_CSTRING my_cnf= { STRING_WITH_LEN("my.cnf") };
+ static const LEX_CSTRING my_ini= { STRING_WITH_LEN("my.ini") };
+ if (path_length >= my_cnf.length)
+ {
+ if (strcasecmp(path + path_length - my_cnf.length, my_cnf.str) == 0)
+ return true; // log file name ends with "my.cnf"
+ DBUG_ASSERT(my_cnf.length == my_ini.length);
+ if (strcasecmp(path + path_length - my_ini.length, my_ini.str) == 0)
+ return true; // log file name ends with "my.ini"
+ }
+
MY_STAT f_stat;
if (my_stat(path, &f_stat, MYF(0)))
@@ -4014,9 +4065,9 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
return false;
}
- (void) dirname_part(path, var->save_result.string_value.str, &path_length);
+ (void) dirname_part(path, val->str, &path_length);
- if (var->save_result.string_value.length - path_length >= FN_LEN)
+ if (val->length - path_length >= FN_LEN)
{ // filename is too long
my_error(ER_PATH_LENGTH, MYF(0), self->name.str);
return true;
@@ -4779,9 +4830,10 @@ static Sys_var_mybool Sys_wsrep_auto_increment_control(
CMD_LINE(OPT_ARG), DEFAULT(TRUE));
static Sys_var_mybool Sys_wsrep_drupal_282555_workaround(
- "wsrep_drupal_282555_workaround", "To use a workaround for"
- "bad autoincrement value",
- GLOBAL_VAR(wsrep_drupal_282555_workaround),
+ "wsrep_drupal_282555_workaround", "Enable a workaround to handle the "
+ "cases where inserting a DEFAULT value into an auto-increment column "
+ "could fail with duplicate key error",
+ GLOBAL_VAR(wsrep_drupal_282555_workaround),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
static Sys_var_charptr sys_wsrep_sst_method(
@@ -4840,13 +4892,14 @@ static Sys_var_charptr Sys_wsrep_start_position (
static Sys_var_ulong Sys_wsrep_max_ws_size (
"wsrep_max_ws_size", "Max write set size (bytes)",
GLOBAL_VAR(wsrep_max_ws_size), CMD_LINE(REQUIRED_ARG),
- /* Upper limit is 65K short of 4G to avoid overlows on 32-bit systems */
- VALID_RANGE(1024, WSREP_MAX_WS_SIZE), DEFAULT(1073741824UL), BLOCK_SIZE(1));
+ VALID_RANGE(1024, WSREP_MAX_WS_SIZE), DEFAULT(WSREP_MAX_WS_SIZE),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(wsrep_max_ws_size_check), ON_UPDATE(wsrep_max_ws_size_update));
static Sys_var_ulong Sys_wsrep_max_ws_rows (
"wsrep_max_ws_rows", "Max number of rows in write set",
GLOBAL_VAR(wsrep_max_ws_rows), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, 1048576), DEFAULT(131072), BLOCK_SIZE(1));
+ VALID_RANGE(0, 1048576), DEFAULT(0), BLOCK_SIZE(1));
static Sys_var_charptr Sys_wsrep_notify_cmd(
"wsrep_notify_cmd", "",
@@ -5375,3 +5428,78 @@ static Sys_var_ulong Sys_log_tc_size(
DEFAULT(my_getpagesize() * 6),
BLOCK_SIZE(my_getpagesize()));
#endif
+
+#ifndef EMBEDDED_LIBRARY
+
+static Sys_var_sesvartrack Sys_track_session_sys_vars(
+ "session_track_system_variables",
+ "Track changes in registered system variables. "
+ "For compatibility with MySQL defaults this variable should be set to "
+ "\"autocommit, character_set_client, character_set_connection, "
+ "character_set_results, time_zone\"",
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(""),
+ NO_MUTEX_GUARD);
+
+static bool update_session_track_schema(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ DBUG_ENTER("update_session_track_schema");
+ DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->
+ update(thd, NULL));
+}
+
+static Sys_var_mybool Sys_session_track_schema(
+ "session_track_schema",
+ "Track changes to the default schema.",
+ SESSION_VAR(session_track_schema),
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
+ ON_UPDATE(update_session_track_schema));
+
+
+static bool update_session_track_tx_info(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ DBUG_ENTER("update_session_track_tx_info");
+ DBUG_RETURN(thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)->
+ update(thd, NULL));
+}
+
+static const char *session_track_transaction_info_names[]=
+ { "OFF", "STATE", "CHARACTERISTICS", NullS };
+
+static Sys_var_enum Sys_session_track_transaction_info(
+ "session_track_transaction_info",
+ "Track changes to the transaction attributes. OFF to disable; "
+ "STATE to track just transaction state (Is there an active transaction? "
+ "Does it have any data? etc.); CHARACTERISTICS to track transaction "
+ "state and report all statements needed to start a transaction with"
+ "the same characteristics (isolation level, read only/read write,"
+ "snapshot - but not any work done / data modified within the "
+ "transaction).",
+ SESSION_VAR(session_track_transaction_info),
+ CMD_LINE(REQUIRED_ARG), session_track_transaction_info_names,
+ DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_session_track_tx_info));
+
+
+static bool update_session_track_state_change(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ DBUG_ENTER("update_session_track_state_change");
+ DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->
+ update(thd, NULL));
+}
+
+static Sys_var_mybool Sys_session_track_state_change(
+ "session_track_state_change",
+ "Track changes to the session state.",
+ SESSION_VAR(session_track_state_change),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
+ ON_UPDATE(update_session_track_state_change));
+
+#endif //EMBEDDED_LIBRARY
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index ca6634849a1..c7f148afd39 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -96,7 +96,6 @@ enum charset_enum {IN_SYSTEM_CHARSET, IN_FS_CHARSET};
static const char *bool_values[3]= {"OFF", "ON", 0};
TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 };
-extern const char *encrypt_algorithm_names[];
/**
A small wrapper class to pass getopt arguments as a pair
@@ -438,10 +437,10 @@ public:
does not destroy individual members of SV, there's no way to free
allocated string variables for every thread.
*/
-class Sys_var_charptr: public sys_var
+class Sys_var_charptr_base: public sys_var
{
public:
- Sys_var_charptr(const char *name_arg,
+ Sys_var_charptr_base(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
enum charset_enum is_os_charset_arg,
@@ -463,8 +462,6 @@ public:
*/
option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR;
global_var(const char*)= def_val;
- SYSVAR_ASSERT(scope() == GLOBAL);
- SYSVAR_ASSERT(size == sizeof(char *));
}
void cleanup()
{
@@ -503,31 +500,35 @@ public:
}
bool do_check(THD *thd, set_var *var)
{ return do_string_check(thd, var, charset(thd)); }
- bool session_update(THD *thd, set_var *var)
- {
- DBUG_ASSERT(FALSE);
- return true;
- }
- bool global_update(THD *thd, set_var *var)
+ bool session_update(THD *thd, set_var *var)= 0;
+ char *global_update_prepare(THD *thd, set_var *var)
{
char *new_val, *ptr= var->save_result.string_value.str;
size_t len=var->save_result.string_value.length;
if (ptr)
{
new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME));
- if (!new_val) return true;
+ if (!new_val) return 0;
new_val[len]=0;
}
else
new_val= 0;
+ return new_val;
+ }
+ void global_update_finish(char *new_val)
+ {
if (flags & ALLOCATED)
my_free(global_var(char*));
flags|= ALLOCATED;
global_var(char*)= new_val;
- return false;
}
- void session_save_default(THD *thd, set_var *var)
- { DBUG_ASSERT(FALSE); }
+ bool global_update(THD *thd, set_var *var)
+ {
+ char *new_val= global_update_prepare(thd, var);
+ global_update_finish(new_val);
+ return (new_val == 0 && var->save_result.string_value.str != 0);
+ }
+ void session_save_default(THD *thd, set_var *var)= 0;
void global_save_default(THD *thd, set_var *var)
{
char *ptr= (char*)(intptr)option.def_value;
@@ -536,6 +537,104 @@ public:
}
};
+class Sys_var_charptr: public Sys_var_charptr_base
+{
+public:
+ Sys_var_charptr(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0) :
+ Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt,
+ is_os_charset_arg, def_val, lock, binlog_status_arg,
+ on_check_func, on_update_func, substitute)
+ {
+ SYSVAR_ASSERT(scope() == GLOBAL);
+ SYSVAR_ASSERT(size == sizeof(char *));
+ }
+
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+};
+
+#ifndef EMBEDDED_LIBRARY
+class Sys_var_sesvartrack: public Sys_var_charptr_base
+{
+public:
+ Sys_var_sesvartrack(const char *name_arg,
+ const char *comment,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock) :
+ Sys_var_charptr_base(name_arg, comment,
+ SESSION_VAR(session_track_system_variables),
+ getopt, is_os_charset_arg, def_val, lock,
+ VARIABLE_NOT_IN_BINLOG, 0, 0, 0)
+ {}
+ bool do_check(THD *thd, set_var *var)
+ {
+ if (Sys_var_charptr_base::do_check(thd, var) ||
+ sysvartrack_validate_value(thd, var->save_result.string_value.str,
+ var->save_result.string_value.length))
+ return TRUE;
+ return FALSE;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ char *new_val= global_update_prepare(thd, var);
+ if (new_val)
+ {
+ if (sysvartrack_reprint_value(thd, new_val,
+ var->save_result.string_value.length))
+ new_val= 0;
+ }
+ global_update_finish(new_val);
+ return (new_val == 0 && var->save_result.string_value.str != 0);
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ return sysvartrack_update(thd, var);
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.string_value.str= global_var(char*);
+ var->save_result.string_value.length=
+ strlen(var->save_result.string_value.str);
+ /* parse and feel list with default values */
+ if (thd)
+ {
+ bool res=
+ sysvartrack_validate_value(thd,
+ var->save_result.string_value.str,
+ var->save_result.string_value.length);
+ DBUG_ASSERT(res == 0);
+ }
+ }
+ uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ {
+ DBUG_ASSERT(thd != NULL);
+ size_t len= sysvartrack_value_len(thd);
+ char *res= (char *)thd->alloc(len + sizeof(char *));
+ if (res)
+ {
+ char *buf= res + sizeof(char *);
+ *((char**) res)= buf;
+ sysvartrack_value_construct(thd, buf, len);
+ }
+ return (uchar *)res;
+ }
+};
+#endif //EMBEDDED_LIBRARY
+
class Sys_var_proxy_user: public sys_var
{
@@ -1978,7 +2077,47 @@ public:
if (var->type == OPT_SESSION && Sys_var_enum::session_update(thd, var))
return TRUE;
if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction())
+ {
+#ifndef EMBEDDED_LIBRARY
+ Transaction_state_tracker *tst= NULL;
+
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ tst= (Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
+#endif //EMBEDDED_LIBRARY
+
thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value;
+
+#ifndef EMBEDDED_LIBRARY
+ if (var->type == OPT_DEFAULT)
+ {
+ enum enum_tx_isol_level l;
+ switch (thd->tx_isolation) {
+ case ISO_READ_UNCOMMITTED:
+ l= TX_ISOL_UNCOMMITTED;
+ break;
+ case ISO_READ_COMMITTED:
+ l= TX_ISOL_COMMITTED;
+ break;
+ case ISO_REPEATABLE_READ:
+ l= TX_ISOL_REPEATABLE;
+ break;
+ case ISO_SERIALIZABLE:
+ l= TX_ISOL_SERIALIZABLE;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ return TRUE;
+ }
+ if (tst)
+ tst->set_isol_level(thd, l);
+ }
+ else if (tst)
+ {
+ tst->set_isol_level(thd, TX_ISOL_INHERIT);
+ }
+#endif //EMBEDDED_LIBRARY
+ }
return FALSE;
}
};
diff --git a/sql/table.cc b/sql/table.cc
index 640ab8267fb..9a9dee6e0ca 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -41,6 +41,7 @@
#include "mdl.h" // MDL_wait_for_graph_visitor
#include "sql_view.h"
#include "rpl_filter.h"
+#include "sql_cte.h"
/* For MySQL 5.7 virtual fields */
#define MYSQL57_GENERATED_FIELD 128
@@ -66,7 +67,7 @@ LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
Keyword added as a prefix when parsing the defining expression for a
virtual column read from the column definition saved in the frm file
*/
-LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
static int64 last_table_id;
@@ -1431,48 +1432,38 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, keys,n_length,int_length, com_length, vcol_screen_length));
+ if (!multi_alloc_root(&share->mem_root,
+ &share->field, (uint)(share->fields+1)*sizeof(Field*),
+ &share->intervals, (uint)interval_count*sizeof(TYPELIB),
+ &share->check_constraints, (uint) share->table_check_constraints * sizeof(Virtual_column_info*),
+ &interval_array, (uint) (share->fields+interval_parts+ keys+3)*sizeof(char *),
+ &names, (uint) (n_length+int_length),
+ &comment_pos, (uint) com_length,
+ &vcol_screen_pos, vcol_screen_length,
+ NullS))
- if (!(field_ptr = (Field **)
- alloc_root(&share->mem_root,
- (uint) ((share->fields+1)*sizeof(Field*)+
- interval_count*sizeof(TYPELIB)+
- share->table_check_constraints *
- sizeof(Virtual_column_info*)+
- (share->fields+interval_parts+
- keys+3)*sizeof(char *)+
- (n_length+int_length+com_length+
- vcol_screen_length)))))
- goto err; /* purecov: inspected */
+ goto err;
- share->field= field_ptr;
+ field_ptr= share->field;
+ table_check_constraints= share->check_constraints;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length+
vcol_screen_length));
strpos= disk_buff+pos;
- share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
- share->check_constraints= ((Virtual_column_info**)
- (share->intervals+interval_count));
- table_check_constraints= share->check_constraints;
- interval_array= (const char **) (table_check_constraints+
- share->table_check_constraints);
- names= (char*) (interval_array+share->fields+interval_parts+keys+3);
if (!interval_count)
share->intervals= 0; // For better debugging
- memcpy((char*) names, strpos+(share->fields*field_pack_length),
- (uint) (n_length+int_length));
- comment_pos= names+(n_length+int_length);
+
+ memcpy(names, strpos+(share->fields*field_pack_length), n_length+int_length);
memcpy(comment_pos, disk_buff+read_length-com_length-vcol_screen_length,
com_length);
- vcol_screen_pos= (uchar*) (names+(n_length+int_length+com_length));
memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length,
vcol_screen_length);
fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
goto err;
- fix_type_pointers(&interval_array, share->intervals, interval_count,
- &names);
+ fix_type_pointers(&interval_array, share->intervals, interval_count, &names);
{
/* Set ENUM and SET lengths */
@@ -1561,6 +1552,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
LEX_STRING comment;
Virtual_column_info *vcol_info= 0;
uint gis_length, gis_decimals, srid= 0;
+ Field::utype unireg_check;
if (new_frm_ver >= 3)
{
@@ -1776,22 +1768,36 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
}
+ /* Convert pre-10.2.2 timestamps to use Field::default_value */
+ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type);
+ if (unireg_check == Field::TIMESTAMP_DNUN_FIELD)
+ unireg_check= Field::TIMESTAMP_UN_FIELD;
+ if (unireg_check == Field::TIMESTAMP_DN_FIELD)
+ unireg_check= Field::NONE;
+
*field_ptr= reg_field=
- make_field(share, &share->mem_root, record+recpos,
- (uint32) field_length,
- null_pos, null_bit_pos,
- pack_flag,
- field_type,
- charset,
- geom_type, srid,
- (Field::utype) MTYP_TYPENR(unireg_type),
- (interval_nr ?
- share->intervals+interval_nr-1 :
- (TYPELIB*) 0),
+ make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
+ null_pos, null_bit_pos, pack_flag, field_type, charset,
+ geom_type, srid, unireg_check,
+ (interval_nr ? share->intervals+interval_nr-1 : NULL),
share->fieldnames.type_names[i]);
if (!reg_field) // Not supported field type
goto err;
+ if (unireg_check != (Field::utype) MTYP_TYPENR(unireg_type))
+ {
+ char buf[32];
+ if (reg_field->decimals())
+ my_snprintf(buf, sizeof(buf), "CURRENT_TIMESTAMP(%d)", reg_field->decimals());
+ else
+ strmov(buf, "CURRENT_TIMESTAMP");
+
+ reg_field->default_value= new (&share->mem_root) Virtual_column_info();
+ reg_field->default_value->stored_in_db= 1;
+ thd->make_lex_string(&reg_field->default_value->expr_str, buf, strlen(buf));
+ share->default_expressions++;
+ }
+
reg_field->field_index= i;
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
@@ -1831,13 +1837,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->stored_rec_length>=recpos)
share->stored_rec_length= recpos-1;
}
- if (reg_field->has_insert_default_function())
- has_insert_default_function= 1;
if (reg_field->has_update_default_function())
+ {
has_update_default_function= 1;
- if (reg_field->has_insert_default_function() ||
- reg_field->has_update_default_function())
- share->default_fields++;
+ if (!reg_field->default_value)
+ share->default_fields++;
+ }
}
*field_ptr=0; // End marker
/* Sanity checks: */
@@ -1943,6 +1948,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
(keyinfo-1)->ext_key_parts;
+ uint add_keyparts_for_this_key= add_first_key_parts;
/*
Do not extend the key that contains a component
@@ -1954,19 +1960,20 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->field[fieldnr-1]->key_length() !=
keyinfo->key_part[i].length)
{
- add_first_key_parts= 0;
+ add_keyparts_for_this_key= 0;
break;
}
}
- if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->user_defined_key_parts)
+ if (add_keyparts_for_this_key < (keyinfo->ext_key_parts -
+ keyinfo->user_defined_key_parts))
{
share->ext_key_parts-= keyinfo->ext_key_parts;
key_part_map ext_key_part_map= keyinfo->ext_key_part_map;
keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->ext_key_part_map= 0;
- for (i= 0; i < add_first_key_parts; i++)
+ for (i= 0; i < add_keyparts_for_this_key; i++)
{
if (ext_key_part_map & 1<<i)
{
@@ -2223,16 +2230,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
case 1: // Generated stored field
vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->vcol_info);
reg_field->vcol_info= vcol_info;
share->virtual_fields++;
share->virtual_stored_fields++; // For insert/load data
break;
case 2: // Default expression
vcol_info->stored_in_db= 1;
+ DBUG_ASSERT(!reg_field->default_value);
reg_field->default_value= vcol_info;
share->default_expressions++;
break;
case 3: // Field check constraint
+ DBUG_ASSERT(!reg_field->check_constraint);
reg_field->check_constraint= vcol_info;
share->field_check_constraints++;
break;
@@ -2299,6 +2309,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bitmap_count= 1;
if (share->table_check_constraints)
{
+ feature_check_constraint++;
if (!(share->check_set= (MY_BITMAP*)
alloc_root(&share->mem_root, sizeof(*share->check_set))))
goto err;
@@ -2512,6 +2523,68 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
}
+static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_vcol_expr");
+
+ const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+
+ const char *save_where= thd->where;
+ thd->where= "virtual column function";
+
+ int error= vcol->expr_item->fix_fields(thd, &vcol->expr_item);
+
+ thd->mark_used_columns= save_mark_used_columns;
+ thd->where= save_where;
+
+ if (unlikely(error))
+ {
+ my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), vcol->expr_str);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/** rerun fix_fields for vcols that returns time- or session- dependent values
+
+ @note this is done for all vcols for INSERT/UPDATE/DELETE,
+ and only as needed for SELECTs.
+*/
+bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_session_vcol_expr");
+ if (!(vcol->flags & (VCOL_TIME_FUNC|VCOL_SESSION_FUNC)))
+ DBUG_RETURN(0);
+
+ vcol->expr_item->cleanup();
+ DBUG_ASSERT(!vcol->expr_item->fixed);
+ DBUG_RETURN(fix_vcol_expr(thd, vcol));
+}
+
+
+/** invoke fix_session_vcol_expr for a vcol
+
+ @note this is called for generated column or a DEFAULT expression from
+ their corresponding fix_fields on SELECT.
+*/
+bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
+ Virtual_column_info *vcol)
+{
+ DBUG_ENTER("fix_session_vcol_expr_for_read");
+ TABLE_LIST *tl= field->table->pos_in_table_list;
+ if (!tl || tl->lock_type >= TL_WRITE_ALLOW_WRITE)
+ DBUG_RETURN(0);
+ Security_context *save_security_ctx= thd->security_ctx;
+ if (tl->security_ctx)
+ thd->security_ctx= tl->security_ctx;
+ bool res= fix_session_vcol_expr(thd, vcol);
+ thd->security_ctx= save_security_ctx;
+ DBUG_RETURN(res);
+}
+
+
/*
@brief
Perform semantic analysis of the defining expression for a virtual column
@@ -2539,53 +2612,31 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
FALSE Otherwise
*/
-static bool fix_vcol_expr(THD *thd, TABLE *table, Field *field,
- Virtual_column_info *vcol)
+static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, Field *field,
+ Virtual_column_info *vcol)
{
Item* func_expr= vcol->expr_item;
- bool result= TRUE;
- TABLE_LIST tables;
- int error= 0;
- const char *save_where;
- enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- DBUG_ENTER("fix_vcol_expr");
+ DBUG_ENTER("fix_and_check_vcol_expr");
DBUG_PRINT("info", ("vcol: %p", vcol));
DBUG_ASSERT(func_expr);
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ if (func_expr->fixed)
+ DBUG_RETURN(0); // nothing to do
- save_where= thd->where;
- thd->where= "virtual column function";
+ if (fix_vcol_expr(thd, vcol))
+ DBUG_RETURN(1);
- /* Fix fields referenced to by the virtual column function */
- thd->in_stored_expression= 1;
- if (!func_expr->fixed)
- error= func_expr->fix_fields(thd, &vcol->expr_item);
- thd->in_stored_expression= 0;
+ if (vcol->flags)
+ DBUG_RETURN(0); // already checked, no need to do it again
- if (unlikely(error))
- {
- DBUG_PRINT("info",
- ("Field in virtual column expression does not belong to the table"));
- my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), vcol->expr_str);
- goto end;
- }
/* fix_fields could've changed the expression */
func_expr= vcol->expr_item;
- /* Number of columns will be checked later */
- thd->where= save_where;
+ /* this was checked in check_expression(), but the frm could be mangled... */
if (unlikely(func_expr->result_type() == ROW_RESULT))
{
- my_error(ER_ROW_EXPR_FOR_VCOL, MYF(0));
- goto end;
- }
-
- /* Check that we are not refering to any not yet initialized fields */
- if (field)
- {
- if (func_expr->walk(&Item::check_field_expression_processor, 0, field))
- goto end;
+ my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
+ DBUG_RETURN(1);
}
/*
@@ -2595,31 +2646,29 @@ static bool fix_vcol_expr(THD *thd, TABLE *table, Field *field,
Item::vcol_func_processor_result res;
res.errors= 0;
- error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
+ int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
if (error || (res.errors & VCOL_IMPOSSIBLE))
- {
+ { // this can only happen if the frm was corrupted
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
- "???", field->field_name);
- goto end;
+ "???", field ? field->field_name : "?????");
+ DBUG_RETURN(1);
}
vcol->flags= res.errors;
/*
Mark what kind of default / virtual fields the table has
*/
- if (vcol->stored_in_db && vcol->flags & VCOL_NON_DETERMINISTIC)
- table->s->non_determinstic_insert= 1;
+ if (vcol->stored_in_db &&
+ vcol->flags & (VCOL_NON_DETERMINISTIC | VCOL_SESSION_FUNC))
+ table->s->non_determinstic_insert= true;
- result= FALSE;
-
-end:
+ if (vcol->flags & VCOL_SESSION_FUNC)
+ table->s->vcols_need_refixing= true;
- thd->mark_used_columns= save_mark_used_columns;
- table->map= 0; //Restore old value
-
- DBUG_RETURN(result);
+ DBUG_RETURN(0);
}
+
/*
@brief
Unpack the definition of a virtual column from its linear representation
@@ -2639,7 +2688,7 @@ end:
pointer to this item is placed into in a Virtual_column_info object
that is created. After this the function performs
semantic analysis of the item by calling the the function
- fix_vcol_expr(). Since the defining expression is part of the table
+ fix_and_check_vcol_expr(). Since the defining expression is part of the table
definition the item for it is created in table->memroot within the
special arena TABLE::expr_arena or in the thd memroot for INSERT DELAYED
@@ -2690,14 +2739,10 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd,
vcol_expr->length +
parse_vcol_keyword.length + 3)))
DBUG_RETURN(0);
- memcpy(vcol_expr_str,
- (char*) parse_vcol_keyword.str,
- parse_vcol_keyword.length);
+ memcpy(vcol_expr_str, parse_vcol_keyword.str, parse_vcol_keyword.length);
str_len= parse_vcol_keyword.length;
vcol_expr_str[str_len++]= '(';
- memcpy(vcol_expr_str + str_len,
- (char*) vcol_expr->str,
- vcol_expr->length);
+ memcpy(vcol_expr_str + str_len, vcol_expr->str, vcol_expr->length);
str_len+= vcol_expr->length;
vcol_expr_str[str_len++]= ')';
vcol_expr_str[str_len++]= 0;
@@ -2740,21 +2785,14 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd,
thd->update_charset(&my_charset_utf8mb4_general_ci,
table->s->table_charset);
}
- thd->in_stored_expression= 1;
error= parse_sql(thd, &parser_state, NULL);
- thd->in_stored_expression= 0;
if (error)
goto err;
- /*
- mark if expression will be stored in the table. This is also used by
- fix_vcol_expr() to mark if we are using non deterministic functions.
- */
vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
vcol_storage.vcol_info->name= vcol->name;
vcol_storage.vcol_info->utf8= vcol->utf8;
- /* Validate the Item tree. */
- if (!fix_vcol_expr(thd, table, field, vcol_storage.vcol_info))
+ if (!fix_and_check_vcol_expr(thd, table, field, vcol_storage.vcol_info))
{
vcol_info= vcol_storage.vcol_info; // Expression ok
goto end;
@@ -2774,6 +2812,14 @@ end:
DBUG_RETURN(vcol_info);
}
+static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
+{
+ bool res= vcol &&
+ vcol->expr_item->walk(&Item::check_field_expression_processor, 0,
+ field);
+ return res;
+}
+
/*
Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
*/
@@ -3038,34 +3084,29 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
}
field->default_value= vcol;
- if (is_create_table &&
- !(vcol->flags & (VCOL_UNKNOWN | VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC)))
- {
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
- my_ptrdiff_t off= share->default_values - outparam->record[0];
- field->move_field_offset(off);
- int res= vcol->expr_item->save_in_field(field, 1);
- field->move_field_offset(-off);
- thd->count_cuted_fields= old_count_cuted_fields;
- if (res != 0 && res != 3)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), field->field_name);
- error= OPEN_FRM_CORRUPTED;
- goto err;
- }
- }
*(dfield_ptr++)= *field_ptr;
}
else
- if ((field->has_insert_default_function() ||
- field->has_update_default_function()))
+ if (field->has_update_default_function())
*(dfield_ptr++)= *field_ptr;
}
*vfield_ptr= 0; // End marker
*dfield_ptr= 0; // End marker
+ /* Check that expressions aren't refering to not yet initialized fields */
+ for (field_ptr= outparam->field; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ if (check_vcol_forward_refs(field, field->vcol_info) ||
+ check_vcol_forward_refs(field, field->check_constraint) ||
+ check_vcol_forward_refs(field, field->default_value))
+ {
+ error= OPEN_FRM_CORRUPTED;
+ goto err;
+ }
+ }
+
/* Update to use trigger fields */
switch_defaults_to_nullable_trigger_fields(outparam);
@@ -6275,7 +6316,7 @@ void TABLE::mark_columns_needed_for_update()
to compare records and detect data change.
*/
if ((file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) &&
- default_field && has_default_function(true))
+ default_field && s->has_update_default_function)
bitmap_union(read_set, write_set);
DBUG_VOID_RETURN;
}
@@ -6573,17 +6614,13 @@ void TABLE::mark_default_fields_for_write(bool is_insert)
for (field_ptr= default_field; *field_ptr; field_ptr++)
{
field= (*field_ptr);
- if (field->default_value)
+ if (is_insert && field->default_value)
{
- if (is_insert)
- {
- bitmap_set_bit(write_set, field->field_index);
- field->default_value->expr_item->
- walk(&Item::register_field_in_read_map, 1, 0);
- }
+ bitmap_set_bit(write_set, field->field_index);
+ field->default_value->expr_item->
+ walk(&Item::register_field_in_read_map, 1, 0);
}
- else if ((is_insert && field->has_insert_default_function()) ||
- (!is_insert && field->has_update_default_function()))
+ else if (!is_insert && field->has_update_default_function())
bitmap_set_bit(write_set, field->field_index);
}
DBUG_VOID_RETURN;
@@ -7314,7 +7351,8 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
{
if (!update_command)
{
- if (field->default_value)
+ if (field->default_value &&
+ (field->default_value->flags || field->flags & BLOB_FLAG))
res|= (field->default_value->expr_item->save_in_field(field, 0) < 0);
else
res|= field->evaluate_insert_default_function();
@@ -7451,7 +7489,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
/*
We're here if:
- validate_value_in_record_with_warn() failed and
- strict mode converted WARN to ERROR
+ strict mo validate_default_values_of_unset_fieldsde converted WARN to ERROR
- or the connection was killed, or closed unexpectedly
*/
DBUG_RETURN(true);
@@ -7462,6 +7500,59 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
}
+bool TABLE::insert_all_rows_into(THD *thd, TABLE *dest, bool with_cleanup)
+{
+ int write_err= 0;
+
+ DBUG_ENTER("TABLE::insert_all_rows_into");
+
+ if (with_cleanup)
+ {
+ if ((write_err= dest->file->ha_delete_all_rows()))
+ goto err;
+ }
+
+ if (file->indexes_are_disabled())
+ dest->file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
+ file->ha_index_or_rnd_end();
+
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
+
+ if (dest->no_rows)
+ dest->file->extra(HA_EXTRA_NO_ROWS);
+ else
+ {
+ /* update table->file->stats.records */
+ file->info(HA_STATUS_VARIABLE);
+ dest->file->ha_start_bulk_insert(file->stats.records);
+ }
+
+ while (!file->ha_rnd_next(dest->record[1]))
+ {
+ write_err= dest->file->ha_write_tmp_row(dest->record[1]);
+ if (write_err)
+ goto err;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ goto err_killed;
+ }
+ }
+ if (!dest->no_rows && dest->file->ha_end_bulk_insert())
+ goto err;
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("error",("Got error: %d",write_err));
+ file->print_error(write_err, MYF(0));
+err_killed:
+ (void) file->ha_rnd_end();
+ DBUG_RETURN(1);
+}
+
+
+
/*
@brief Reset const_table flag
@@ -7502,20 +7593,24 @@ void TABLE_LIST::reset_const_table()
bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
{
- SELECT_LEX_UNIT *unit;
+ SELECT_LEX_UNIT *unit= get_unit();
DBUG_ENTER("handle_derived");
DBUG_PRINT("enter", ("phases: 0x%x", phases));
- if ((unit= get_unit()))
+
+ if (unit)
{
- for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
- if (sl->handle_derived(lex, phases))
- DBUG_RETURN(TRUE);
- DBUG_RETURN(mysql_handle_single_derived(lex, this, phases));
+ if (!is_with_table_recursive_reference())
+ {
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ if (sl->handle_derived(lex, phases))
+ DBUG_RETURN(TRUE);
+ }
+ if (mysql_handle_single_derived(lex, this, phases))
+ DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
}
-
/**
@brief
Return unit of this derived table/view
@@ -7623,7 +7718,8 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
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))
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI) &&
+ !is_recursive_with_table())
set_merged_derived();
else
set_materialized_derived();
@@ -7793,6 +7889,7 @@ bool TABLE_LIST::is_with_table()
return derived && derived->with_element;
}
+
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
@@ -7808,3 +7905,186 @@ double KEY::actual_rec_per_key(uint i)
return (is_statistics_from_stat_tables ?
read_stats->get_avg_frequency(i) : (double) rec_per_key[i]);
}
+
+
+/**
+ @brief
+ Mark subformulas of a condition unusable for the condition pushed into table
+
+ @param cond The condition whose subformulas are to be marked
+
+ @details
+ This method recursively traverses the AND-OR condition cond and for each subformula
+ of the codition it checks whether it can be usable for the extraction of a condition
+ that can be pushed into this table. The subformulas that are not usable are
+ marked with the flag NO_EXTRACTION_FL.
+ @note
+ This method is called before any call of TABLE_LIST::build_pushable_cond_for_table.
+ The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
+ for the subformula when extracting the pushable condition.
+*/
+
+void TABLE_LIST::check_pushable_cond_for_table(Item *cond)
+{
+ table_map tab_map= table->map;
+ cond->clear_extraction_flag();
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ uint count= 0;
+ Item *item;
+ while ((item=li++))
+ {
+ check_pushable_cond_for_table(item);
+ if (item->get_extraction_flag() != NO_EXTRACTION_FL)
+ count++;
+ else if (!and_cond)
+ break;
+ }
+ if ((and_cond && count == 0) || item)
+ {
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+ if (and_cond)
+ li.rewind();
+ while ((item= li++))
+ item->clear_extraction_flag();
+ }
+ }
+ else if (cond->walk(&Item::exclusive_dependence_on_table_processor,
+ 0, (void *) &tab_map))
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+}
+
+
+/**
+ @brief
+ Build condition extractable from the given one depended only on this table
+
+ @param thd The thread handle
+ @param cond The condition from which the pushable one is to be extracted
+
+ @details
+ For the given condition cond this method finds out what condition depended
+ only on this table can be extracted from cond. If such condition C exists
+ the method builds the item for it.
+ The method uses the flag NO_EXTRACTION_FL set by the preliminary call of
+ the method TABLE_LIST::check_pushable_cond_for_table to figure out whether
+ a subformula depends only on this table or not.
+ @note
+ The built condition C is always implied by the condition cond
+ (cond => C). The method tries to build the most restictive such
+ condition (i.e. for any other condition C' such that cond => C'
+ we have C => C').
+ @note
+ The build item is not ready for usage: substitution for the field items
+ has to be done and it has to be re-fixed.
+
+ @retval
+ the built condition pushable into this table if such a condition exists
+ NULL if there is no such a condition
+*/
+
+Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
+{
+ table_map tab_map= table->map;
+ bool is_multiple_equality= cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC;
+ if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
+ return 0;
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool cond_and= false;
+ Item_cond *new_cond;
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ cond_and= true;
+ new_cond=new (thd->mem_root) Item_cond_and(thd);
+ }
+ else
+ new_cond= new (thd->mem_root) Item_cond_or(thd);
+ if (!new_cond)
+ return 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ if (!cond_and)
+ return 0;
+ continue;
+ }
+ Item *fix= build_pushable_cond_for_table(thd, item);
+ if (!fix && !cond_and)
+ return 0;
+ if (!fix)
+ continue;
+ new_cond->argument_list()->push_back(fix, thd->mem_root);
+ }
+ switch (new_cond->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ return new_cond;
+ }
+ }
+ else if (is_multiple_equality)
+ {
+ if (!(cond->used_tables() & tab_map))
+ return 0;
+ Item *new_cond= NULL;
+ int i= 0;
+ Item_equal *item_equal= (Item_equal *) cond;
+ Item *left_item = item_equal->get_const();
+ Item_equal_fields_iterator it(*item_equal);
+ Item *item;
+ if (!left_item)
+ {
+ while ((item=it++))
+ if (item->used_tables() == tab_map)
+ {
+ left_item= item;
+ break;
+ }
+ }
+ if (!left_item)
+ return 0;
+ while ((item=it++))
+ {
+ if (!(item->used_tables() == tab_map))
+ continue;
+ Item_func_eq *eq= 0;
+ Item *left_item_clone= left_item->build_clone(thd, thd->mem_root);
+ Item *right_item_clone= item->build_clone(thd, thd->mem_root);
+ if (left_item_clone && right_item_clone)
+ eq= new (thd->mem_root) Item_func_eq(thd, right_item_clone,
+ left_item_clone);
+ if (eq)
+ {
+ i++;
+ switch (i)
+ {
+ case 1:
+ new_cond= eq;
+ break;
+ case 2:
+ new_cond= new (thd->mem_root) Item_cond_and(thd, new_cond, eq);
+ break;
+ default:
+ ((Item_cond_and*)new_cond)->argument_list()->push_back(eq,
+ thd->mem_root);
+ }
+ }
+ }
+ if (new_cond)
+ new_cond->fix_fields(thd, &new_cond);
+ return new_cond;
+ }
+ else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
+ return cond->build_clone(thd, thd->mem_root);
+ return 0;
+}
diff --git a/sql/table.h b/sql/table.h
index 651fab7c4cb..bc30b00ac45 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -681,10 +681,10 @@ struct TABLE_SHARE
bool can_cmp_whole_record;
bool table_creation_was_logged;
bool non_determinstic_insert;
+ bool vcols_need_refixing;
bool virtual_stored_fields;
bool check_set_initialized;
bool has_update_default_function;
- bool has_insert_default_function;
ulong table_map_id; /* for row-based replication */
/*
@@ -1021,13 +1021,14 @@ private:
One should use methods of I_P_List template instead.
*/
TABLE *share_all_next, **share_all_prev;
+ TABLE *global_free_next, **global_free_prev;
friend struct All_share_tables;
+ friend struct Table_cache_instance;
public:
+ uint32 instance; /** Table cache instance this TABLE is belonging to */
THD *in_use; /* Which thread uses this */
- /* Time when table was released to table cache. Valid for unused tables. */
- ulonglong tc_time;
Field **field; /* Pointer to fields */
uchar *record[2]; /* Pointer to records */
@@ -1310,18 +1311,6 @@ public:
void mark_columns_used_by_check_constraints(void);
void mark_check_constraint_columns_for_read(void);
int verify_constraints(bool ignore_failure);
- /**
- Check if a table has a default function either for INSERT or UPDATE-like
- operation
- @retval true there is a default function
- @retval false there is no default function
- */
- inline bool has_default_function(bool is_update)
- {
- return (is_update ?
- s->has_update_default_function :
- s->has_insert_default_function);
- }
inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
MY_BITMAP *write_set_arg)
{
@@ -1441,6 +1430,8 @@ public:
inline Field **field_to_fill();
bool validate_default_values_of_unset_fields(THD *thd) const;
+
+ bool insert_all_rows_into(THD *thd, TABLE *dest, bool with_cleanup);
};
@@ -1893,7 +1884,10 @@ struct TABLE_LIST
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
*/
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
- With_element *with; /* With element of with_table */
+ With_element *with; /* With element defining this table (if any) */
+ /* Bitmap of the defining with element */
+ table_map with_internal_reference_map;
+ bool block_handle_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
/*
@@ -2263,6 +2257,10 @@ struct TABLE_LIST
return (derived_type & DTYPE_TABLE);
}
bool is_with_table();
+ bool is_recursive_with_table();
+ bool is_with_table_recursive_reference();
+ bool fill_recursive(THD *thd);
+
inline void set_view()
{
derived_type= DTYPE_VIEW;
@@ -2347,6 +2345,8 @@ struct TABLE_LIST
return false;
}
void set_lock_type(THD* thd, enum thr_lock_type lock);
+ void check_pushable_cond_for_table(Item *cond);
+ Item *build_pushable_cond_for_table(THD *thd, Item *cond);
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
@@ -2631,6 +2631,9 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
const char *alias, uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
bool is_create_table);
+bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol);
+bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
+ Virtual_column_info *vcol);
Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root,
TABLE *table,
Field *field,
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index bfe51df5de1..6a6b8ab827e 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -55,10 +55,14 @@
/** Configuration. */
ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */
ulong tc_size; /**< Table cache threshold for LRU eviction. */
+uint32 tc_instances;
+static uint32 tc_active_instances= 1;
+static uint32 tc_contention_warning_reported;
/** Data collections. */
static LF_HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
/** Collection of unused TABLE_SHARE objects. */
+static
I_P_List <TDC_element,
I_P_List_adapter<TDC_element, &TDC_element::next, &TDC_element::prev>,
I_P_List_null_counter,
@@ -67,8 +71,6 @@ I_P_List <TDC_element,
static int64 tdc_version; /* Increments on each reload */
static bool tdc_inited;
-static int32 tc_count; /**< Number of TABLE objects in table cache. */
-
/**
Protects unused shares list.
@@ -81,11 +83,13 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */
static mysql_mutex_t LOCK_unused_shares;
#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share;
+static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share,
+ key_LOCK_table_cache;
static PSI_mutex_info all_tc_mutexes[]=
{
{ &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL },
- { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
+ { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 },
+ { &key_LOCK_table_cache, "LOCK_table_cache", 0 }
};
static PSI_cond_key key_TABLE_SHARE_COND_release;
@@ -93,19 +97,6 @@ static PSI_cond_info all_tc_conds[]=
{
{ &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 }
};
-
-
-static void init_tc_psi_keys(void)
-{
- const char *category= "sql";
- int count;
-
- count= array_elements(all_tc_mutexes);
- mysql_mutex_register(category, all_tc_mutexes, count);
-
- count= array_elements(all_tc_conds);
- mysql_cond_register(category, all_tc_conds, count);
-}
#endif
@@ -125,67 +116,153 @@ static int fix_thd_pins(THD *thd)
part of table definition cache.
*/
-static void intern_close_table(TABLE *table)
+struct Table_cache_instance
{
- DBUG_ENTER("intern_close_table");
- DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx",
- table->s ? table->s->db.str : "?",
- table->s ? table->s->table_name.str : "?",
- (long) table));
+ /**
+ Protects free_tables (TABLE::global_free_next and TABLE::global_free_prev),
+ records, Share_free_tables::List (TABLE::prev and TABLE::next),
+ TABLE::in_use.
+ */
+ mysql_mutex_t LOCK_table_cache;
+ I_P_List <TABLE, I_P_List_adapter<TABLE, &TABLE::global_free_next,
+ &TABLE::global_free_prev>,
+ I_P_List_null_counter, I_P_List_fast_push_back<TABLE> >
+ free_tables;
+ ulong records;
+ uint mutex_waits;
+ uint mutex_nowaits;
+ /** Avoid false sharing between instances */
+ char pad[CPU_LEVEL1_DCACHE_LINESIZE];
+
+ Table_cache_instance(): records(0), mutex_waits(0), mutex_nowaits(0)
+ {
+ mysql_mutex_init(key_LOCK_table_cache, &LOCK_table_cache,
+ MY_MUTEX_INIT_FAST);
+ }
- delete table->triggers;
- if (table->file) // Not true if placeholder
+ ~Table_cache_instance()
{
- (void) closefrm(table);
- tdc_release_share(table->s);
+ mysql_mutex_destroy(&LOCK_table_cache);
+ DBUG_ASSERT(free_tables.is_empty());
+ DBUG_ASSERT(records == 0);
}
- table->alias.free();
- my_free(table);
- DBUG_VOID_RETURN;
-}
+ /**
+ Lock table cache mutex and check contention.
-/**
- Get number of TABLE objects (used and unused) in table cache.
-*/
+ Instance is considered contested if more than 20% of mutex acquisiotions
+ can't be served immediately. Up to 100 000 probes may be performed to avoid
+ instance activation on short sporadic peaks. 100 000 is estimated maximum
+ number of queries one instance can serve in one second.
-uint tc_records(void)
+ These numbers work well on a 2 socket / 20 core / 40 threads Intel Broadwell
+ system, that is expected number of instances is activated within reasonable
+ warmup time. It may have to be adjusted for other systems.
+
+ Only TABLE object acquistion is instrumented. We intentionally avoid this
+ overhead on TABLE object release. All other table cache mutex acquistions
+ are considered out of hot path and are not instrumented either.
+ */
+ void lock_and_check_contention(uint32 n_instances, uint32 instance)
+ {
+ if (mysql_mutex_trylock(&LOCK_table_cache))
+ {
+ mysql_mutex_lock(&LOCK_table_cache);
+ if (++mutex_waits == 20000)
+ {
+ if (n_instances < tc_instances)
+ {
+ if (my_atomic_cas32_weak_explicit((int32*) &tc_active_instances,
+ (int32*) &n_instances,
+ (int32) n_instances + 1,
+ MY_MEMORY_ORDER_RELAXED,
+ MY_MEMORY_ORDER_RELAXED))
+ {
+ sql_print_information("Detected table cache mutex contention at instance %d: "
+ "%d%% waits. Additional table cache instance "
+ "activated. Number of instances after "
+ "activation: %d.",
+ instance + 1,
+ mutex_waits * 100 / (mutex_nowaits + mutex_waits),
+ n_instances + 1);
+ }
+ }
+ else if (!my_atomic_fas32_explicit((int32*) &tc_contention_warning_reported,
+ 1, MY_MEMORY_ORDER_RELAXED))
+ {
+ sql_print_warning("Detected table cache mutex contention at instance %d: "
+ "%d%% waits. Additional table cache instance "
+ "cannot be activated: consider raising "
+ "table_open_cache_instances. Number of active "
+ "instances: %d.",
+ instance + 1,
+ mutex_waits * 100 / (mutex_nowaits + mutex_waits),
+ n_instances);
+ }
+ mutex_waits= 0;
+ mutex_nowaits= 0;
+ }
+ }
+ else if (++mutex_nowaits == 80000)
+ {
+ mutex_waits= 0;
+ mutex_nowaits= 0;
+ }
+ }
+};
+
+
+static Table_cache_instance *tc;
+
+
+static void intern_close_table(TABLE *table)
{
- return my_atomic_load32_explicit(&tc_count, MY_MEMORY_ORDER_RELAXED);
+ delete table->triggers;
+ DBUG_ASSERT(table->file);
+ closefrm(table);
+ tdc_release_share(table->s);
+ my_free(table);
}
/**
- Wait for MDL deadlock detector to complete traversing tdc.all_tables.
-
- Must be called before updating TABLE_SHARE::tdc.all_tables.
+ Get number of TABLE objects (used and unused) in table cache.
*/
-static void tc_wait_for_mdl_deadlock_detector(TDC_element *element)
+uint tc_records(void)
{
- while (element->all_tables_refs)
- mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
+ ulong total= 0;
+ for (ulong i= 0; i < tc_instances; i++)
+ {
+ mysql_mutex_lock(&tc[i].LOCK_table_cache);
+ total+= tc[i].records;
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+ }
+ return total;
}
/**
Remove TABLE object from table cache.
-
- - decrement tc_count
- - remove object from TABLE_SHARE::tdc.all_tables
*/
static void tc_remove_table(TABLE *table)
{
- mysql_mutex_assert_owner(&table->s->tdc->LOCK_table_share);
- tc_wait_for_mdl_deadlock_detector(table->s->tdc);
- my_atomic_add32_explicit(&tc_count, -1, MY_MEMORY_ORDER_RELAXED);
- table->s->tdc->all_tables.remove(table);
+ TDC_element *element= table->s->tdc;
+
+ mysql_mutex_lock(&element->LOCK_table_share);
+ /* Wait for MDL deadlock detector to complete traversing tdc.all_tables. */
+ while (element->all_tables_refs)
+ mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
+ element->all_tables.remove(table);
+ mysql_mutex_unlock(&element->LOCK_table_share);
+
+ intern_close_table(table);
}
static void tc_remove_all_unused_tables(TDC_element *element,
- TDC_element::TABLE_list *purge_tables,
+ Share_free_tables::List *purge_tables,
bool mark_flushed)
{
TABLE *table;
@@ -200,10 +277,18 @@ static void tc_remove_all_unused_tables(TDC_element *element,
*/
if (mark_flushed)
element->flushed= true;
- while ((table= element->free_tables.pop_front()))
+ for (ulong i= 0; i < tc_instances; i++)
{
- tc_remove_table(table);
- purge_tables->push_front(table);
+ mysql_mutex_lock(&tc[i].LOCK_table_cache);
+ while ((table= element->free_tables[i].list.pop_front()))
+ {
+ tc[i].records--;
+ tc[i].free_tables.remove(table);
+ DBUG_ASSERT(element->all_tables_refs == 0);
+ element->all_tables.remove(table);
+ purge_tables->push_front(table);
+ }
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
}
}
@@ -225,7 +310,7 @@ static void tc_remove_all_unused_tables(TDC_element *element,
struct tc_purge_arg
{
- TDC_element::TABLE_list purge_tables;
+ Share_free_tables::List purge_tables;
bool mark_flushed;
};
@@ -252,20 +337,6 @@ void tc_purge(bool mark_flushed)
/**
- Get last element of free_tables.
-*/
-
-static TABLE *tc_free_tables_back(TDC_element *element)
-{
- TDC_element::TABLE_list::Iterator it(element->free_tables);
- TABLE *entry, *last= 0;
- while ((entry= it++))
- last= entry;
- return last;
-}
-
-
-/**
Add new TABLE object to table cache.
@pre TABLE object is used by caller.
@@ -281,79 +352,35 @@ static TABLE *tc_free_tables_back(TDC_element *element)
- free evicted object
*/
-struct tc_add_table_arg
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- ulonglong purge_time;
-};
-
-
-static my_bool tc_add_table_callback(TDC_element *element, tc_add_table_arg *arg)
+void tc_add_table(THD *thd, TABLE *table)
{
- TABLE *table;
+ uint32 i= thd->thread_id % my_atomic_load32_explicit((int32*) &tc_active_instances,
+ MY_MEMORY_ORDER_RELAXED);
+ TABLE *LRU_table= 0;
+ TDC_element *element= table->s->tdc;
+ DBUG_ASSERT(table->in_use == thd);
+ table->instance= i;
mysql_mutex_lock(&element->LOCK_table_share);
- if ((table= tc_free_tables_back(element)) && table->tc_time < arg->purge_time)
- {
- memcpy(arg->key, element->m_key, element->m_key_length);
- arg->key_length= element->m_key_length;
- arg->purge_time= table->tc_time;
- }
+ /* Wait for MDL deadlock detector to complete traversing tdc.all_tables. */
+ while (element->all_tables_refs)
+ mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
+ element->all_tables.push_front(table);
mysql_mutex_unlock(&element->LOCK_table_share);
- return FALSE;
-}
-
-void tc_add_table(THD *thd, TABLE *table)
-{
- bool need_purge;
- DBUG_ASSERT(table->in_use == thd);
- mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
- tc_wait_for_mdl_deadlock_detector(table->s->tdc);
- table->s->tdc->all_tables.push_front(table);
- mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
-
- /* If we have too many TABLE instances around, try to get rid of them */
- need_purge= my_atomic_add32_explicit(&tc_count, 1, MY_MEMORY_ORDER_RELAXED) >=
- (int32) tc_size;
-
- if (need_purge)
+ mysql_mutex_lock(&tc[i].LOCK_table_cache);
+ if (tc[i].records == tc_size && (LRU_table= tc[i].free_tables.pop_front()))
{
- tc_add_table_arg argument;
- argument.purge_time= ULONGLONG_MAX;
- tdc_iterate(thd, (my_hash_walk_action) tc_add_table_callback, &argument);
-
- if (argument.purge_time != ULONGLONG_MAX)
- {
- TDC_element *element= (TDC_element*) lf_hash_search(&tdc_hash,
- thd->tdc_hash_pins,
- argument.key,
- argument.key_length);
- if (element)
- {
- TABLE *entry;
- mysql_mutex_lock(&element->LOCK_table_share);
- lf_hash_search_unpin(thd->tdc_hash_pins);
-
- /*
- It may happen that oldest table was acquired meanwhile. In this case
- just go ahead, number of objects in table cache will normalize
- eventually.
- */
- if ((entry= tc_free_tables_back(element)) &&
- entry->tc_time == argument.purge_time)
- {
- element->free_tables.remove(entry);
- tc_remove_table(entry);
- mysql_mutex_unlock(&element->LOCK_table_share);
- intern_close_table(entry);
- }
- else
- mysql_mutex_unlock(&element->LOCK_table_share);
- }
- }
+ LRU_table->s->tdc->free_tables[i].list.remove(LRU_table);
+ /* Needed if MDL deadlock detector chimes in before tc_remove_table() */
+ LRU_table->in_use= thd;
}
+ else
+ tc[i].records++;
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+
+ if (LRU_table)
+ tc_remove_table(LRU_table);
}
@@ -369,10 +396,14 @@ void tc_add_table(THD *thd, TABLE *table)
static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
{
+ uint32 n_instances=
+ my_atomic_load32_explicit((int32*) &tc_active_instances,
+ MY_MEMORY_ORDER_RELAXED);
+ uint32 i= thd->thread_id % n_instances;
TABLE *table;
- mysql_mutex_lock(&element->LOCK_table_share);
- table= element->free_tables.pop_front();
+ tc[i].lock_and_check_contention(n_instances, i);
+ table= element->free_tables[i].list.pop_front();
if (table)
{
DBUG_ASSERT(!table->in_use);
@@ -381,8 +412,9 @@ static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
DBUG_ASSERT(table->db_stat && table->file);
/* The children must be detached from the table. */
DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+ tc[i].free_tables.remove(table);
}
- mysql_mutex_unlock(&element->LOCK_table_share);
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
return table;
}
@@ -413,40 +445,27 @@ static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
@retval false object released
*/
-bool tc_release_table(TABLE *table)
+void tc_release_table(TABLE *table)
{
+ uint32 i= table->instance;
DBUG_ASSERT(table->in_use);
DBUG_ASSERT(table->file);
- if (table->needs_reopen() || tc_records() > tc_size)
+ mysql_mutex_lock(&tc[i].LOCK_table_cache);
+ if (table->needs_reopen() || table->s->tdc->flushed ||
+ tc[i].records > tc_size)
{
- mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
- goto purge;
+ tc[i].records--;
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
+ tc_remove_table(table);
+ }
+ else
+ {
+ table->in_use= 0;
+ table->s->tdc->free_tables[i].list.push_front(table);
+ tc[i].free_tables.push_back(table);
+ mysql_mutex_unlock(&tc[i].LOCK_table_cache);
}
-
- table->tc_time= my_interval_timer();
-
- mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
- if (table->s->tdc->flushed)
- goto purge;
- /*
- in_use doesn't really need mutex protection, but must be reset after
- checking tdc.flushed and before this table appears in free_tables.
- Resetting in_use is needed only for print_cached_tables() and
- list_open_tables().
- */
- table->in_use= 0;
- /* Add table to the list of unused TABLE objects for this share. */
- table->s->tdc->free_tables.push_front(table);
- mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
- return false;
-
-purge:
- tc_remove_table(table);
- mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
- table->in_use= 0;
- intern_close_table(table);
- return true;
}
@@ -456,7 +475,10 @@ static void tdc_assert_clean_share(TDC_element *element)
DBUG_ASSERT(element->ref_count == 0);
DBUG_ASSERT(element->m_flush_tickets.is_empty());
DBUG_ASSERT(element->all_tables.is_empty());
- DBUG_ASSERT(element->free_tables.is_empty());
+#ifndef DBUG_OFF
+ for (ulong i= 0; i < tc_instances; i++)
+ DBUG_ASSERT(element->free_tables[i].list.is_empty());
+#endif
DBUG_ASSERT(element->all_tables_refs == 0);
DBUG_ASSERT(element->next == 0);
DBUG_ASSERT(element->prev == 0);
@@ -527,7 +549,8 @@ static void lf_alloc_constructor(uchar *arg)
mysql_cond_init(key_TABLE_SHARE_COND_release, &element->COND_release, 0);
element->m_flush_tickets.empty();
element->all_tables.empty();
- element->free_tables.empty();
+ for (ulong i= 0; i < tc_instances; i++)
+ element->free_tables[i].list.empty();
element->all_tables_refs= 0;
element->share= 0;
element->ref_count= 0;
@@ -573,23 +596,29 @@ static uchar *tdc_hash_key(const TDC_element *element, size_t *length,
Initialize table definition cache.
*/
-void tdc_init(void)
+bool tdc_init(void)
{
DBUG_ENTER("tdc_init");
#ifdef HAVE_PSI_INTERFACE
- init_tc_psi_keys();
+ mysql_mutex_register("sql", all_tc_mutexes, array_elements(all_tc_mutexes));
+ mysql_cond_register("sql", all_tc_conds, array_elements(all_tc_conds));
#endif
+ /* Extra instance is allocated to avoid false sharing */
+ if (!(tc= new Table_cache_instance[tc_instances + 1]))
+ DBUG_RETURN(true);
tdc_inited= true;
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
MY_MUTEX_INIT_FAST);
tdc_version= 1L; /* Increments on each reload */
- lf_hash_init(&tdc_hash, sizeof(TDC_element), LF_HASH_UNIQUE, 0, 0,
+ lf_hash_init(&tdc_hash, sizeof(TDC_element) +
+ sizeof(Share_free_tables) * (tc_instances - 1),
+ LF_HASH_UNIQUE, 0, 0,
(my_hash_get_key) tdc_hash_key,
&my_charset_bin);
tdc_hash.alloc.constructor= lf_alloc_constructor;
tdc_hash.alloc.destructor= lf_alloc_destructor;
tdc_hash.initializer= (lf_hash_initializer) tdc_hash_initializer;
- DBUG_VOID_RETURN;
+ DBUG_RETURN(false);
}
@@ -631,6 +660,7 @@ void tdc_deinit(void)
tdc_inited= false;
lf_hash_destroy(&tdc_hash);
mysql_mutex_destroy(&LOCK_unused_shares);
+ delete [] tc;
}
DBUG_VOID_RETURN;
}
@@ -1038,7 +1068,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name,
bool kill_delayed_threads)
{
- TDC_element::TABLE_list purge_tables;
+ Share_free_tables::List purge_tables;
TABLE *table;
TDC_element *element;
uint my_refs= 1;
diff --git a/sql/table_cache.h b/sql/table_cache.h
index c05baae1f15..cac69296cc2 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -18,6 +18,15 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+struct Share_free_tables
+{
+ typedef I_P_List <TABLE, TABLE_share> List;
+ List list;
+ /** Avoid false sharing between instances */
+ char pad[CPU_LEVEL1_DCACHE_LINESIZE];
+};
+
+
struct TDC_element
{
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
@@ -26,10 +35,8 @@ struct TDC_element
bool flushed;
TABLE_SHARE *share;
- typedef I_P_List <TABLE, TABLE_share> TABLE_list;
/**
- Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
- all_tables_refs.
+ Protects ref_count, m_flush_tickets, all_tables, flushed, all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
@@ -45,7 +52,9 @@ struct TDC_element
for this share.
*/
All_share_tables_list all_tables;
- TABLE_list free_tables;
+ /** Avoid false sharing between TDC_element and free_tables */
+ char pad[CPU_LEVEL1_DCACHE_LINESIZE];
+ Share_free_tables free_tables[1];
};
@@ -59,8 +68,9 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
+extern uint32 tc_instances;
-extern void tdc_init(void);
+extern bool tdc_init(void);
extern void tdc_start_shutdown(void);
extern void tdc_deinit(void);
extern ulong tdc_records(void);
@@ -86,7 +96,7 @@ extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
extern uint tc_records(void);
extern void tc_purge(bool mark_flushed= false);
extern void tc_add_table(THD *thd, TABLE *table);
-extern bool tc_release_table(TABLE *table);
+extern void tc_release_table(TABLE *table);
/**
Create a table cache key for non-temporary table.
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index a7996efc382..d6c343dc04e 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -164,9 +164,8 @@ THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
if (!setup_connection_thread_globals(thd))
{
- if (!login_connection(thd))
+ if (!thd_prepare_connection(thd))
{
- prepare_new_connection_state(thd);
/*
Check if THD is ok, as prepare_new_connection_state()
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 8b188709ce6..9bacfb81bba 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -25,6 +25,44 @@
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_acl.h"
+
+#ifndef EMBEDDED_LIBRARY
+/**
+ Helper: Tell tracker (if any) that transaction ended.
+*/
+static void trans_track_end_trx(THD *thd)
+{
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ {
+ ((Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER))->end_trx(thd);
+ }
+}
+#else
+#define trans_track_end_trx(A) do{}while(0)
+#endif //EMBEDDED_LIBRARY
+
+
+/**
+ Helper: transaction ended, SET TRANSACTION one-shot variables
+ revert to session values. Let the transaction state tracker know.
+*/
+void trans_reset_one_shot_chistics(THD *thd)
+{
+#ifndef EMBEDDED_LIBRARY
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ {
+ Transaction_state_tracker *tst= (Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
+
+ tst->set_read_flags(thd, TX_READ_INHERIT);
+ tst->set_isol_level(thd, TX_ISOL_INHERIT);
+ }
+#endif //EMBEDDED_LIBRARY
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
+}
+
/* Conditions under which the transaction state must not change. */
static bool trans_check(THD *thd)
{
@@ -125,11 +163,20 @@ static bool xa_trans_force_rollback(THD *thd)
bool trans_begin(THD *thd, uint flags)
{
int res= FALSE;
+#ifndef EMBEDDED_LIBRARY
+ Transaction_state_tracker *tst= NULL;
+#endif //EMBEDDED_LIBRARY
DBUG_ENTER("trans_begin");
if (trans_check(thd))
DBUG_RETURN(TRUE);
+#ifndef EMBEDDED_LIBRARY
+ if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
+ tst= (Transaction_state_tracker *)
+ thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
+#endif //EMBEDDED_LIBRARY
+
thd->locked_tables_list.unlock_locked_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -172,7 +219,13 @@ bool trans_begin(THD *thd, uint flags)
DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) &&
(flags & MYSQL_START_TRANS_OPT_READ_WRITE)));
if (flags & MYSQL_START_TRANS_OPT_READ_ONLY)
+ {
thd->tx_read_only= true;
+#ifndef EMBEDDED_LIBRARY
+ if (tst)
+ tst->set_read_flags(thd, TX_READ_ONLY);
+#endif //EMBEDDED_LIBRARY
+ }
else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE)
{
/*
@@ -189,6 +242,14 @@ bool trans_begin(THD *thd, uint flags)
DBUG_RETURN(true);
}
thd->tx_read_only= false;
+ /*
+ This flags that tx_read_only was set explicitly, rather than
+ just from the session's default.
+ */
+#ifndef EMBEDDED_LIBRARY
+ if (tst)
+ tst->set_read_flags(thd, TX_READ_WRITE);
+#endif //EMBEDDED_LIBRARY
}
#ifdef WITH_WSREP
@@ -203,9 +264,20 @@ bool trans_begin(THD *thd, uint flags)
thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY;
DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
+#ifndef EMBEDDED_LIBRARY
+ if (tst)
+ tst->add_trx_state(thd, TX_EXPLICIT);
+#endif //EMBEDDED_LIBRARY
+
/* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */
if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
+ {
+#ifndef EMBEDDED_LIBRARY
+ if (tst)
+ tst->add_trx_state(thd, TX_WITH_SNAPSHOT);
+#endif //EMBEDDED_LIBRARY
res= ha_start_consistent_snapshot(thd);
+ }
DBUG_RETURN(MY_TEST(res));
}
@@ -255,6 +327,8 @@ bool trans_commit(THD *thd)
thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
thd->lex->start_transaction_opt= 0;
+ trans_track_end_trx(thd);
+
DBUG_RETURN(MY_TEST(res));
}
@@ -308,8 +382,9 @@ bool trans_commit_implicit(THD *thd)
@@session.completion_type since it's documented
to not have any effect on implicit commit.
*/
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
+
+ trans_track_end_trx(thd);
DBUG_RETURN(res);
}
@@ -349,6 +424,8 @@ bool trans_rollback(THD *thd)
thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
thd->lex->start_transaction_opt= 0;
+ trans_track_end_trx(thd);
+
DBUG_RETURN(MY_TEST(res));
}
@@ -396,6 +473,8 @@ bool trans_rollback_implicit(THD *thd)
/* Rollback should clear transaction_rollback_request flag. */
DBUG_ASSERT(! thd->transaction_rollback_request);
+ trans_track_end_trx(thd);
+
DBUG_RETURN(MY_TEST(res));
}
@@ -434,8 +513,7 @@ bool trans_commit_stmt(THD *thd)
res= ha_commit_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
{
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
+ trans_reset_one_shot_chistics(thd);
if (WSREP_ON)
wsrep_post_commit(thd, FALSE);
}
@@ -487,10 +565,7 @@ bool trans_rollback_stmt(THD *thd)
wsrep_register_hton(thd, FALSE);
ha_rollback_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
- {
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- thd->tx_read_only= thd->variables.tx_read_only;
- }
+ trans_reset_one_shot_chistics(thd);
}
(void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
@@ -912,6 +987,8 @@ bool trans_xa_commit(THD *thd)
xid_cache_delete(thd, &thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
+ trans_track_end_trx(thd);
+
DBUG_RETURN(res);
}
@@ -968,5 +1045,7 @@ bool trans_xa_rollback(THD *thd)
xid_cache_delete(thd, &thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
+ trans_track_end_trx(thd);
+
DBUG_RETURN(res);
}
diff --git a/sql/transaction.h b/sql/transaction.h
index 54b25f1de2a..90de11aabe3 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -44,4 +44,6 @@ bool trans_xa_prepare(THD *thd);
bool trans_xa_commit(THD *thd);
bool trans_xa_rollback(THD *thd);
+void trans_reset_one_shot_chistics(THD *thd);
+
#endif /* TRANSACTION_H */
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 19d03d23cc7..d3a9b832aaf 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -634,7 +634,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
if (add_expr_length(thd, &field->vcol_info, &expression_length))
DBUG_RETURN(1);
- if (field->has_default_expression())
+ if (field->default_value && field->default_value->expr_str.length)
if (add_expr_length(thd, &field->default_value, &expression_length))
DBUG_RETURN(1);
if (add_expr_length(thd, &field->check_constraint, &expression_length))
@@ -735,7 +735,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
n_length+int_length+com_length+expression_length > 65535L ||
int_count > 255)
{
- my_message(ER_TOO_MANY_FIELDS, ER_THD(thd, ER_TOO_MANY_FIELDS), MYF(0));
+ my_message(ER_TOO_MANY_FIELDS, "Table definition is too large", MYF(0));
DBUG_RETURN(1);
}
@@ -983,7 +983,7 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
if (field->vcol_info)
pack_expression(&buff, field->vcol_info, field_nr,
field->vcol_info->stored_in_db ? 1 : 0);
- if (field->has_default_expression())
+ if (field->default_value && field->default_value->expr_str.length)
pack_expression(&buff, field->default_value, field_nr, 2);
if (field->check_constraint)
pack_expression(&buff, field->check_constraint, field_nr, 3);
@@ -1039,7 +1039,10 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
field->sql_type,
field->charset,
field->geom_type, field->srid,
- field->unireg_check,
+ field->unireg_check == Field::TIMESTAMP_DNUN_FIELD
+ ? Field::TIMESTAMP_UN_FIELD
+ : field->unireg_check == Field::TIMESTAMP_DN_FIELD
+ ? Field::NONE : field->unireg_check,
field->save_interval ? field->save_interval :
field->interval,
field->field_name);
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index b4557cfe1b7..059aabe7b46 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -39,15 +39,9 @@ static Log_event* wsrep_read_log_event(
const char *error= 0;
Log_event *res= 0;
- if (data_len > wsrep_max_ws_size)
- {
- error = "Event too big";
- goto err;
- }
-
- res= Log_event::read_log_event(buf, data_len, &error, description_event, true);
+ res= Log_event::read_log_event(buf, data_len, &error, description_event,
+ true);
-err:
if (!res)
{
DBUG_ASSERT(error != 0);
diff --git a/sql/wsrep_binlog.h b/sql/wsrep_binlog.h
index 1e820529211..864813d5c98 100644
--- a/sql/wsrep_binlog.h
+++ b/sql/wsrep_binlog.h
@@ -19,7 +19,7 @@
#include "sql_class.h" // THD, IO_CACHE
#define HEAP_PAGE_SIZE 65536 /* 64K */
-#define WSREP_MAX_WS_SIZE (0xFFFFFFFFUL - HEAP_PAGE_SIZE)
+#define WSREP_MAX_WS_SIZE 2147483647 /* 2GB */
/*
Write the contents of a cache to a memory buffer.
diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc
index 818ef843cb5..bc77c434525 100644
--- a/sql/wsrep_check_opts.cc
+++ b/sql/wsrep_check_opts.cc
@@ -18,8 +18,6 @@
#include "sys_vars_shared.h"
#include "wsrep.h"
#include "wsrep_sst.h"
-//#include <sql_class.h>
-//#include "wsrep_mysqld.h"
extern char *my_bind_addr_str;
diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
index fa34a5bbc55..2a93484002e 100644
--- a/sql/wsrep_hton.cc
+++ b/sql/wsrep_hton.cc
@@ -34,11 +34,14 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd);
*/
void wsrep_cleanup_transaction(THD *thd)
{
+ if (!WSREP(thd)) return;
+
if (wsrep_emulate_bin_log) thd_binlog_trx_reset(thd);
thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID;
thd->wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
thd->wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
thd->wsrep_exec_mode= LOCAL_STATE;
+ thd->wsrep_affected_rows= 0;
return;
}
@@ -109,13 +112,7 @@ void wsrep_register_hton(THD* thd, bool all)
*/
void wsrep_post_commit(THD* thd, bool all)
{
- /*
- TODO: It can perhaps be fixed in a more elegant fashion by turning off
- wsrep_emulate_binlog if wsrep_on=0 on server start.
- https://github.com/codership/mysql-wsrep/issues/112
- */
- if (!WSREP_ON)
- return;
+ if (!WSREP(thd)) return;
switch (thd->wsrep_exec_mode)
{
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index e3b503a82fb..a8253ddf3ad 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -52,49 +52,64 @@ rpl_sidno wsrep_sidno= -1;
my_bool wsrep_preordered_opt= FALSE;
/*
- * Begin configuration options and their default values
+ * Begin configuration options
*/
extern my_bool plugins_are_initialized;
extern uint kill_cached_threads;
extern mysql_cond_t COND_thread_cache;
-const char* wsrep_data_home_dir = NULL;
-const char* wsrep_dbug_option = "";
-
-long wsrep_slave_threads = 1; // # of slave action appliers wanted
-int wsrep_slave_count_change = 0; // # of appliers to stop or start
-my_bool wsrep_debug = 0; // enable debug level logging
-my_bool wsrep_convert_LOCK_to_trx = 1; // convert locking sessions to trx
-ulong wsrep_retry_autocommit = 5; // retry aborted autocommit trx
-my_bool wsrep_auto_increment_control = 1; // control auto increment variables
-my_bool wsrep_drupal_282555_workaround = 1; // retry autoinc insert after dupkey
-my_bool wsrep_incremental_data_collection = 0; // incremental data collection
-ulong wsrep_max_ws_size = 1073741824UL;//max ws (RBR buffer) size
-ulong wsrep_max_ws_rows = 65536; // max number of rows in ws
-int wsrep_to_isolation = 0; // # of active TO isolation threads
-my_bool wsrep_certify_nonPK = 1; // certify, even when no primary key
-long wsrep_max_protocol_version = 3; // maximum protocol version to use
-ulong wsrep_forced_binlog_format = BINLOG_FORMAT_UNSPEC;
-my_bool wsrep_recovery = 0; // recovery
-my_bool wsrep_replicate_myisam = 0; // enable myisam replication
-my_bool wsrep_log_conflicts = 0;
-ulong wsrep_mysql_replication_bundle = 0;
-my_bool wsrep_desync = 0; // desynchronize the node from the
- // cluster
-my_bool wsrep_load_data_splitting = 1; // commit load data every 10K intervals
-my_bool wsrep_restart_slave = 0; // should mysql slave thread be
- // restarted, if node joins back
-my_bool wsrep_restart_slave_activated = 0; // node has dropped, and slave
- // restart will be needed
-my_bool wsrep_slave_UK_checks = 0; // slave thread does UK checks
-my_bool wsrep_slave_FK_checks = 0; // slave thread does FK checks
-bool wsrep_new_cluster = false; // Bootstrap the cluster ?
-
-// Use wsrep_gtid_domain_id for galera transactions?
-bool wsrep_gtid_mode = 0;
-// gtid_domain_id for galera transactions.
-uint32 wsrep_gtid_domain_id = 0;
+/* System variables. */
+const char *wsrep_provider;
+const char *wsrep_provider_options;
+const char *wsrep_cluster_address;
+const char *wsrep_cluster_name;
+const char *wsrep_node_name;
+const char *wsrep_node_address;
+const char *wsrep_node_incoming_address;
+const char *wsrep_start_position;
+const char *wsrep_data_home_dir;
+const char *wsrep_dbug_option;
+const char *wsrep_notify_cmd;
+const char *wsrep_sst_method;
+const char *wsrep_sst_receive_address;
+const char *wsrep_sst_donor;
+const char *wsrep_sst_auth;
+my_bool wsrep_debug; // Enable debug level logging
+my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to trx
+my_bool wsrep_auto_increment_control; // Control auto increment variables
+my_bool wsrep_drupal_282555_workaround; // Retry autoinc insert after dupkey
+my_bool wsrep_certify_nonPK; // Certify, even when no primary key
+my_bool wsrep_recovery; // Recovery
+my_bool wsrep_replicate_myisam; // Enable MyISAM replication
+my_bool wsrep_log_conflicts;
+my_bool wsrep_load_data_splitting; // Commit load data every 10K intervals
+my_bool wsrep_slave_UK_checks; // Slave thread does UK checks
+my_bool wsrep_slave_FK_checks; // Slave thread does FK checks
+my_bool wsrep_sst_donor_rejects_queries;
+my_bool wsrep_restart_slave; // Should mysql slave thread be
+ // restarted, when node joins back?
+my_bool wsrep_desync; // De(re)synchronize the node from the
+ // cluster
+long wsrep_slave_threads; // No. of slave appliers threads
+ulong wsrep_retry_autocommit; // Retry aborted autocommit trx
+ulong wsrep_max_ws_size; // Max allowed ws (RBR buffer) size
+ulong wsrep_max_ws_rows; // Max number of rows in ws
+ulong wsrep_forced_binlog_format;
+ulong wsrep_mysql_replication_bundle;
+bool wsrep_gtid_mode; // Use wsrep_gtid_domain_id
+ // for galera transactions?
+uint32 wsrep_gtid_domain_id; // gtid_domain_id for galera
+ // transactions
+
+/* Other configuration variables and their default values. */
+my_bool wsrep_incremental_data_collection= 0; // Incremental data collection
+my_bool wsrep_restart_slave_activated= 0; // Node has dropped, and slave
+ // restart will be needed
+bool wsrep_new_cluster= false; // Bootstrap the cluster?
+int wsrep_slave_count_change= 0; // No. of appliers to stop/start
+int wsrep_to_isolation= 0; // No. of active TO isolation threads
+long wsrep_max_protocol_version= 3; // Maximum protocol version to use
/*
* End configuration options
@@ -169,7 +184,7 @@ static PSI_file_info wsrep_files[]=
my_bool wsrep_inited = 0; // initialized ?
-static const wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
+static wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
static char cluster_uuid_str[40]= { 0, };
static const char* cluster_status_str[WSREP_VIEW_MAX] =
{
@@ -562,7 +577,7 @@ int wsrep_init()
return 1;
}
- wsrep_sst_auth_init(wsrep_sst_auth);
+ wsrep_sst_auth_init();
wsrep_ready_set(FALSE);
assert(wsrep_provider);
@@ -576,8 +591,7 @@ int wsrep_init()
WSREP_ERROR("wsrep_load(%s) failed: %s (%d). Reverting to no provider.",
wsrep_provider, strerror(rcode), rcode);
strcpy((char*)wsrep_provider, WSREP_NONE); // damn it's a dirty hack
- (void) wsrep_init();
- return rcode;
+ return wsrep_init();
}
else /* this is for recursive call above */
{
@@ -789,8 +803,11 @@ void wsrep_init_startup (bool first)
wsrep_debug, wsrep_convert_LOCK_to_trx,
(wsrep_on_fun)wsrep_on);
+ /* Skip replication start if dummy wsrep provider is loaded */
+ if (!strcmp(wsrep_provider, WSREP_NONE)) return;
+
/* Skip replication start if no cluster address */
- if (!wsrep_cluster_address || strlen(wsrep_cluster_address) == 0) return;
+ if (!wsrep_cluster_address || wsrep_cluster_address[0] == 0) return;
if (first) wsrep_sst_grab(); // do it so we can wait for SST below
@@ -903,7 +920,7 @@ bool wsrep_start_replication()
return true;
}
- if (!wsrep_cluster_address || strlen(wsrep_cluster_address)== 0)
+ if (!wsrep_cluster_address || wsrep_cluster_address[0]== 0)
{
// if provider is non-trivial, but no address is specified, wait for address
wsrep_ready_set(FALSE);
@@ -1404,6 +1421,12 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
case SQLCOM_ALTER_EVENT:
buf_err= wsrep_alter_event_query(thd, &buf, &buf_len);
break;
+ case SQLCOM_CREATE_ROLE:
+ if (sp_process_definer(thd))
+ {
+ WSREP_WARN("Failed to set CREATE ROLE definer for TOI.");
+ }
+ /* fallthrough */
default:
buf_err= wsrep_to_buf_helper(thd, thd->query(), thd->query_length(), &buf,
&buf_len);
@@ -1478,19 +1501,15 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
thd->wsrep_exec_mode, thd->query() );
- if (!wsrep_desync)
+ ret = wsrep->desync(wsrep);
+ if (ret != WSREP_OK)
{
- ret = wsrep->desync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
- ret, (thd->db ? thd->db : "(null)"), thd->query());
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- return(ret);
- }
+ WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return(ret);
}
- else
- WSREP_DEBUG("RSU desync skipped: %d", wsrep_desync);
+
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying++;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
@@ -1505,15 +1524,13 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
wsrep_replaying--;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
- if (!wsrep_desync)
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
{
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("resync failed %d for schema: %s, query: %s",
- ret, (thd->db ? thd->db : "(null)"), thd->query());
- }
+ WSREP_WARN("resync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
}
+
my_error(ER_LOCK_DEADLOCK, MYF(0));
return(1);
}
@@ -1549,18 +1566,15 @@ static void wsrep_RSU_end(THD *thd)
(thd->db ? thd->db : "(null)"),
thd->query());
}
- if (!wsrep_desync)
+
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
{
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"), thd->query());
- return;
- }
+ WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"), thd->query());
+ return;
}
- else
- WSREP_DEBUG("RSU resync skipped: %d", wsrep_desync);
+
thd->variables.wsrep_on = 1;
}
@@ -1684,22 +1698,41 @@ void wsrep_to_isolation_end(THD *thd)
@retval FALSE Lock request cannot be granted
*/
-bool
-wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
- MDL_ticket *ticket,
- const MDL_key *key
-) {
+bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
+ MDL_ticket *ticket,
+ const MDL_key *key)
+{
/* Fallback to the non-wsrep behaviour */
if (!WSREP_ON) return FALSE;
- THD *request_thd = requestor_ctx->get_thd();
- THD *granted_thd = ticket->get_ctx()->get_thd();
- bool ret = FALSE;
+ THD *request_thd= requestor_ctx->get_thd();
+ THD *granted_thd= ticket->get_ctx()->get_thd();
+ bool ret= false;
const char* schema= key->db_name();
int schema_len= key->db_name_length();
mysql_mutex_lock(&request_thd->LOCK_wsrep_thd);
+
+ /*
+ We consider granting MDL exceptions only for appliers (BF THD) and ones
+ executing under TOI mode.
+
+ Rules:
+ 1. If granted/owner THD is also an applier (BF THD) or one executing
+ under TOI mode, then we grant the requested lock to the requester
+ THD.
+ @return true
+
+ 2. If granted/owner THD is executing a FLUSH command or already has an
+ explicit lock, then do not grant the requested lock to the requester
+ THD and it has to wait.
+ @return false
+
+ 3. In all other cases the granted/owner THD is aborted and the requested
+ lock is not granted to the requester THD, thus it has to wait.
+ @return false
+ */
if (request_thd->wsrep_exec_mode == TOTAL_ORDER ||
request_thd->wsrep_exec_mode == REPL_RECV)
{
@@ -1716,7 +1749,7 @@ wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
request_thd, granted_thd);
ticket->wsrep_report(true);
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- ret = TRUE;
+ ret= true;
}
else if (granted_thd->lex->sql_command == SQLCOM_FLUSH ||
granted_thd->mdl_context.has_explicit_locks())
@@ -1724,38 +1757,39 @@ wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
WSREP_DEBUG("BF thread waiting for FLUSH");
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- ret = FALSE;
- }
- else if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE)
- {
- WSREP_DEBUG("DROP caused BF abort");
- ticket->wsrep_report(wsrep_debug);
- mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
- ret = FALSE;
- }
- else if (granted_thd->wsrep_query_state == QUERY_COMMITTING)
- {
- WSREP_DEBUG("MDL granted, but committing thd abort scheduled");
- ticket->wsrep_report(wsrep_debug);
- mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
- ret = FALSE;
+ ret= false;
}
else
{
- WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len,
- request_thd, granted_thd);
- ticket->wsrep_report(wsrep_debug);
+ /* Print some debug information. */
+ if (wsrep_debug)
+ {
+ if ((request_thd->lex->sql_command == SQLCOM_DROP_TABLE))
+ {
+ WSREP_DEBUG("DROP caused BF abort");
+ }
+ else if ((granted_thd->wsrep_query_state == QUERY_COMMITTING))
+ {
+ WSREP_DEBUG("MDL granted, but committing thd abort scheduled");
+ }
+ else
+ {
+ WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len,
+ request_thd, granted_thd);
+ }
+ ticket->wsrep_report(true);
+ }
+
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
- ret = FALSE;
+ wsrep_abort_thd((void *) request_thd, (void *) granted_thd, 1);
+ ret= false;
}
}
else
{
mysql_mutex_unlock(&request_thd->LOCK_wsrep_thd);
}
+
return ret;
}
@@ -2620,3 +2654,13 @@ void wsrep_aborting_thd_enqueue(THD *thd)
aborting->next = wsrep_aborting_thd;
wsrep_aborting_thd = aborting;
}
+
+bool wsrep_node_is_donor()
+{
+ return (WSREP_ON) ? (wsrep_config_state->get_status() == 2) : false;
+}
+
+bool wsrep_node_is_synced()
+{
+ return (WSREP_ON) ? (wsrep_config_state->get_status() == 4) : false;
+}
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index ea81384da75..04ccc1a7e45 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -50,6 +50,7 @@ struct wsrep_thd_shadow {
ulong tx_isolation;
char *db;
size_t db_length;
+ my_hrtime_t user_time;
};
// Global wsrep parameters
@@ -168,9 +169,14 @@ extern void wsrep_prepend_PATH (const char* path);
/* Other global variables */
extern wsrep_seqno_t wsrep_locked_seqno;
-#define WSREP_ON \
+#define WSREP_ON \
(global_system_variables.wsrep_on)
+#define WSREP_ON_NEW \
+ ((global_system_variables.wsrep_on) && \
+ wsrep_provider && \
+ strcmp(wsrep_provider, WSREP_NONE))
+
#define WSREP(thd) \
(WSREP_ON && wsrep && (thd && thd->variables.wsrep_on))
@@ -307,6 +313,8 @@ void wsrep_replay_transaction(THD *thd);
bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
TABLE_LIST* src_table,
HA_CREATE_INFO *create_info);
+bool wsrep_node_is_donor();
+bool wsrep_node_is_synced();
#else /* WITH_WSREP */
diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc
index e7d30d5a9c1..d1beb9c38d6 100644
--- a/sql/wsrep_notify.cc
+++ b/sql/wsrep_notify.cc
@@ -17,7 +17,6 @@
#include "wsrep_priv.h"
#include "wsrep_utils.h"
-const char* wsrep_notify_cmd="";
static const char* _status_str(wsrep_member_status_t status)
{
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index 6d0cfc1c43a..50f54fddc95 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -34,14 +34,8 @@ char wsrep_defaults_file[FN_REFLEN * 2 + 10 +
sizeof(WSREP_SST_OPT_CONF) +
sizeof(WSREP_SST_OPT_EXTRA_CONF)] = {0};
-const char* wsrep_sst_method = WSREP_SST_DEFAULT;
-const char* wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO;
-const char* wsrep_sst_donor = "";
- char* wsrep_sst_auth = NULL;
-
// container for real auth string
static const char* sst_auth_real = NULL;
-my_bool wsrep_sst_donor_rejects_queries = FALSE;
bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
{
@@ -175,10 +169,9 @@ bool wsrep_sst_auth_update (sys_var *self, THD* thd, enum_var_type type)
return sst_auth_real_set (wsrep_sst_auth);
}
-void wsrep_sst_auth_init (const char* value)
+void wsrep_sst_auth_init ()
{
- if (wsrep_sst_auth == value) wsrep_sst_auth = NULL;
- if (value) sst_auth_real_set (value);
+ sst_auth_real_set(wsrep_sst_auth);
}
bool wsrep_sst_donor_check (sys_var *self, THD* thd, set_var* var)
@@ -535,7 +528,7 @@ static void* sst_joiner_thread (void* a)
} else {
// Scan state ID first followed by wsrep_gtid_domain_id.
char uuid[512];
- long int domain_id;
+ unsigned long int domain_id;
size_t len= pos - out + 1;
if (len > sizeof(uuid)) goto err; // safety check
@@ -549,11 +542,11 @@ static void* sst_joiner_thread (void* a)
else if (wsrep_gtid_mode)
{
errno= 0; /* Reset the errno */
- domain_id= strtol(pos + 1, NULL, 10);
+ domain_id= strtoul(pos + 1, NULL, 10);
err= errno;
/* Check if we received a valid gtid_domain_id. */
- if (err == EINVAL || err == ERANGE || domain_id < 0x0 || domain_id > 0xFFFF)
+ if (err == EINVAL || err == ERANGE)
{
WSREP_ERROR("Failed to get donor wsrep_gtid_domain_id.");
err= EINVAL;
@@ -1178,6 +1171,16 @@ wait_signal:
if (!err)
{
sst_disallow_writes (thd.ptr, true);
+ /*
+ Lets also keep statements that modify binary logs (like RESET LOGS,
+ RESET MASTER) from proceeding until the files have been transferred
+ to the joiner node.
+ */
+ if (mysql_bin_log.is_open())
+ {
+ mysql_mutex_lock(mysql_bin_log.get_log_lock());
+ }
+
locked= true;
goto wait_signal;
}
@@ -1186,6 +1189,11 @@ wait_signal:
{
if (locked)
{
+ if (mysql_bin_log.is_open())
+ {
+ mysql_mutex_assert_owner(mysql_bin_log.get_log_lock());
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock());
+ }
sst_disallow_writes (thd.ptr, false);
thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
locked= false;
@@ -1218,6 +1226,11 @@ wait_signal:
if (locked) // don't forget to unlock server before return
{
+ if (mysql_bin_log.is_open())
+ {
+ mysql_mutex_assert_owner(mysql_bin_log.get_log_lock());
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock());
+ }
sst_disallow_writes (thd.ptr, false);
thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
}
diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h
index 512da060599..460046bc4ad 100644
--- a/sql/wsrep_sst.h
+++ b/sql/wsrep_sst.h
@@ -55,8 +55,8 @@
extern const char* wsrep_sst_method;
extern const char* wsrep_sst_receive_address;
extern const char* wsrep_sst_donor;
-extern char* wsrep_sst_auth;
-extern my_bool wsrep_sst_donor_rejects_queries;
+extern const char* wsrep_sst_auth;
+extern my_bool wsrep_sst_donor_rejects_queries;
/*! Synchronizes applier thread start with init thread */
extern void wsrep_sst_grab();
@@ -64,6 +64,7 @@ extern void wsrep_sst_grab();
extern bool wsrep_sst_wait();
/*! Signals wsrep that initialization is complete, writesets can be applied */
extern bool wsrep_sst_continue();
+extern void wsrep_sst_auth_init();
extern void wsrep_sst_auth_free();
extern void wsrep_SE_init_grab(); /*! grab init critical section */
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 36768a37973..a810a5a44ae 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -143,6 +143,9 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
shadow->wsrep_exec_mode = thd->wsrep_exec_mode;
shadow->vio = thd->net.vio;
+ // Disable general logging on applier threads
+ thd->variables.option_bits |= OPTION_LOG_OFF;
+ // Enable binlogging if opt_log_slave_updates is set
if (opt_log_slave_updates)
thd->variables.option_bits|= OPTION_BIN_LOG;
else
@@ -164,6 +167,7 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
shadow->db = thd->db;
shadow->db_length = thd->db_length;
+ shadow->user_time = thd->user_time;
thd->reset_db(NULL, 0);
}
@@ -174,6 +178,7 @@ static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
thd->wsrep_exec_mode = shadow->wsrep_exec_mode;
thd->net.vio = shadow->vio;
thd->variables.tx_isolation = shadow->tx_isolation;
+ thd->user_time = shadow->user_time;
thd->reset_db(shadow->db, shadow->db_length);
delete thd->system_thread_info.rpl_sql_info;
@@ -187,6 +192,7 @@ static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
void wsrep_replay_transaction(THD *thd)
{
+ DBUG_ENTER("wsrep_replay_transaction");
/* checking if BF trx must be replayed */
if (thd->wsrep_conflict_state== MUST_REPLAY) {
DBUG_ASSERT(wsrep_thd_trx_seqno(thd));
@@ -195,6 +201,13 @@ void wsrep_replay_transaction(THD *thd)
{
WSREP_ERROR("replay issue, thd has reported status already");
}
+
+ /*
+ PS reprepare observer should have been removed already.
+ open_table() will fail if we have dangling observer here.
+ */
+ DBUG_ASSERT(thd->m_reprepare_observer == NULL);
+
thd->get_stmt_da()->reset_diagnostics_area();
thd->wsrep_conflict_state= REPLAYING;
@@ -301,6 +314,7 @@ void wsrep_replay_transaction(THD *thd)
mysql_mutex_unlock(&LOCK_wsrep_replaying);
}
}
+ DBUG_VOID_RETURN;
}
static void wsrep_replication_process(THD *thd)
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index 8e443b573cb..bb5ee7baa57 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -26,14 +26,6 @@
#include <cstdio>
#include <cstdlib>
-const char* wsrep_provider = 0;
-const char* wsrep_provider_options = 0;
-const char* wsrep_cluster_address = 0;
-const char* wsrep_cluster_name = 0;
-const char* wsrep_node_name = 0;
-const char* wsrep_node_address = 0;
-const char* wsrep_node_incoming_address = 0;
-const char* wsrep_start_position = 0;
int wsrep_init_vars()
{
@@ -45,8 +37,6 @@ int wsrep_init_vars()
wsrep_node_address = my_strdup("", MYF(MY_WME));
wsrep_node_incoming_address= my_strdup(WSREP_NODE_INCOMING_AUTO, MYF(MY_WME));
wsrep_start_position = my_strdup(WSREP_START_POSITION_ZERO, MYF(MY_WME));
-
- global_system_variables.binlog_format=BINLOG_FORMAT_ROW;
return 0;
}
@@ -203,16 +193,46 @@ bool wsrep_start_position_init (const char* val)
return false;
}
+static int get_provider_option_value(const char* opts,
+ const char* opt_name,
+ ulong* opt_value)
+{
+ int ret= 1;
+ ulong opt_value_tmp;
+ char *opt_value_str, *s, *opts_copy= my_strdup(opts, MYF(MY_WME));
+
+ if ((opt_value_str= strstr(opts_copy, opt_name)) == NULL)
+ goto end;
+ opt_value_str= strtok_r(opt_value_str, "=", &s);
+ if (opt_value_str == NULL) goto end;
+ opt_value_str= strtok_r(NULL, ";", &s);
+ if (opt_value_str == NULL) goto end;
+
+ opt_value_tmp= strtoul(opt_value_str, NULL, 10);
+ if (errno == ERANGE) goto end;
+
+ *opt_value= opt_value_tmp;
+ ret= 0;
+
+end:
+ my_free(opts_copy);
+ return ret;
+}
+
static bool refresh_provider_options()
{
+ DBUG_ASSERT(wsrep);
+
WSREP_DEBUG("refresh_provider_options: %s",
(wsrep_provider_options) ? wsrep_provider_options : "null");
char* opts= wsrep->options_get(wsrep);
if (opts)
{
- if (wsrep_provider_options) my_free((void *)wsrep_provider_options);
- wsrep_provider_options = (char*)my_memdup(opts, strlen(opts) + 1,
- MYF(MY_WME));
+ wsrep_provider_options_init(opts);
+ get_provider_option_value(wsrep_provider_options,
+ (char*)"repl.max_ws_size",
+ &wsrep_max_ws_size);
+ free(opts);
}
else
{
@@ -327,17 +347,17 @@ void wsrep_provider_init (const char* value)
bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var)
{
- return 0;
-}
-
-bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type)
-{
if (wsrep == NULL)
{
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
return true;
}
+ return false;
+}
+bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type)
+{
+ DBUG_ASSERT(wsrep);
wsrep_status_t ret= wsrep->options_set(wsrep, wsrep_provider_options);
if (ret != WSREP_OK)
{
@@ -366,14 +386,14 @@ bool wsrep_cluster_address_check (sys_var *self, THD* thd, set_var* var)
char addr_buf[FN_REFLEN];
if ((! var->save_result.string_value.str) ||
- (var->save_result.string_value.length > (FN_REFLEN - 1))) // safety
+ (var->save_result.string_value.length >= sizeof(addr_buf))) // safety
goto err;
- memcpy(addr_buf, var->save_result.string_value.str,
- var->save_result.string_value.length);
- addr_buf[var->save_result.string_value.length]= 0;
+ strmake(addr_buf, var->save_result.string_value.str,
+ MY_MIN(sizeof(addr_buf)-1, var->save_result.string_value.length));
- if (!wsrep_cluster_address_verify(addr_buf)) return 0;
+ if (!wsrep_cluster_address_verify(addr_buf))
+ return 0;
err:
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
@@ -431,8 +451,8 @@ void wsrep_cluster_address_init (const char* value)
(wsrep_cluster_address) ? wsrep_cluster_address : "null",
(value) ? value : "null");
- if (wsrep_cluster_address) my_free ((void*)wsrep_cluster_address);
- wsrep_cluster_address = (value) ? my_strdup(value, MYF(0)) : NULL;
+ my_free((void*) wsrep_cluster_address);
+ wsrep_cluster_address= my_strdup(value ? value : "", MYF(0));
}
/* wsrep_cluster_name cannot be NULL or an empty string. */
@@ -530,6 +550,12 @@ bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
{
+ if (wsrep == NULL)
+ {
+ my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
+ return true;
+ }
+
bool new_wsrep_desync= (bool) var->save_result.ulonglong_value;
if (wsrep_desync == new_wsrep_desync) {
if (new_wsrep_desync) {
@@ -541,20 +567,10 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
ER_WRONG_VALUE_FOR_VAR,
"'wsrep_desync' is already OFF.");
}
+ return false;
}
- return 0;
-}
-
-bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
-{
- if (wsrep == NULL)
- {
- my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
- return true;
- }
-
wsrep_status_t ret(WSREP_WARNING);
- if (wsrep_desync) {
+ if (new_wsrep_desync) {
ret = wsrep->desync (wsrep);
if (ret != WSREP_OK) {
WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret,
@@ -576,6 +592,39 @@ bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
return false;
}
+bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
+{
+ DBUG_ASSERT(wsrep);
+ return false;
+}
+
+bool wsrep_max_ws_size_check(sys_var *self, THD* thd, set_var* var)
+{
+ if (wsrep == NULL)
+ {
+ my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
+ return true;
+ }
+ return false;
+}
+
+bool wsrep_max_ws_size_update (sys_var *self, THD *thd, enum_var_type)
+{
+ DBUG_ASSERT(wsrep);
+
+ char max_ws_size_opt[128];
+ my_snprintf(max_ws_size_opt, sizeof(max_ws_size_opt),
+ "repl.max_ws_size=%d", wsrep_max_ws_size);
+ wsrep_status_t ret= wsrep->options_set(wsrep, max_ws_size_opt);
+ if (ret != WSREP_OK)
+ {
+ WSREP_ERROR("Set options returned %d", ret);
+ refresh_provider_options();
+ return true;
+ }
+ return refresh_provider_options();
+}
+
static SHOW_VAR wsrep_status_vars[]=
{
{"connected", (char*) &wsrep_connected, SHOW_BOOL},
diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h
index 202d6e393e0..1509fc7d589 100644
--- a/sql/wsrep_var.h
+++ b/sql/wsrep_var.h
@@ -81,7 +81,6 @@ extern bool wsrep_sst_receive_address_update UPDATE_ARGS;
extern bool wsrep_sst_auth_check CHECK_ARGS;
extern bool wsrep_sst_auth_update UPDATE_ARGS;
-extern void wsrep_sst_auth_init INIT_ARGS;
extern bool wsrep_sst_donor_check CHECK_ARGS;
extern bool wsrep_sst_donor_update UPDATE_ARGS;
@@ -92,13 +91,15 @@ extern bool wsrep_slave_threads_update UPDATE_ARGS;
extern bool wsrep_desync_check CHECK_ARGS;
extern bool wsrep_desync_update UPDATE_ARGS;
+extern bool wsrep_max_ws_size_check CHECK_ARGS;
+extern bool wsrep_max_ws_size_update UPDATE_ARGS;
+
#else /* WITH_WSREP */
#define WSREP_NONE
#define wsrep_provider_init(X)
#define wsrep_init_vars() (0)
#define wsrep_start_position_init(X)
-#define wsrep_sst_auth_init(X)
#endif /* WITH_WSREP */
#endif /* WSREP_VAR_H */