
[Commits] b428e82: MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
by holyfoot@askmonty.org 13 Feb '19
by holyfoot@askmonty.org 13 Feb '19
13 Feb '19
revision-id: b428e822da09b2bc82a2447332bb980f93c80262 (mariadb-10.4.1-103-gb428e82)
parent(s): 8aae31cf494678b6253031c627566e50bc666920
committer: Alexey Botchkov
timestamp: 2019-02-14 02:46:57 +0400
message:
MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
XA transactions now are kept persistent after prepare.
XA_prepare_log_event implamented, and XA tranasctions are logged
as XA transactions.
---
sql/handler.cc | 9 ++
sql/handler.h | 10 ++
sql/log.cc | 115 +++++++++++++---
sql/log.h | 10 ++
sql/log_event.cc | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++--
sql/log_event.h | 81 +++++++++++
sql/sql_class.cc | 18 +--
sql/sql_class.h | 20 ++-
sql/sql_connect.cc | 1 +
sql/transaction.cc | 69 +++++++++-
sql/transaction.h | 1 +
11 files changed, 691 insertions(+), 40 deletions(-)
diff --git a/sql/handler.cc b/sql/handler.cc
index 001055c..3b2a3e0 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1214,6 +1214,9 @@ static int prepare_or_error(handlerton *ht, THD *thd, bool all)
}
+/*static inline */int
+binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
+ bool all, my_xid xid);
/**
@retval
0 ok
@@ -1225,6 +1228,7 @@ int ha_prepare(THD *thd)
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
Ha_trx_info *ha_info= trans->ha_list;
+
DBUG_ENTER("ha_prepare");
if (ha_info)
@@ -1250,6 +1254,11 @@ int ha_prepare(THD *thd)
}
}
+ if (unlikely(tc_log->log_prepare(thd, all)))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
diff --git a/sql/handler.h b/sql/handler.h
index fc6246c..613c1c3 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -810,6 +810,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
diff --git a/sql/log.cc b/sql/log.cc
index a56117a..316b871 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -87,6 +87,9 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+static int binlog_xa_recover(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
static const LEX_CSTRING write_error_msg=
@@ -1688,6 +1691,9 @@ int binlog_init(void *p)
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare;
+ binlog_hton->recover= binlog_xa_recover;
+ binlog_hton->commit_by_xid = binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid = binlog_rollback_by_xid;
binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
return 0;
@@ -1883,23 +1889,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1961,11 +1960,77 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
+ real work is done in MYSQL_BIN_LOG::log_and_order()
*/
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
+static int binlog_xa_recover(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
+ return 0;
+}
+
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xc_len= sizeof("XA COMMIT ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ if (!cache_mngr)
+ return 1;
+
+ memcpy(buf, "XA COMMIT ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ Query_log_event qe(thd, buf, buflen, TRUE, FALSE, FALSE, 0);
+ return binlog_flush_cache(thd, cache_mngr, &qe, TRUE, TRUE, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xr_len= sizeof("XA ROLLBACK ") - 1; // do not count trailing 0
+ char buf[xr_len + xid_t::ser_buf_size];
+ size_t buflen;
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
+ memcpy(buf, "XA ROLLBACK ", xr_len);
+ buflen= xr_len + serialize_xid(xid, buf+xr_len);
+ Query_log_event qe(thd, buf, buflen, FALSE, TRUE, TRUE, 0);
+ return mysql_bin_log.write_event(&qe);
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9809,6 +9874,24 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_prepare(THD *thd, bool all)
+{
+ XID *xid= &thd->transaction.xid_state.xid;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+
+ if (!cache_mngr)
+ {
+ WSREP_DEBUG("Skipping empty log_xid: %s", thd->query());
+ return 0;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 7dfdb36..92fdf95 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -698,6 +707,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7a0d0be..354c5f3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -6222,6 +6224,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7874,7 +7877,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7882,8 +7885,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7914,6 +7931,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_XA_TRANSACTION;
+ xid= thd->transaction.xid_state.xid;
+ }
}
@@ -7956,7 +7979,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -7968,8 +7991,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8012,7 +8052,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8026,6 +8066,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8079,11 +8125,25 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_XA_TRANSACTION)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
+
thd->update_stats();
if (likely(!thd->is_slave_error))
@@ -8202,9 +8262,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_XA_TRANSACTION) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
goto err;
+ }
return cache.flush_data();
err:
@@ -9003,6 +9083,300 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
#endif /* !MYSQL_CLIENT */
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gtrid_length + bqual_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
+
+ return buf;
+}
+
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA END %s\n%s\n",
+ buf, print_event_info->delimiter) ||
+ my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ rpl_gtid gtid;
+ uint64 sub_id= 0;
+ Relay_log_info const *rli= rgi->rli;
+ xid_t xid;
+ void *hton= NULL;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ xa_states c_state= thd->transaction.xid_state.xa_state;
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
+ false, &hton);
+ thd->transaction.xid_state.xa_state= c_state;
+ if (err)
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ return err;
+ }
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+ /* For a slave XA_prepare_log_event is COMMIT */
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (trans_xa_end(thd))
+ return 1;
+
+ if (!one_phase)
+ {
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+
+ if (!res && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi);
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
+}
+
+
+Log_event::enum_skip_reason
+XA_prepare_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_log_event::do_shall_skip");
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+#ifdef WITH_WSREP
+ else if (wsrep_mysql_replication_bundle && WSREP_ON &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* !MYSQL_CLIENT */
+
+
/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -14789,7 +15163,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 38a40c9..b5c48c9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3064,6 +3065,79 @@ class Xid_log_event: public Log_event
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+*/
+
+class XA_prepare_log_event: public Log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Log_event(thd_arg, 0, TRUE), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+ bool is_valid() const { return 1; }
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3376,6 +3450,11 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3404,6 +3483,8 @@ class Gtid_log_event: public Log_event
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_XA_TRANSACTION is set for XA transaction. */
+ static const uchar FL_XA_TRANSACTION= 64;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index fa2f866..cc75da9 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1461,12 +1461,19 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
-#error xid_state in the cache should be replaced by the allocated value
+ trans_detach(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ }
+ else
+ {
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ trans_rollback(this);
+ xid_cache_delete(this, &transaction.xid_state);
}
-#endif
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
@@ -1474,11 +1481,6 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 69fabee..76befcb 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1255,6 +1255,18 @@ typedef struct st_xid_state {
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
/**
Check that XA transaction has an uncommitted work. Report an error
@@ -1278,6 +1290,12 @@ typedef struct st_xid_state {
}
return false;
}
+
+ void reset()
+ {
+ xid.null();
+ is_binlogged= FALSE;
+ }
} XID_STATE;
void xid_cache_init(void);
@@ -2603,7 +2621,7 @@ class THD :public Statement,
then.
*/
if (!xid_state.rm_error)
- xid_state.xid.null();
+ xid_state.reset();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index b48070b..3e4a067 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1414,6 +1414,7 @@ void do_handle_one_connection(CONNECT *connect)
#endif
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 13614d3..64533d7 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -790,6 +790,44 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
/**
+ Detach the current XA transaction;
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_detach(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+ Ha_trx_info *ha_info, *ha_info_next;
+
+ DBUG_ENTER("trans_detach");
+
+// DBUG_ASSERT(xid_s->xa_state == XA_PREPARED &&
+// xid_cache_search(thd, &xid_s->xid));
+
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED))
+ DBUG_RETURN(TRUE);
+
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Starts an XA transaction with the given xid value.
@param thd Current thread
@@ -928,6 +966,12 @@ bool trans_xa_commit(THD *thd)
res= !xs;
if (res)
my_error(ER_XAER_NOTA, MYF(0));
+ else if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ res= TRUE;
+ }
else
{
res= xa_trans_rolled_back(xs);
@@ -978,8 +1022,16 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ if (res || (res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
@@ -1044,7 +1096,18 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(TRUE);
}
- res= xa_trans_force_rollback(thd);
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ res= res || xa_trans_force_rollback(thd);
+ if (res || (res= MY_TEST(xa_trans_force_rollback(thd))))
+ my_error(ER_XAER_RMERR, MYF(0));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
diff --git a/sql/transaction.h b/sql/transaction.h
index 7e34693..f228cc6 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -29,6 +29,7 @@ bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
bool trans_rollback_implicit(THD *thd);
+bool trans_detach(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
1
0

[Commits] b428e82: MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
by holyfoot@askmonty.org 13 Feb '19
by holyfoot@askmonty.org 13 Feb '19
13 Feb '19
revision-id: b428e822da09b2bc82a2447332bb980f93c80262 (mariadb-10.4.1-103-gb428e82)
parent(s): 8aae31cf494678b6253031c627566e50bc666920
committer: Alexey Botchkov
timestamp: 2019-02-14 02:46:57 +0400
message:
MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
XA transactions now are kept persistent after prepare.
XA_prepare_log_event implamented, and XA tranasctions are logged
as XA transactions.
---
sql/handler.cc | 9 ++
sql/handler.h | 10 ++
sql/log.cc | 115 +++++++++++++---
sql/log.h | 10 ++
sql/log_event.cc | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++--
sql/log_event.h | 81 +++++++++++
sql/sql_class.cc | 18 +--
sql/sql_class.h | 20 ++-
sql/sql_connect.cc | 1 +
sql/transaction.cc | 69 +++++++++-
sql/transaction.h | 1 +
11 files changed, 691 insertions(+), 40 deletions(-)
diff --git a/sql/handler.cc b/sql/handler.cc
index 001055c..3b2a3e0 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1214,6 +1214,9 @@ static int prepare_or_error(handlerton *ht, THD *thd, bool all)
}
+/*static inline */int
+binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
+ bool all, my_xid xid);
/**
@retval
0 ok
@@ -1225,6 +1228,7 @@ int ha_prepare(THD *thd)
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
Ha_trx_info *ha_info= trans->ha_list;
+
DBUG_ENTER("ha_prepare");
if (ha_info)
@@ -1250,6 +1254,11 @@ int ha_prepare(THD *thd)
}
}
+ if (unlikely(tc_log->log_prepare(thd, all)))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
diff --git a/sql/handler.h b/sql/handler.h
index fc6246c..613c1c3 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -810,6 +810,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
diff --git a/sql/log.cc b/sql/log.cc
index a56117a..316b871 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -87,6 +87,9 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+static int binlog_xa_recover(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
static const LEX_CSTRING write_error_msg=
@@ -1688,6 +1691,9 @@ int binlog_init(void *p)
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare;
+ binlog_hton->recover= binlog_xa_recover;
+ binlog_hton->commit_by_xid = binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid = binlog_rollback_by_xid;
binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
return 0;
@@ -1883,23 +1889,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1961,11 +1960,77 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
+ real work is done in MYSQL_BIN_LOG::log_and_order()
*/
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
+static int binlog_xa_recover(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
+ return 0;
+}
+
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xc_len= sizeof("XA COMMIT ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ if (!cache_mngr)
+ return 1;
+
+ memcpy(buf, "XA COMMIT ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ Query_log_event qe(thd, buf, buflen, TRUE, FALSE, FALSE, 0);
+ return binlog_flush_cache(thd, cache_mngr, &qe, TRUE, TRUE, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xr_len= sizeof("XA ROLLBACK ") - 1; // do not count trailing 0
+ char buf[xr_len + xid_t::ser_buf_size];
+ size_t buflen;
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
+ memcpy(buf, "XA ROLLBACK ", xr_len);
+ buflen= xr_len + serialize_xid(xid, buf+xr_len);
+ Query_log_event qe(thd, buf, buflen, FALSE, TRUE, TRUE, 0);
+ return mysql_bin_log.write_event(&qe);
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9809,6 +9874,24 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_prepare(THD *thd, bool all)
+{
+ XID *xid= &thd->transaction.xid_state.xid;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+
+ if (!cache_mngr)
+ {
+ WSREP_DEBUG("Skipping empty log_xid: %s", thd->query());
+ return 0;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 7dfdb36..92fdf95 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -698,6 +707,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7a0d0be..354c5f3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -6222,6 +6224,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7874,7 +7877,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7882,8 +7885,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7914,6 +7931,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_XA_TRANSACTION;
+ xid= thd->transaction.xid_state.xid;
+ }
}
@@ -7956,7 +7979,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -7968,8 +7991,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8012,7 +8052,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8026,6 +8066,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8079,11 +8125,25 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_XA_TRANSACTION)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
+
thd->update_stats();
if (likely(!thd->is_slave_error))
@@ -8202,9 +8262,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_XA_TRANSACTION) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
goto err;
+ }
return cache.flush_data();
err:
@@ -9003,6 +9083,300 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
#endif /* !MYSQL_CLIENT */
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gtrid_length + bqual_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
+
+ return buf;
+}
+
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA END %s\n%s\n",
+ buf, print_event_info->delimiter) ||
+ my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ rpl_gtid gtid;
+ uint64 sub_id= 0;
+ Relay_log_info const *rli= rgi->rli;
+ xid_t xid;
+ void *hton= NULL;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ xa_states c_state= thd->transaction.xid_state.xa_state;
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
+ false, &hton);
+ thd->transaction.xid_state.xa_state= c_state;
+ if (err)
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ return err;
+ }
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+ /* For a slave XA_prepare_log_event is COMMIT */
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (trans_xa_end(thd))
+ return 1;
+
+ if (!one_phase)
+ {
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+
+ if (!res && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi);
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
+}
+
+
+Log_event::enum_skip_reason
+XA_prepare_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_log_event::do_shall_skip");
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+#ifdef WITH_WSREP
+ else if (wsrep_mysql_replication_bundle && WSREP_ON &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* !MYSQL_CLIENT */
+
+
/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -14789,7 +15163,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 38a40c9..b5c48c9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3064,6 +3065,79 @@ class Xid_log_event: public Log_event
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+*/
+
+class XA_prepare_log_event: public Log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Log_event(thd_arg, 0, TRUE), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+ bool is_valid() const { return 1; }
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3376,6 +3450,11 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3404,6 +3483,8 @@ class Gtid_log_event: public Log_event
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_XA_TRANSACTION is set for XA transaction. */
+ static const uchar FL_XA_TRANSACTION= 64;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index fa2f866..cc75da9 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1461,12 +1461,19 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
-#error xid_state in the cache should be replaced by the allocated value
+ trans_detach(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ }
+ else
+ {
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ trans_rollback(this);
+ xid_cache_delete(this, &transaction.xid_state);
}
-#endif
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
@@ -1474,11 +1481,6 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 69fabee..76befcb 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1255,6 +1255,18 @@ typedef struct st_xid_state {
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
/**
Check that XA transaction has an uncommitted work. Report an error
@@ -1278,6 +1290,12 @@ typedef struct st_xid_state {
}
return false;
}
+
+ void reset()
+ {
+ xid.null();
+ is_binlogged= FALSE;
+ }
} XID_STATE;
void xid_cache_init(void);
@@ -2603,7 +2621,7 @@ class THD :public Statement,
then.
*/
if (!xid_state.rm_error)
- xid_state.xid.null();
+ xid_state.reset();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index b48070b..3e4a067 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1414,6 +1414,7 @@ void do_handle_one_connection(CONNECT *connect)
#endif
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 13614d3..64533d7 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -790,6 +790,44 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
/**
+ Detach the current XA transaction;
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_detach(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+ Ha_trx_info *ha_info, *ha_info_next;
+
+ DBUG_ENTER("trans_detach");
+
+// DBUG_ASSERT(xid_s->xa_state == XA_PREPARED &&
+// xid_cache_search(thd, &xid_s->xid));
+
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED))
+ DBUG_RETURN(TRUE);
+
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Starts an XA transaction with the given xid value.
@param thd Current thread
@@ -928,6 +966,12 @@ bool trans_xa_commit(THD *thd)
res= !xs;
if (res)
my_error(ER_XAER_NOTA, MYF(0));
+ else if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ res= TRUE;
+ }
else
{
res= xa_trans_rolled_back(xs);
@@ -978,8 +1022,16 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ if (res || (res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
@@ -1044,7 +1096,18 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(TRUE);
}
- res= xa_trans_force_rollback(thd);
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ res= res || xa_trans_force_rollback(thd);
+ if (res || (res= MY_TEST(xa_trans_force_rollback(thd))))
+ my_error(ER_XAER_RMERR, MYF(0));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
diff --git a/sql/transaction.h b/sql/transaction.h
index 7e34693..f228cc6 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -29,6 +29,7 @@ bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
bool trans_rollback_implicit(THD *thd);
+bool trans_detach(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
1
0
revision-id: e8b6c15010e6fffe17e27c165b8c60b51a8f66a7 (mariadb-10.3.12-55-ge8b6c15)
parent(s): ce0678f6cb367c2f3bf897d467846b47aa710529
committer: Alexey Botchkov
timestamp: 2019-02-13 23:26:23 +0400
message:
connect.xml.result fixed.
---
storage/connect/mysql-test/connect/r/xml.result | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result
index 99739b1..6a0c9db 100644
--- a/storage/connect/mysql-test/connect/r/xml.result
+++ b/storage/connect/mysql-test/connect/r/xml.result
@@ -323,7 +323,7 @@ HEX(c) 3F3F3F3F3F3F3F
Warnings:
Level Warning
Code 1366
-Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column 'c' at row 1
+Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column `test`.`t1`.`c` at row 1
Level Warning
Code 1105
Message Out of range value ÁÂÃÄÅÆÇ for column 'c' at row 1
1
0

[Commits] 62fad4e: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 13 Feb '19
by IgorBabaev 13 Feb '19
13 Feb '19
revision-id: 62fad4e8e964786cd1c8d675f3c071a405a8d187 (mariadb-10.3.6-137-g62fad4e)
parent(s): 953ca199fb62bcd190d2af4d6177f986564ec1ad
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-13 08:55:38 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
Fixing failures of federated test on 32-bit platforms
---
storage/federatedx/ha_federatedx.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc
index f55b0bc..b0a08a0 100644
--- a/storage/federatedx/ha_federatedx.cc
+++ b/storage/federatedx/ha_federatedx.cc
@@ -3684,7 +3684,7 @@ struct st_mysql_storage_engine federatedx_storage_engine=
my_bool use_pushdown;
static MYSQL_SYSVAR_BOOL(pushdown, use_pushdown, 0,
"Use query fragments pushdown capabilities", NULL, NULL, FALSE);
-static struct st_mysql_sys_var* sysvars[]= { MYSQL_SYSVAR(pushdown) };
+static struct st_mysql_sys_var* sysvars[]= { MYSQL_SYSVAR(pushdown), NULL };
#include "federatedx_pushdown.cc"
1
0

[Commits] a78365c11ac: MDEV-18553: MDEV-16327 pre-requisits isolation of LIMIT/OFFSET handling
by Oleksandr Byelkin 13 Feb '19
by Oleksandr Byelkin 13 Feb '19
13 Feb '19
revision-id: a78365c11ac8b8ac2697a185d2516152a44f3dfe (mariadb-10.4.1-78-ga78365c11ac)
parent(s): d97a26a67dca82d166e9edefe5f950ea8a110dfd
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-02-13 09:54:51 +0100
message:
MDEV-18553: MDEV-16327 pre-requisits isolation of LIMIT/OFFSET handling
---
sql/group_by_handler.cc | 8 +++----
sql/group_by_handler.h | 1 +
sql/item_subselect.cc | 2 +-
sql/opt_subselect.cc | 9 +++-----
sql/sql_class.cc | 44 +++++++++++-------------------------
sql/sql_class.h | 6 ++---
sql/sql_derived.cc | 2 +-
sql/sql_error.cc | 10 ++++-----
sql/sql_insert.cc | 7 ++----
sql/sql_lex.cc | 10 ++-------
sql/sql_lex.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-
sql/sql_parse.cc | 13 ++++++-----
sql/sql_profile.cc | 10 ++++-----
sql/sql_repl.cc | 10 ++++-----
sql/sql_select.cc | 46 ++++++++++++++++++++------------------
sql/sql_union.cc | 36 +++++++++++++-----------------
16 files changed, 148 insertions(+), 125 deletions(-)
diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc
index f18758a2d94..f5ed24370b1 100644
--- a/sql/group_by_handler.cc
+++ b/sql/group_by_handler.cc
@@ -40,7 +40,7 @@ int Pushdown_query::execute(JOIN *join)
{
int err;
ha_rows max_limit;
- ha_rows *reset_limit= 0;
+ bool reset_limit= FALSE;
Item **reset_item= 0;
THD *thd= handler->thd;
TABLE *table= handler->table;
@@ -52,11 +52,11 @@ int Pushdown_query::execute(JOIN *join)
if (store_data_in_temp_table)
{
max_limit= join->tmp_table_param.end_write_records;
- reset_limit= &join->unit->select_limit_cnt;
+ reset_limit= TRUE;
}
else
{
- max_limit= join->unit->select_limit_cnt;
+ max_limit= join->unit->lim.get_select_limit();
if (join->unit->fake_select_lex)
reset_item= &join->unit->fake_select_lex->select_limit;
}
@@ -112,7 +112,7 @@ int Pushdown_query::execute(JOIN *join)
break; // LIMIT reached
join->do_send_rows= 0; // Calculate FOUND_ROWS()
if (reset_limit)
- *reset_limit= HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
if (reset_item)
*reset_item= 0;
}
diff --git a/sql/group_by_handler.h b/sql/group_by_handler.h
index d3f48a15c24..be981f0da5f 100644
--- a/sql/group_by_handler.h
+++ b/sql/group_by_handler.h
@@ -56,6 +56,7 @@ struct Query
ORDER *order_by;
Item *having;
// LIMIT
+ //ha_rows select_limit_cnt, offset_limit_cnt;
};
class group_by_handler
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 0ace59fd2fc..e860c3fa98d 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -2721,7 +2721,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
join_arg->thd->change_item_tree(&unit->global_parameters()->select_limit,
new (thd->mem_root)
Item_int(thd, (int32) 1));
- unit->select_limit_cnt= 1;
+ unit->lim.set_single_row();
DBUG_RETURN(false);
}
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index c4c30c9b50d..e9284926385 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -5492,11 +5492,8 @@ int select_value_catcher::send_data(List<Item> &items)
DBUG_ASSERT(!assigned);
DBUG_ASSERT(items.elements == n_elements);
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
Item *val_item;
List_iterator_fast<Item> li(items);
@@ -6289,7 +6286,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Set the limit of this JOIN object as well, because normally its being
set in the beginning of JOIN::optimize, which was already done.
*/
- select_limit= in_subs->unit->select_limit_cnt;
+ select_limit= in_subs->unit->lim.get_select_limit();
}
else if (in_subs->test_strategy(SUBS_IN_TO_EXISTS))
{
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 2952adbd3e6..ce6d71082fb 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2936,11 +2936,8 @@ int select_send::send_data(List<Item> &items)
DBUG_ENTER("select_send::send_data");
/* unit is not set when using 'delete ... returning' */
- if (unit && unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(FALSE);
- }
+ if (unit && unit->lim.check_and_move_offset())
+ DBUG_RETURN(FALSE); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(FALSE);
@@ -3205,11 +3202,8 @@ int select_export::send_data(List<Item> &items)
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
tmp.length(0);
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
row_count++;
@@ -3465,11 +3459,8 @@ int select_dump::send_data(List<Item> &items)
Item *item;
DBUG_ENTER("select_dump::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
@@ -3508,11 +3499,8 @@ int select_singlerow_subselect::send_data(List<Item> &items)
MYF(current_thd->lex->ignore ? ME_WARNING : 0));
DBUG_RETURN(1);
}
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
List_iterator_fast<Item> li(items);
@@ -3649,11 +3637,8 @@ int select_exists_subselect::send_data(List<Item> &items)
{
DBUG_ENTER("select_exists_subselect::send_data");
Item_exists_subselect *it= (Item_exists_subselect *)item;
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
it->value= 1;
@@ -4060,12 +4045,9 @@ int select_dumpvar::send_data(List<Item> &items)
{
DBUG_ENTER("select_dumpvar::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (row_count++)
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
+ if (row_count++)
{
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b1da6e19247..7427b2745bc 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -5117,9 +5117,9 @@ class select_result :public select_result_sink
/* this method is called just before the first row of the table can be read */
virtual void prepare_to_read_rows() {}
- void reset_offset_limit()
+ void remove_offset_limit()
{
- unit->offset_limit_cnt= 0;
+ unit->lim.remove_offset();
}
/*
@@ -5786,7 +5786,7 @@ class select_union_direct :public select_unit
*/
DBUG_ASSERT(false); /* purecov: inspected */
}
- void reset_offset_limit_cnt()
+ void remove_offset_limit()
{
// EXPLAIN should never output to a select_union_direct
DBUG_ASSERT(false); /* purecov: inspected */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 878aa715b84..4fd44a18550 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1148,7 +1148,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX *first_select= unit->first_select();
unit->set_limit(unit->global_parameters());
- if (unit->select_limit_cnt == HA_POS_ERROR)
+ if (unit->lim.is_unlimited())
first_select->options&= ~OPTION_FOUND_ROWS;
lex->current_select= first_select;
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 8d639f9271d..6bb36dbb089 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -783,7 +783,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
const Sql_condition *err;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ulonglong idx= 0;
+ ha_rows idx;
Protocol *protocol=thd->protocol;
DBUG_ENTER("mysqld_show_warnings");
@@ -808,14 +808,14 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
- while ((err= it++))
+ for (idx= 1; (err= it++) ; idx++)
{
/* Skip levels that the user is not interested in */
if (!(levels_to_show & ((ulong) 1 << err->get_level())))
continue;
- if (++idx <= unit->offset_limit_cnt)
- continue;
- if (idx > unit->select_limit_cnt)
+ if (unit->lim.check_and_move_offset())
+ continue; // using limit offset,count
+ if (idx > unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
protocol->store(warning_level_names[err->get_level()].str,
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index f5e4185db92..c4f60f0551c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3840,11 +3840,8 @@ int select_insert::send_data(List<Item> &values)
DBUG_ENTER("select_insert::send_data");
bool error=0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (unlikely(thd->killed == ABORT_QUERY))
DBUG_RETURN(0);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 1b8f448553b..2c298a17391 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2321,8 +2321,7 @@ void st_select_lex_unit::init_query()
{
init_query_common();
set_linkage(GLOBAL_OPTIONS_TYPE);
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
union_distinct= 0;
prepared= optimized= optimized_2= executed= 0;
optimize_started= 0;
@@ -3462,12 +3461,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
- offset_limit_cnt= sl->get_offset();
- select_limit_cnt= sl->get_limit();
- if (select_limit_cnt + offset_limit_cnt >= select_limit_cnt)
- select_limit_cnt+= offset_limit_cnt;
- else
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_limit(sl->get_limit(), sl->get_offset());
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 40ba3b6e7b7..0270a4acfcc 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -816,6 +816,63 @@ void create_explain_query(LEX *lex, MEM_ROOT *mem_root);
void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root);
bool print_explain_for_slow_log(LEX *lex, THD *thd, String *str);
+class Select_limit_counters
+{
+ ha_rows offset_limit_cnt_start,
+ select_limit_cnt, offset_limit_cnt;
+
+ public:
+ Select_limit_counters():
+ offset_limit_cnt_start(0),
+ select_limit_cnt(0), offset_limit_cnt(0)
+ {};
+
+ void set_limit(ha_rows limit, ha_rows offset)
+ {
+ offset_limit_cnt_start= offset;
+ select_limit_cnt= limit;
+ if (select_limit_cnt + offset_limit_cnt_start >=
+ select_limit_cnt)
+ select_limit_cnt+= offset_limit_cnt_start;
+ else
+ select_limit_cnt= HA_POS_ERROR;
+ reset();
+ }
+
+ void set_single_row()
+ {
+ offset_limit_cnt= offset_limit_cnt_start= 0;
+ select_limit_cnt= 1;
+ }
+
+ void reset()
+ {
+ offset_limit_cnt= offset_limit_cnt_start;
+ }
+
+ bool is_unlimited()
+ { return select_limit_cnt == HA_POS_ERROR; }
+ void set_unlimited()
+ { select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; }
+
+ bool check_and_move_offset()
+ {
+ if (offset_limit_cnt)
+ {
+ offset_limit_cnt--;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ void remove_offset() { offset_limit_cnt= 0; }
+
+ ha_rows get_select_limit()
+ { return select_limit_cnt; }
+ ha_rows get_offset_limit()
+ { return offset_limit_cnt; }
+};
+
+
class st_select_lex_unit: public st_select_lex_node {
protected:
TABLE_LIST result_table_list;
@@ -891,7 +948,7 @@ class st_select_lex_unit: public st_select_lex_node {
//node on which we should return current_select pointer after parsing subquery
st_select_lex *return_to;
/* LIMIT clause runtime counters */
- ha_rows select_limit_cnt, offset_limit_cnt;
+ Select_limit_counters lim;
/* not NULL if unit used in subselect, point to subselect item */
Item_subselect *item;
/*
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7d6f71cda21..9e6600d65b6 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4579,7 +4579,7 @@ mysql_execute_command(THD *thd)
select_lex->where,
select_lex->order_list.elements,
select_lex->order_list.first,
- unit->select_limit_cnt,
+ unit->lim.get_select_limit(),
lex->duplicates, lex->ignore,
&found, &updated);
MYSQL_UPDATE_DONE(res, found, updated);
@@ -4924,7 +4924,7 @@ mysql_execute_command(THD *thd)
res = mysql_delete(thd, all_tables,
select_lex->where, &select_lex->order_list,
- unit->select_limit_cnt, select_lex->options,
+ unit->lim.get_select_limit(), select_lex->options,
sel_result);
if (replaced_protocol)
@@ -5771,7 +5771,8 @@ mysql_execute_command(THD *thd)
res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- unit->select_limit_cnt, unit->offset_limit_cnt);
+ unit->lim.get_select_limit(),
+ unit->lim.get_offset_limit());
break;
case SQLCOM_BEGIN:
@@ -6511,8 +6512,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
/*
Do like the original select_describe did: remove OFFSET from the
top-level LIMIT
- */
- result->reset_offset_limit();
+ */
+ result->remove_offset_limit();
if (lex->explain_json)
{
lex->explain->print_explain_json(result, lex->analyze_stmt);
@@ -7853,7 +7854,7 @@ void mysql_init_multi_delete(LEX *lex)
lex->sql_command= SQLCOM_DELETE_MULTI;
mysql_init_select(lex);
lex->first_select_lex()->select_limit= 0;
- lex->unit.select_limit_cnt= HA_POS_ERROR;
+ lex->unit.lim.set_unlimited();
lex->first_select_lex()->table_list.
save_and_clear(&lex->auxiliary_table_list);
lex->query_tables= 0;
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 6ca21aebb37..e2b7b18faac 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -404,7 +404,7 @@ bool PROFILING::show_profiles()
MEM_ROOT *mem_root= thd->mem_root;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows idx= 0;
+ ha_rows idx;
Protocol *protocol= thd->protocol;
void *iterator;
DBUG_ENTER("PROFILING::show_profiles");
@@ -428,9 +428,9 @@ bool PROFILING::show_profiles()
unit->set_limit(sel);
- for (iterator= history.new_iterator();
+ for (iterator= history.new_iterator(), idx= 1;
iterator != NULL;
- iterator= history.iterator_next(iterator))
+ iterator= history.iterator_next(iterator), idx++)
{
prof= history.iterator_value(iterator);
@@ -438,9 +438,9 @@ bool PROFILING::show_profiles()
double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
- if (++idx <= unit->offset_limit_cnt)
+ if (unit->lim.check_and_move_offset())
continue;
- if (idx > unit->select_limit_cnt)
+ if (idx > unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 2ee175293de..6f712750e41 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -3911,7 +3911,7 @@ bool mysql_show_binlog_events(THD* thd)
if (binary_log->is_open())
{
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows event_count, limit_start, limit_end;
+ ha_rows event_count;
my_off_t pos = MY_MAX(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
const char *log_file_name = lex_mi->log_file_name;
@@ -3926,8 +3926,6 @@ bool mysql_show_binlog_events(THD* thd)
}
unit->set_limit(thd->lex->current_select);
- limit_start= unit->offset_limit_cnt;
- limit_end= unit->select_limit_cnt;
name= search_file_name;
if (log_file_name)
@@ -4006,7 +4004,7 @@ bool mysql_show_binlog_events(THD* thd)
description_event,
opt_master_verify_checksum)); )
{
- if (event_count >= limit_start &&
+ if (!unit->lim.check_and_move_offset() &&
ev->net_send(protocol, linfo.log_file_name, pos))
{
errmsg = "Net error";
@@ -4040,11 +4038,11 @@ bool mysql_show_binlog_events(THD* thd)
pos = my_b_tell(&log);
- if (++event_count >= limit_end)
+ if (++event_count >= unit->lim.get_select_limit())
break;
}
- if (unlikely(event_count < limit_end && log.error))
+ if (unlikely(event_count < unit->lim.get_select_limit() && log.error))
{
errmsg = "Wrong offset or I/O error";
mysql_mutex_unlock(log_lock);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 978f0785887..aa4c40866a9 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1508,7 +1508,7 @@ JOIN::optimize_inner()
{
DBUG_ENTER("JOIN::optimize");
subq_exit_fl= false;
- do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
+ do_send_rows = (unit->lim.get_select_limit()) ? 1 : 0;
DEBUG_SYNC(thd, "before_join_optimize");
@@ -1579,9 +1579,9 @@ JOIN::optimize_inner()
DBUG_RETURN(-1);
row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
- unit->select_limit_cnt);
+ unit->lim.get_select_limit());
/* select_limit is used to decide if we are likely to scan the whole table */
- select_limit= unit->select_limit_cnt;
+ select_limit= unit->lim.get_select_limit();
if (having || (select_options & OPTION_FOUND_ROWS))
select_limit= HA_POS_ERROR;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
@@ -1790,9 +1790,10 @@ JOIN::optimize_inner()
thd->change_item_tree(&sel->having, having);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
- (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
+ (!unit->lim.get_select_limit() &&
+ !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
- if (unit->select_limit_cnt)
+ if (unit->lim.get_select_limit())
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE"));
@@ -3314,7 +3315,7 @@ bool JOIN::make_aggr_tables_info()
*/
sort_tab->filesort->limit=
(has_group_by || (join_tab + table_count > curr_tab + 1)) ?
- select_limit : unit->select_limit_cnt;
+ select_limit : unit->lim.get_select_limit();
}
if (!only_const_tables() &&
!join_tab[const_tables].filesort &&
@@ -3698,8 +3699,7 @@ JOIN::reinit()
{
DBUG_ENTER("JOIN::reinit");
- unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
- select_lex->offset_limit->val_uint() : 0);
+ unit->lim.reset();
first_record= false;
group_sent= false;
@@ -5157,7 +5157,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
for (i= 0; i < join->table_count ; i++)
records*= join->best_positions[i].records_read ?
(ha_rows)join->best_positions[i].records_read : 1;
- set_if_smaller(records, unit->select_limit_cnt);
+ set_if_smaller(records, unit->lim.get_select_limit());
join->select_lex->increase_derived_records(records);
}
}
@@ -7446,7 +7446,7 @@ best_access_path(JOIN *join,
if (!best_key &&
idx == join->const_tables &&
s->table == join->sort_by_table &&
- join->unit->select_limit_cnt >= records)
+ join->unit->lim.get_select_limit() >= records)
join->sort_by_table= (TABLE*) 1; // Must use temporary table
DBUG_VOID_RETURN;
@@ -10799,7 +10799,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
!tab->loosescan_match_tab && // (1)
((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) ||
(!tab->const_keys.is_clear_all() && i == join->const_tables &&
- join->unit->select_limit_cnt <
+ join->unit->lim.get_select_limit() <
join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS))))
{
@@ -10823,7 +10823,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt), 0,
+ join->unit->lim.get_select_limit()), 0,
FALSE, FALSE) < 0)
{
/*
@@ -10837,7 +10837,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt),0,
+ join->unit->lim.get_select_limit()),0,
FALSE, FALSE) < 0)
DBUG_RETURN(1); // Impossible WHERE
}
@@ -20581,7 +20581,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
++join->send_records;
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
!join->do_send_rows)
{
/*
@@ -20599,7 +20599,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
}
}
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -20740,13 +20740,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
else if (join->send_records >= join->fetch_limit)
{
@@ -20831,7 +20831,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
}
}
@@ -22142,7 +22142,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
(tab->join->select_options &
OPTION_FOUND_ROWS) ?
HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,TRUE,
+ tab->join->unit->
+ lim.get_select_limit(),
+ TRUE,
TRUE, FALSE) <= 0;
if (res)
{
@@ -22244,7 +22246,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
select->test_quick_select(join->thd, tmp_map, 0,
join->select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt,
+ join->unit->lim.get_select_limit(),
TRUE, FALSE, FALSE);
if (cond_saved)
@@ -22671,7 +22673,7 @@ JOIN_TAB::remove_duplicates()
if (!field_count && !(join->select_options & OPTION_FOUND_ROWS) && !having)
{ // only const items with no OPTION_FOUND_ROWS
- join->unit->select_limit_cnt= 1; // Only send first row
+ join->unit->lim.set_single_row(); // Only send first row
DBUG_RETURN(false);
}
@@ -24859,7 +24861,7 @@ int JOIN::rollup_send_data(uint idx)
copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]);
if ((!having || having->val_int()))
{
- if (send_records < unit->select_limit_cnt && do_send_rows &&
+ if (send_records < unit->lim.get_select_limit() && do_send_rows &&
(res= result->send_data(rollup.fields[i])) > 0)
return 1;
if (!res)
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index ca8a5e7a8b3..d563301a339 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -110,11 +110,8 @@ int select_unit::send_data(List<Item> &values)
{
int rc;
int not_reported_error= 0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- return 0;
- }
+ if (unit->lim.check_and_move_offset())
+ return 0; // using limit offset,count
if (thd->killed == ABORT_QUERY)
return 0;
if (table->no_rows_with_nulls)
@@ -883,8 +880,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
else
{
sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
if (!sl->join->procedure &&
result->prepare(sl->join->fields_list, this))
{
@@ -1317,7 +1313,7 @@ bool st_select_lex_unit::optimize()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
if (sl->tvc->optimize(thd))
{
@@ -1337,13 +1333,13 @@ bool st_select_lex_unit::optimize()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1352,7 +1348,7 @@ bool st_select_lex_unit::optimize()
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
@@ -1432,13 +1428,13 @@ bool st_select_lex_unit::exec()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1449,14 +1445,14 @@ bool st_select_lex_unit::exec()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->tvc->optimize(thd);
}
else
{
- sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->join->select_options=
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
}
@@ -1478,9 +1474,7 @@ bool st_select_lex_unit::exec()
}
if (!sl->tvc)
saved_error= sl->join->error;
- offset_limit_cnt= (ha_rows)(sl->offset_limit ?
- sl->offset_limit->val_uint() :
- 0);
+ lim.reset();
if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
@@ -1507,8 +1501,8 @@ bool st_select_lex_unit::exec()
DBUG_RETURN(1);
}
}
- if (found_rows_for_union && !sl->braces &&
- select_limit_cnt != HA_POS_ERROR)
+ if (found_rows_for_union && !sl->braces &&
+ !lim.is_unlimited())
{
/*
This is a union without braces. Remember the number of rows that
1
0
revision-id: 953ca199fb62bcd190d2af4d6177f986564ec1ad (mariadb-10.3.6-136-g953ca19)
parent(s): 27c3abde3071ad2010cbcda5b07435ad15364a70 be8709eb7bdf2a68a1c04fd8ab368113f5f39b63
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 23:19:43 -0800
message:
Merge branch '10.4' into bb-10.4-mdev17096
cmake/build_configurations/mysql_release.cmake | 3 +
cmake/wsrep.cmake | 20 +-
extra/innochecksum.cc | 138 +-
extra/mariabackup/fil_cur.cc | 32 +-
extra/mariabackup/fil_cur.h | 7 +-
extra/mariabackup/read_filt.cc | 2 +-
extra/mariabackup/read_filt.h | 2 +-
extra/mariabackup/write_filt.cc | 12 +-
extra/mariabackup/xtrabackup.cc | 37 +-
extra/mariabackup/xtrabackup.h | 9 +-
libmysqld/CMakeLists.txt | 1 +
mysql-test/main/failed_auth_unixsocket.result | 2 +-
mysql-test/main/failed_auth_unixsocket.test | 2 +-
mysql-test/main/information_schema.result | 3 +
.../main/information_schema_all_engines.result | 8 +-
mysql-test/main/mysql_upgrade-6984.result | 2 +-
mysql-test/main/mysql_upgrade-6984.test | 2 +-
mysql-test/main/mysqld--help.result | 8 +
mysql-test/main/opt_trace.result | 3499 ++++++++++++++++++++
mysql-test/main/opt_trace.test | 335 ++
mysql-test/main/opt_trace_index_merge.result | 249 ++
mysql-test/main/opt_trace_index_merge.test | 21 +
.../main/opt_trace_index_merge_innodb.result | 242 ++
mysql-test/main/opt_trace_index_merge_innodb.test | 31 +
mysql-test/main/opt_trace_security.result | 396 +++
mysql-test/main/opt_trace_security.test | 197 ++
mysql-test/main/type_timestamp.result | 9 +
mysql-test/main/type_timestamp.test | 8 +
mysql-test/suite/funcs_1/r/is_columns_is.result | 8 +
.../suite/funcs_1/r/is_columns_is_embedded.result | 8 +
mysql-test/suite/funcs_1/r/is_tables_is.result | 50 +
.../suite/funcs_1/r/is_tables_is_embedded.result | 50 +
mysql-test/suite/galera/disabled.def | 1 -
.../galera/r/galera_gcache_recover_manytrx.result | 2 +
mysql-test/suite/galera/r/galera_sst_rsync2.result | 2 +
.../galera/r/galera_var_load_data_splitting.result | 4 +
mysql-test/suite/galera/t/galera_sst_mysqldump.cnf | 2 -
.../r/galera_evs_suspect_timeout.result | 1 -
.../t/galera_evs_suspect_timeout.test | 2 +-
.../suite/innodb/r/alter_varchar_change.result | 18 +-
.../suite/innodb/r/instant_alter_bugs.result | 47 +
.../suite/innodb/r/instant_alter_import.result | 72 +
.../suite/innodb/t/alter_varchar_change.test | 18 +-
mysql-test/suite/innodb/t/instant_alter_bugs.test | 49 +
.../suite/innodb/t/instant_alter_import.test | 84 +
.../roles/i_s_applicable_roles_is_default.result | 2 +-
.../roles/i_s_applicable_roles_is_default.test | 2 +-
.../sys_vars/r/sysvars_server_embedded,32bit.rdiff | 319 +-
.../sys_vars/r/sysvars_server_embedded.result | 28 +
.../r/sysvars_server_notembedded,32bit.rdiff | 339 +-
.../sys_vars/r/sysvars_server_notembedded.result | 28 +
mysys/my_fopen.c | 5 +-
.../mysql-test/user_variables/basic.result | 2 +-
plugin/user_variables/user_variables.cc | 2 +-
scripts/mysql_install_db.sh | 29 +-
scripts/mysql_secure_installation.sh | 59 +-
scripts/mysql_system_tables_data.sql | 9 +-
scripts/wsrep_sst_mariabackup.sh | 4 +-
scripts/wsrep_sst_rsync.sh | 23 +-
sql/CMakeLists.txt | 1 +
sql/ha_sequence.cc | 2 +-
sql/handler.h | 1 +
sql/my_json_writer.cc | 75 +-
sql/my_json_writer.h | 425 ++-
sql/mysqld.cc | 9 +-
sql/mysqld.h | 3 +-
sql/opt_range.cc | 845 ++++-
sql/opt_range.h | 2 +-
sql/opt_subselect.cc | 66 +-
sql/opt_table_elimination.cc | 30 +-
sql/opt_trace.cc | 722 ++++
sql/opt_trace.h | 201 ++
sql/opt_trace_context.h | 92 +
sql/set_var.h | 9 +
sql/sp_head.cc | 13 +
sql/sp_head.h | 1 +
sql/sql_class.cc | 14 +
sql/sql_class.h | 15 +
sql/sql_derived.cc | 49 +-
sql/sql_explain.cc | 2 +-
sql/sql_parse.cc | 13 +-
sql/sql_parse.h | 2 +-
sql/sql_prepare.cc | 12 +
sql/sql_select.cc | 632 +++-
sql/sql_show.cc | 7 +
sql/sql_test.cc | 27 +-
sql/sql_test.h | 2 +
sql/sql_type.h | 8 +-
sql/sql_view.cc | 10 +
sql/sys_vars.cc | 18 +
sql/table.cc | 2 +
sql/wsrep_mysqld.cc | 15 +-
sql/wsrep_server_state.cc | 1 +
sql/wsrep_sst.cc | 17 +-
storage/innobase/btr/btr0btr.cc | 115 +-
storage/innobase/btr/btr0bulk.cc | 13 +-
storage/innobase/btr/btr0cur.cc | 179 +-
storage/innobase/btr/btr0defragment.cc | 18 +-
storage/innobase/btr/btr0pcur.cc | 2 +-
storage/innobase/btr/btr0scrub.cc | 22 +-
storage/innobase/btr/btr0sea.cc | 2 +-
storage/innobase/buf/buf0buf.cc | 166 +-
storage/innobase/buf/buf0dblwr.cc | 82 +-
storage/innobase/buf/buf0dump.cc | 8 +-
storage/innobase/buf/buf0flu.cc | 11 +-
storage/innobase/buf/buf0lru.cc | 68 +-
storage/innobase/buf/buf0rea.cc | 99 +-
storage/innobase/data/data0data.cc | 8 +-
storage/innobase/dict/dict0boot.cc | 2 +-
storage/innobase/dict/dict0crea.cc | 24 +-
storage/innobase/dict/dict0dict.cc | 56 +-
storage/innobase/dict/dict0stats.cc | 4 +-
storage/innobase/fil/fil0crypt.cc | 103 +-
storage/innobase/fil/fil0fil.cc | 140 +-
storage/innobase/fil/fil0pagecompress.cc | 3 +-
storage/innobase/fsp/fsp0file.cc | 43 +-
storage/innobase/fsp/fsp0fsp.cc | 441 +--
storage/innobase/fts/fts0fts.cc | 4 +-
storage/innobase/fts/fts0que.cc | 8 +-
storage/innobase/fut/fut0lst.cc | 64 +-
storage/innobase/gis/gis0rtree.cc | 8 +-
storage/innobase/gis/gis0sea.cc | 11 +-
storage/innobase/handler/ha_innodb.cc | 81 +-
storage/innobase/handler/handler0alter.cc | 41 +-
storage/innobase/handler/i_s.cc | 13 +-
storage/innobase/ibuf/ibuf0ibuf.cc | 346 +-
storage/innobase/include/btr0btr.h | 57 +-
storage/innobase/include/btr0btr.ic | 14 +-
storage/innobase/include/btr0cur.h | 16 +-
storage/innobase/include/btr0types.h | 10 +-
storage/innobase/include/buf0buf.h | 56 +-
storage/innobase/include/buf0rea.h | 37 +-
storage/innobase/include/dict0dict.h | 34 +-
storage/innobase/include/dict0dict.ic | 22 -
storage/innobase/include/fil0crypt.h | 41 +-
storage/innobase/include/fil0fil.h | 59 +-
storage/innobase/include/fsp0fsp.h | 106 +-
storage/innobase/include/fsp0fsp.ic | 69 +-
storage/innobase/include/fut0fut.h | 28 +-
storage/innobase/include/fut0fut.ic | 68 -
storage/innobase/include/ibuf0ibuf.h | 75 +-
storage/innobase/include/ibuf0ibuf.ic | 49 +-
storage/innobase/include/mem0mem.ic | 4 +-
storage/innobase/include/mtr0types.h | 9 +-
storage/innobase/include/os0file.h | 2 +-
storage/innobase/include/page0size.h | 197 --
storage/innobase/include/page0zip.h | 15 +-
storage/innobase/include/page0zip.ic | 21 +-
storage/innobase/include/row0ext.h | 9 +-
storage/innobase/include/trx0rseg.ic | 5 +-
storage/innobase/include/trx0sys.h | 2 +-
storage/innobase/include/trx0undo.ic | 8 +-
storage/innobase/lock/lock0lock.cc | 2 +-
storage/innobase/log/log0log.cc | 8 +-
storage/innobase/log/log0recv.cc | 32 +-
storage/innobase/mtr/mtr0mtr.cc | 2 +-
storage/innobase/os/os0file.cc | 2 -
storage/innobase/page/page0zip.cc | 17 +-
storage/innobase/rem/rem0rec.cc | 8 +-
storage/innobase/row/row0ext.cc | 32 +-
storage/innobase/row/row0ftsort.cc | 4 +-
storage/innobase/row/row0import.cc | 119 +-
storage/innobase/row/row0log.cc | 18 +-
storage/innobase/row/row0merge.cc | 17 +-
storage/innobase/row/row0mysql.cc | 12 +-
storage/innobase/row/row0purge.cc | 2 +-
storage/innobase/row/row0row.cc | 4 +-
storage/innobase/row/row0sel.cc | 16 +-
storage/innobase/row/row0upd.cc | 39 +-
storage/innobase/srv/srv0srv.cc | 2 -
storage/innobase/srv/srv0start.cc | 13 +-
storage/innobase/trx/trx0rec.cc | 34 +-
storage/innobase/trx/trx0rseg.cc | 20 +-
storage/innobase/trx/trx0undo.cc | 8 +-
wsrep-lib | 2 +-
175 files changed, 10544 insertions(+), 2956 deletions(-)
diff --cc sql/sql_select.cc
index 38b0e7e,06205a4..1f12490
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@@ -64,7 -64,8 +64,9 @@@
#include "sys_vars_shared.h"
#include "sp_head.h"
#include "sp_rcontext.h"
+#include "select_handler.h"
+ #include "my_json_writer.h"
+ #include "opt_trace.h"
/*
A key part number that means we're using a fulltext scan.
1
0

[Commits] 27c3abd: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 13 Feb '19
by IgorBabaev 13 Feb '19
13 Feb '19
revision-id: 27c3abde3071ad2010cbcda5b07435ad15364a70 (mariadb-10.3.6-135-g27c3abd)
parent(s): 17d00d9a94da2c2b57fc7cf75036d92ee6dc9298
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 22:56:24 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
MDEV-17631 select_handler for a full query pushdown
Added comments and file headers for files introduced in these tasks.
---
sql/derived_handler.cc | 63 ++++++++++++++++++++++++++-----
sql/derived_handler.h | 30 +++++++++++++--
sql/select_handler.cc | 43 +++++++++++++++++++++
sql/select_handler.h | 28 +++++++++++++-
sql/sql_derived.cc | 21 +++++++++++
sql/sql_lex.h | 2 +
sql/sql_select.cc | 26 +++++++++++++
sql/table.h | 6 +++
storage/federatedx/federatedx_pushdown.cc | 19 ++++++++++
9 files changed, 223 insertions(+), 15 deletions(-)
diff --git a/sql/derived_handler.cc b/sql/derived_handler.cc
index 1fa5e94..76fd736 100644
--- a/sql/derived_handler.cc
+++ b/sql/derived_handler.cc
@@ -1,18 +1,41 @@
+/*
+ Copyright (c) 2018, 2019 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "derived_handler.h"
-void derived_handler::set_derived(TABLE_LIST *tbl)
-{
- derived= tbl;
- table= tbl->table;
- unit= tbl->derived;
- select= unit->first_select();
- tmp_table_param= select->next_select() ?
- ((select_unit *)(unit->result))->get_tmp_table_param() :
- &select->join->tmp_table_param;
-}
+
+/**
+ The methods of the Pushdown_derived class.
+
+ The objects of this class are used for pushdown of the derived tables
+ into engines. The main method of the class is Pushdown_derived::execute()
+ that initiates execution of the query specifying a derived by a foreign
+ engine, receives the rows of the result set and put them in a temporary
+ table on the server side.
+
+ The method uses only the functions of the derived_handle interface to do
+ this. The constructor of the class gets this interface as a parameter.
+
+ Currently a derived tables pushed into an engine is always materialized.
+ It could be changed if the cases when the tables is used as driving table.
+*/
+
Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
: derived(tbl), handler(h)
@@ -20,11 +43,13 @@ Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_derived::~Pushdown_derived()
{
delete handler;
}
+
int Pushdown_derived::execute()
{
int err;
@@ -82,3 +107,21 @@ int Pushdown_derived::execute()
DBUG_RETURN(-1); // Error not sent to client
}
+
+void derived_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
+
+
+void derived_handler::set_derived(TABLE_LIST *tbl)
+{
+ derived= tbl;
+ table= tbl->table;
+ unit= tbl->derived;
+ select= unit->first_select();
+ tmp_table_param= select->next_select() ?
+ ((select_unit *)(unit->result))->get_tmp_table_param() :
+ &select->join->tmp_table_param;
+}
+
diff --git a/sql/derived_handler.h b/sql/derived_handler.h
index c312a93..171165b 100644
--- a/sql/derived_handler.h
+++ b/sql/derived_handler.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2016, 2017 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 DERIVED_HANDLER_INCLUDED
#define DERIVED_HANDLER_INCLUDED
@@ -8,6 +24,13 @@ class TMP_TABLE_PARAM;
typedef class st_select_lex_unit SELECT_LEX_UNIT;
+/**
+ @class derived_handler
+
+ This interface class is to be used for execution of queries that specify
+ derived table by foreign engines
+*/
+
class derived_handler
{
public:
@@ -23,11 +46,12 @@ class derived_handler
*/
TABLE *table;
+ /* The parameters if the temporary table used at its creation */
TMP_TABLE_PARAM *tmp_table_param;
- SELECT_LEX_UNIT *unit;
+ SELECT_LEX_UNIT *unit; // Specifies the derived table
- SELECT_LEX *select;
+ SELECT_LEX *select; // The first select of the specification
derived_handler(THD *thd_arg, handlerton *ht_arg)
: thd(thd_arg), ht(ht_arg), derived(0),table(0), tmp_table_param(0),
@@ -53,7 +77,7 @@ class derived_handler
virtual int end_scan()=0;
/* Report errors */
- virtual void print_error(int error, myf errflag)=0;
+ virtual void print_error(int error, myf errflag);
void set_derived(TABLE_LIST *tbl);
};
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
index 9a8d391..f020d2f 100644
--- a/sql/select_handler.cc
+++ b/sql/select_handler.cc
@@ -1,21 +1,55 @@
+/*
+ Copyright (c) 2018, 2019 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "select_handler.h"
+/**
+ The methods of the Pushdown_select class.
+
+ The objects of this class are used for pushdown of the select queries
+ into engines. The main method of the class is Pushdown_select::execute()
+ that initiates execution of a select query by a foreign engine, receives the
+ rows of the result set, put it in a buffer of a temporary table and send
+ them from the buffer directly into output.
+
+ The method uses the functions of the select_handle interface to do this.
+ It also employes plus some helper functions to create the needed temporary
+ table and to send rows from the temporary table into output.
+ The constructor of the class gets the select_handler interface as a parameter.
+*/
+
+
Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h)
: select(sel), handler(h)
{
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_select::~Pushdown_select()
{
delete handler;
select->select_h= NULL;
}
+
bool Pushdown_select::init()
{
List<Item> types;
@@ -38,6 +72,7 @@ bool Pushdown_select::init()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_result_set_metadata()
{
THD *thd= handler->thd;
@@ -59,6 +94,7 @@ bool Pushdown_select::send_result_set_metadata()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_data()
{
THD *thd= handler->thd;
@@ -83,6 +119,7 @@ bool Pushdown_select::send_data()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_eof()
{
THD *thd= handler->thd;
@@ -98,6 +135,7 @@ bool Pushdown_select::send_eof()
DBUG_RETURN(false);
}
+
int Pushdown_select::execute()
{
int err;
@@ -143,3 +181,8 @@ int Pushdown_select::execute()
handler->print_error(err, MYF(0));
DBUG_RETURN(-1); // Error not sent to client
}
+
+void select_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
diff --git a/sql/select_handler.h b/sql/select_handler.h
index 19a1883..e2ad13b 100644
--- a/sql/select_handler.h
+++ b/sql/select_handler.h
@@ -1,20 +1,44 @@
+/*
+ Copyright (c) 2018, 2019 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 SELECT_HANDLER_INCLUDED
#define SELECT_HANDLER_INCLUDED
#include "mariadb.h"
#include "sql_priv.h"
+/**
+ @class select_handler
+
+ This interface class is to be used for execution of select queries
+ by foreign engines
+*/
+
class select_handler
{
public:
THD *thd;
handlerton *ht;
- SELECT_LEX *select;
+ SELECT_LEX *select; // Select to be excuted
/*
Temporary table where all results should be stored in record[0]
The table has a field for every item from the select_lex::item_list.
+ The table is actually never filled. Only its record buffer is used.
*/
TABLE *table;
@@ -42,7 +66,7 @@ class select_handler
virtual int end_scan() = 0;
/* Report errors */
- virtual void print_error(int error, myf errflag) = 0;
+ virtual void print_error(int error, myf errflag);
};
#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 564049e..fbd8365 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -932,6 +932,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->is_materialized_derived() && derived->dt_handler)
{
+ /* Create an object for execution of the query specifying the table */
if (!(derived->pushdown_derived=
new (thd->mem_root) Pushdown_derived(derived, derived->dt_handler)))
{
@@ -1151,6 +1152,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
int res;
if (unit->executed)
DBUG_RETURN(FALSE);
+ /* Execute the query that specifies the derived table by a foreign engine */
res= derived->pushdown_derived->execute();
unit->executed= true;
delete derived->pushdown_derived;
@@ -1457,6 +1459,25 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
}
+/**
+ @brief
+ Look for provision of the derived_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function looks through its tables of the query that specifies this
+ derived table searching for a table whose handlerton owns a
+ create_derived call-back function. If the call of this function returns
+ a derived_handler interface object then the server will push the query
+ specifying the derived table into this engine.
+ This is a responsibility of the create_derived call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found derived_handler if the search is successful
+ 0 otherwise
+*/
+
derived_handler *TABLE_LIST::find_derived_handler(THD *thd)
{
if (!derived || is_recursive_with_table())
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c33307c..25e8ad3 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1257,7 +1257,9 @@ class st_select_lex: public st_select_lex_node
table_value_constr *tvc;
bool in_tvc;
+ /* The interface employed to execute the select query by a foreign engine */
select_handler *select_h;
+ /* The object used to organize execution of the query by a foreign engine */
Pushdown_select *pushdown_select;
/** System Versioning */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index e9040e1..38b0e7e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1444,7 +1444,10 @@ int JOIN::optimize()
if (select_lex->pushdown_select)
{
if (!(select_options & SELECT_DESCRIBE))
+ {
+ /* Prepare to execute the query pushed into a foreign engine */
res= select_lex->pushdown_select->init();
+ }
with_two_phase_optimization= false;
}
else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
@@ -4074,6 +4077,7 @@ void JOIN::exec_inner()
}
else if (select_lex->pushdown_select)
{
+ /* Execute the query pushed into a foreign engine */
error= select_lex->pushdown_select->execute();
DBUG_VOID_RETURN;
}
@@ -4288,9 +4292,11 @@ mysql_select(THD *thd,
}
}
+ /* Look for a table owned by an engine with the select_handler interface */
select_lex->select_h= select_lex->find_select_handler(thd);
if (select_lex->select_h)
{
+ /* Create a Pushdown_select object for later execution of the query */
if (!(select_lex->pushdown_select=
new (thd->mem_root) Pushdown_select(select_lex,
select_lex->select_h)))
@@ -27620,6 +27626,26 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
return cond;
}
+
+/**
+ @brief
+ Look for provision of the select_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function checks that this is an upper level select and if so looks
+ through its tables searching for one whose handlerton owns a
+ create_select call-back function. If the call of this function returns
+ a select_handler interface object then the server will push the select
+ query into this engine.
+ This is a responsibility of the create_select call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found select_handler if the search is successful
+ 0 otherwise
+*/
+
select_handler *SELECT_LEX::find_select_handler(THD *thd)
{
if (next_select())
diff --git a/sql/table.h b/sql/table.h
index f7bcdaa..40dd752 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2136,8 +2136,14 @@ struct TABLE_LIST
TABLE_LIST * next_with_rec_ref;
bool is_derived_with_recursive_reference;
bool block_handle_derived;
+ /* The interface employed to materialize the table by a foreign engine */
derived_handler *dt_handler;
+ /* The text of the query specifying the derived table */
LEX_CSTRING derived_spec;
+ /*
+ The object used to organize execution of the query that specifies
+ the derived table by a foreign engine
+ */
Pushdown_derived *pushdown_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc
index 906d87c..2bcee94 100644
--- a/storage/federatedx/federatedx_pushdown.cc
+++ b/storage/federatedx/federatedx_pushdown.cc
@@ -16,6 +16,25 @@
/* !!! For inclusion into ha_federatedx.cc */
+
+/*
+ This is a quick a dirty implemention of the derived_handler and select_handler
+ interfaces to be used to push select queries and the queries specifying
+ derived tables into FEDERATEDX engine.
+ The functions
+ create_federatedx_derived_handler and
+ create_federatedx_select_handler
+ that return the corresponding interfaces for pushdown capabilities do
+ not check a lot of things. In particular they do not check that the tables
+ of the pushed queries belong to the same foreign server.
+
+ The implementation is provided purely for testing purposes.
+ The pushdown capabilities are enabled by turning on the plugin system
+ variable federated_pushdown:
+ set global federated_pushdown=1;
+*/
+
+
static derived_handler*
create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
{
1
0

[Commits] 536711f: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 13 Feb '19
by IgorBabaev 13 Feb '19
13 Feb '19
revision-id: 536711f6724fefeb8fecc69a8b9befde2c72cc0e (mariadb-10.3.6-135-g536711f)
parent(s): 17d00d9a94da2c2b57fc7cf75036d92ee6dc9298
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 22:43:17 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
MDEV-17631 select_handler for a full query pushdown
Added comments and file headers for files introduced in these tasks.
---
sql/derived_handler.cc | 63 ++++++++++++++++++++++++++-----
sql/derived_handler.h | 32 ++++++++++++++--
sql/select_handler.cc | 43 +++++++++++++++++++++
sql/select_handler.h | 28 +++++++++++++-
sql/sql_derived.cc | 21 +++++++++++
sql/sql_lex.h | 4 +-
sql/sql_select.cc | 26 +++++++++++++
sql/table.h | 6 +++
storage/federatedx/federatedx_pushdown.cc | 19 ++++++++++
9 files changed, 225 insertions(+), 17 deletions(-)
diff --git a/sql/derived_handler.cc b/sql/derived_handler.cc
index 1fa5e94..5ef2f07 100644
--- a/sql/derived_handler.cc
+++ b/sql/derived_handler.cc
@@ -1,18 +1,41 @@
+/*
+ Copyright (c) 2018, 2019 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "derived_handler.h"
-void derived_handler::set_derived(TABLE_LIST *tbl)
-{
- derived= tbl;
- table= tbl->table;
- unit= tbl->derived;
- select= unit->first_select();
- tmp_table_param= select->next_select() ?
- ((select_unit *)(unit->result))->get_tmp_table_param() :
- &select->join->tmp_table_param;
-}
+
+/**
+ The methods of the Pushdown_derived class.
+
+ The objects of this class are used for pushdown of the derived tables
+ into engines. The main method of the class is Pushdown_derived::execute()
+ that initiates execution of the query specifying a derived by a foreign
+ engine, receives the rows of the result set and put them in a temporary
+ table on the server side.
+
+ The method uses only the functions of the derived_handle interface to do
+ this. The constructor of the class gets this interface as a parameter.
+
+ Currently a derived tables pushed into an engine is always materialized.
+ It could be changed if the cases when the tables is used as driving table.
+*/
+
Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
: derived(tbl), handler(h)
@@ -20,11 +43,13 @@ Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_derived::~Pushdown_derived()
{
delete handler;
}
+
int Pushdown_derived::execute()
{
int err;
@@ -82,3 +107,21 @@ int Pushdown_derived::execute()
DBUG_RETURN(-1); // Error not sent to client
}
+
+void derived_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
+
+
+void derived_handler::set_derived(TABLE_LIST *tbl)
+{
+ derived= tbl;
+ table= tbl->table;
+ unit= tbl->derived;
+ select= unit->first_select();
+ tmp_table_param= select->next_select() ?
+ ((select_unit *)(unit->result))->get_tmp_table_param() :
+ &select->join->tmp_table_param;
+}
+
diff --git a/sql/derived_handler.h b/sql/derived_handler.h
index c312a93..b1fa1ce 100644
--- a/sql/derived_handler.h
+++ b/sql/derived_handler.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2016, 2017 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 DERIVED_HANDLER_INCLUDED
#define DERIVED_HANDLER_INCLUDED
@@ -8,6 +24,13 @@ class TMP_TABLE_PARAM;
typedef class st_select_lex_unit SELECT_LEX_UNIT;
+/**
+ @class derived_handler
+
+ This interface class is to be used for execution of queries that specify
+ derived table by foreign engines
+*/
+
class derived_handler
{
public:
@@ -23,11 +46,12 @@ class derived_handler
*/
TABLE *table;
- TMP_TABLE_PARAM *tmp_table_param;
+ /* The parameters if the temporary table used at its creation */
+ TMP_TABLE_PARAM *tmp_table_param;
- SELECT_LEX_UNIT *unit;
+ SELECT_LEX_UNIT *unit; // Specifies the derived table
- SELECT_LEX *select;
+ SELECT_LEX *select; // The first select of the specification
derived_handler(THD *thd_arg, handlerton *ht_arg)
: thd(thd_arg), ht(ht_arg), derived(0),table(0), tmp_table_param(0),
@@ -53,7 +77,7 @@ class derived_handler
virtual int end_scan()=0;
/* Report errors */
- virtual void print_error(int error, myf errflag)=0;
+ virtual void print_error(int error, myf errflag);
void set_derived(TABLE_LIST *tbl);
};
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
index 9a8d391..178ae6c 100644
--- a/sql/select_handler.cc
+++ b/sql/select_handler.cc
@@ -1,21 +1,55 @@
+/*
+ Copyright (c) 2018, 2019 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "select_handler.h"
+/**
+ The methods of the Pushdown_select class.
+
+ The objects of this class are used for pushdown of the select queries
+ into engines. The main method of the class is Pushdown_select::execute()
+ that initiates execution of a select query by a foreign engine, receives the
+ rows of the result set, put it in a buffer of a temporary table and send
+ them from the buffer directly into output.
+
+ The method uses the functions of the select_handle interface to do this.
+ It also employes plus some helper functions to create the needed temporary
+ table and to send rows from the temporary table into output.
+ The constructor of the class gets the select_handler interface as a parameter.
+*/
+
+
Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h)
: select(sel), handler(h)
{
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_select::~Pushdown_select()
{
delete handler;
select->select_h= NULL;
}
+
bool Pushdown_select::init()
{
List<Item> types;
@@ -38,6 +72,7 @@ bool Pushdown_select::init()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_result_set_metadata()
{
THD *thd= handler->thd;
@@ -59,6 +94,7 @@ bool Pushdown_select::send_result_set_metadata()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_data()
{
THD *thd= handler->thd;
@@ -83,6 +119,7 @@ bool Pushdown_select::send_data()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_eof()
{
THD *thd= handler->thd;
@@ -98,6 +135,7 @@ bool Pushdown_select::send_eof()
DBUG_RETURN(false);
}
+
int Pushdown_select::execute()
{
int err;
@@ -143,3 +181,8 @@ int Pushdown_select::execute()
handler->print_error(err, MYF(0));
DBUG_RETURN(-1); // Error not sent to client
}
+
+void select_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
diff --git a/sql/select_handler.h b/sql/select_handler.h
index 19a1883..bb7c9a8 100644
--- a/sql/select_handler.h
+++ b/sql/select_handler.h
@@ -1,20 +1,44 @@
+/*
+ Copyright (c) 2018, 2019 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 SELECT_HANDLER_INCLUDED
#define SELECT_HANDLER_INCLUDED
#include "mariadb.h"
#include "sql_priv.h"
+/**
+ @class select_handler
+
+ This interface class is to be used for execution of select queries
+ by foreign engines
+*/
+
class select_handler
{
public:
THD *thd;
handlerton *ht;
- SELECT_LEX *select;
+ SELECT_LEX *select; // Select to be excuted
/*
Temporary table where all results should be stored in record[0]
The table has a field for every item from the select_lex::item_list.
+ The table is actually never filled. Only its record buffer is used.
*/
TABLE *table;
@@ -42,7 +66,7 @@ class select_handler
virtual int end_scan() = 0;
/* Report errors */
- virtual void print_error(int error, myf errflag) = 0;
+ virtual void print_error(int error, myf errflag);
};
#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 564049e..2db42b7 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -932,6 +932,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->is_materialized_derived() && derived->dt_handler)
{
+ /* Create an object for execution of the query specifying the table */
if (!(derived->pushdown_derived=
new (thd->mem_root) Pushdown_derived(derived, derived->dt_handler)))
{
@@ -1151,6 +1152,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
int res;
if (unit->executed)
DBUG_RETURN(FALSE);
+ /* Execute the query that specifies the derived table by a foreign engine */
res= derived->pushdown_derived->execute();
unit->executed= true;
delete derived->pushdown_derived;
@@ -1457,6 +1459,25 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
}
+/**
+ @brief
+ Look for provision of the derived_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function looks through its tables of the query that specifies this
+ derived table searching for a table whose handlerton owns a
+ create_derived call-back function. If the call of this function returns
+ a derived_handler interface object then the server will push the query
+ specifying the derived table into this engine.
+ This is a responsibility of the create_derived call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found derived_handler if the search is successful
+ 0 otherwise
+*/
+
derived_handler *TABLE_LIST::find_derived_handler(THD *thd)
{
if (!derived || is_recursive_with_table())
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c33307c..124f619 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1257,8 +1257,10 @@ class st_select_lex: public st_select_lex_node
table_value_constr *tvc;
bool in_tvc;
+ /* The interface employed to execute the select query by a foreign engine */
select_handler *select_h;
- Pushdown_select *pushdown_select;
+ /* The object used to organize execution of the query by a foreign engine */
+ Pushdown_select *pushdown_select;
/** System Versioning */
public:
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index e9040e1..cabc617 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1444,7 +1444,10 @@ int JOIN::optimize()
if (select_lex->pushdown_select)
{
if (!(select_options & SELECT_DESCRIBE))
+ {
+ /* Prepare to execute the query pushed into a foreign engine */
res= select_lex->pushdown_select->init();
+ }
with_two_phase_optimization= false;
}
else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
@@ -4074,6 +4077,7 @@ void JOIN::exec_inner()
}
else if (select_lex->pushdown_select)
{
+ /* Execute the query pushed into a foreign engine */
error= select_lex->pushdown_select->execute();
DBUG_VOID_RETURN;
}
@@ -4288,9 +4292,11 @@ mysql_select(THD *thd,
}
}
+ /* Look for a table owned by an engine with the select_handler interface */
select_lex->select_h= select_lex->find_select_handler(thd);
if (select_lex->select_h)
{
+ /* Create a Pushdown_select object for later execution of the query */
if (!(select_lex->pushdown_select=
new (thd->mem_root) Pushdown_select(select_lex,
select_lex->select_h)))
@@ -27620,6 +27626,26 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
return cond;
}
+
+/**
+ @brief
+ Look for provision of the select_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function checks that this is an upper level select and if so looks
+ through its tables searching for one whose handlerton owns a
+ create_select call-back function. If the call of this function returns
+ a select_handler interface object then the server will push the select
+ query into this engine.
+ This is a responsibility of the create_select call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found select_handler if the search is successful
+ 0 otherwise
+*/
+
select_handler *SELECT_LEX::find_select_handler(THD *thd)
{
if (next_select())
diff --git a/sql/table.h b/sql/table.h
index f7bcdaa..0d78130 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2136,8 +2136,14 @@ struct TABLE_LIST
TABLE_LIST * next_with_rec_ref;
bool is_derived_with_recursive_reference;
bool block_handle_derived;
+ /* The interface employed to materialize the table by a foreign engine */
derived_handler *dt_handler;
+ /* The text of the query specifying the derived table */
LEX_CSTRING derived_spec;
+ /*
+ The object used to organize execution of the query that specifies
+ the derived table by a foreign engine
+ */
Pushdown_derived *pushdown_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc
index 906d87c..6da22ec 100644
--- a/storage/federatedx/federatedx_pushdown.cc
+++ b/storage/federatedx/federatedx_pushdown.cc
@@ -16,6 +16,25 @@
/* !!! For inclusion into ha_federatedx.cc */
+
+/*
+ This is a quick a dirty implemention of the derived_handler and select_handler
+ interfaces to be used to push select queries and the queries specifying
+ derived tables into FEDERATEDX engine.
+ The functions
+ create_federatedx_derived_handler and
+ create_federatedx_select_handler
+ that return the corresponding interfaces for pushdown capabilities do
+ not check a lot of things. In particular they do not check that the tables
+ of the pushed queries belong to the same foreign server.
+
+ The implementation is provided purely for testing purposes.
+ The pushdown capabilities are enabled by turning on the plugin system
+ variable federated_pushdown:
+ set global federated_pushdown=1;
+*/
+
+
static derived_handler*
create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
{
1
0

[Commits] ce709b82efe: MDEV-18564: Change wsrep_load_data_splitting off by default
by jan 13 Feb '19
by jan 13 Feb '19
13 Feb '19
revision-id: ce709b82efe465b87b5f839b23b933893ea7685a (mariadb-10.4.1-166-gce709b82efe)
parent(s): 3a269a8b5272c15751f565daa1566d1f61a1f04f
author: Jan Lindström
committer: Jan Lindström
timestamp: 2019-02-13 08:39:44 +0200
message:
MDEV-18564: Change wsrep_load_data_splitting off by default
Variable wsrep_load_data_splitting is deprecated and should be off
by default.
---
mysql-test/suite/galera/r/galera_defaults.result | 2 +-
mysql-test/suite/galera/r/galera_var_load_data_splitting.result | 6 +++++-
mysql-test/suite/galera/r/partition.result | 2 +-
mysql-test/suite/galera/t/galera_var_load_data_splitting.test | 3 +++
mysql-test/suite/sys_vars/r/sysvars_wsrep.result | 4 ++--
mysql-test/suite/sys_vars/r/wsrep_load_data_splitting_basic.result | 4 ++--
sql/sys_vars.cc | 2 +-
7 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result
index 20ea590e2e5..4aef4f5e48c 100644
--- a/mysql-test/suite/galera/r/galera_defaults.result
+++ b/mysql-test/suite/galera/r/galera_defaults.result
@@ -34,7 +34,7 @@ WSREP_FORCED_BINLOG_FORMAT NONE
WSREP_GTID_DOMAIN_ID 0
WSREP_GTID_MODE OFF
WSREP_IGNORE_APPLY_ERRORS 7
-WSREP_LOAD_DATA_SPLITTING ON
+WSREP_LOAD_DATA_SPLITTING OFF
WSREP_LOG_CONFLICTS OFF
WSREP_MAX_WS_ROWS 0
WSREP_MAX_WS_SIZE 2147483647
diff --git a/mysql-test/suite/galera/r/galera_var_load_data_splitting.result b/mysql-test/suite/galera/r/galera_var_load_data_splitting.result
index 9078e9ea985..66bc6bc4a9a 100644
--- a/mysql-test/suite/galera/r/galera_var_load_data_splitting.result
+++ b/mysql-test/suite/galera/r/galera_var_load_data_splitting.result
@@ -12,7 +12,11 @@ COUNT(*) = 95000
wsrep_last_committed_diff
1
connection node_1;
-SET GLOBAL wsrep_load_data_splitting = 1;;
+SET GLOBAL wsrep_load_data_splitting = 0;;
+Warnings:
+Warning 1287 '@@wsrep_load_data_splitting' is deprecated and will be removed in a future release
+connection node_2;
+SET GLOBAL wsrep_load_data_splitting = 0;;
Warnings:
Warning 1287 '@@wsrep_load_data_splitting' is deprecated and will be removed in a future release
DROP TABLE t1;
diff --git a/mysql-test/suite/galera/r/partition.result b/mysql-test/suite/galera/r/partition.result
index 213f9dfdbab..2e16d06519c 100644
--- a/mysql-test/suite/galera/r/partition.result
+++ b/mysql-test/suite/galera/r/partition.result
@@ -159,7 +159,7 @@ wsrep_last_committed_diff
AS_EXPECTED_1_or_2
DROP TABLE t1;
connection node_1;
-SET GLOBAL wsrep_load_data_splitting = 1;;
+SET GLOBAL wsrep_load_data_splitting = 0;;
Warnings:
Warning 1287 '@@wsrep_load_data_splitting' is deprecated and will be removed in a future release
disconnect node_2;
diff --git a/mysql-test/suite/galera/t/galera_var_load_data_splitting.test b/mysql-test/suite/galera/t/galera_var_load_data_splitting.test
index 38dab0a981b..621cb69fc16 100644
--- a/mysql-test/suite/galera/t/galera_var_load_data_splitting.test
+++ b/mysql-test/suite/galera/t/galera_var_load_data_splitting.test
@@ -36,4 +36,7 @@ SELECT COUNT(*) = 95000 FROM t1;
--connection node_1
--eval SET GLOBAL wsrep_load_data_splitting = $wsrep_load_data_splitting_orig;
+--connection node_2
+--eval SET GLOBAL wsrep_load_data_splitting = $wsrep_load_data_splitting_orig;
+
DROP TABLE t1;
diff --git a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result
index dd3e70c587d..49eaaa965dc 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result
@@ -227,9 +227,9 @@ READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME WSREP_LOAD_DATA_SPLITTING
SESSION_VALUE NULL
-GLOBAL_VALUE ON
+GLOBAL_VALUE OFF
GLOBAL_VALUE_ORIGIN COMPILE-TIME
-DEFAULT_VALUE ON
+DEFAULT_VALUE OFF
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT To commit LOAD DATA transaction after every 10K rows inserted (deprecated)
diff --git a/mysql-test/suite/sys_vars/r/wsrep_load_data_splitting_basic.result b/mysql-test/suite/sys_vars/r/wsrep_load_data_splitting_basic.result
index d373a3832d5..3171a690486 100644
--- a/mysql-test/suite/sys_vars/r/wsrep_load_data_splitting_basic.result
+++ b/mysql-test/suite/sys_vars/r/wsrep_load_data_splitting_basic.result
@@ -6,7 +6,7 @@ SET @wsrep_load_data_splitting_global_saved = @@global.wsrep_load_data_splitting
# default
SELECT @@global.wsrep_load_data_splitting;
@@global.wsrep_load_data_splitting
-1
+0
# scope
SELECT @@session.wsrep_load_data_splitting;
@@ -42,7 +42,7 @@ Warnings:
Warning 1287 '@@wsrep_load_data_splitting' is deprecated and will be removed in a future release
SELECT @@global.wsrep_load_data_splitting;
@@global.wsrep_load_data_splitting
-1
+0
# invalid values
SET @@global.wsrep_load_data_splitting=NULL;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 7241685fb61..fd5ad8c9a5d 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -5522,7 +5522,7 @@ static Sys_var_mybool Sys_wsrep_load_data_splitting(
"wsrep_load_data_splitting", "To commit LOAD DATA "
"transaction after every 10K rows inserted (deprecated)",
GLOBAL_VAR(wsrep_load_data_splitting),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ CMD_LINE(OPT_ARG), DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
static Sys_var_mybool Sys_wsrep_slave_FK_checks(
1
0

[Commits] 17d00d9: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 12 Feb '19
by IgorBabaev 12 Feb '19
12 Feb '19
revision-id: 17d00d9a94da2c2b57fc7cf75036d92ee6dc9298 (mariadb-10.3.6-134-g17d00d9)
parent(s): 58b950737c547df42c82744029cbcad235e0efd9
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 14:00:48 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
Made the setting of the system variable federated_pushdown at
the default connection.
---
mysql-test/suite/federated/federatedx_create_handlers.result | 3 +++
mysql-test/suite/federated/federatedx_create_handlers.test | 5 ++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/mysql-test/suite/federated/federatedx_create_handlers.result b/mysql-test/suite/federated/federatedx_create_handlers.result
index a25b019..473972c 100644
--- a/mysql-test/suite/federated/federatedx_create_handlers.result
+++ b/mysql-test/suite/federated/federatedx_create_handlers.result
@@ -4,6 +4,8 @@ connection master;
CREATE DATABASE federated;
connection slave;
CREATE DATABASE federated;
+connection default;
+set global federated_pushdown=1;
connection slave;
DROP TABLE IF EXISTS federated.t1;
Warnings:
@@ -301,6 +303,7 @@ DROP TABLE federated.t1, federated.t2, federated.t3;
connection slave;
DROP TABLE federated.t1, federated.t2;
connection default;
+set global federated_pushdown=0;
connection master;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated;
diff --git a/mysql-test/suite/federated/federatedx_create_handlers.test b/mysql-test/suite/federated/federatedx_create_handlers.test
index 558b846..373b2aa 100644
--- a/mysql-test/suite/federated/federatedx_create_handlers.test
+++ b/mysql-test/suite/federated/federatedx_create_handlers.test
@@ -1,6 +1,8 @@
--source have_federatedx.inc
--source include/federated.inc
+connection default;
+
set global federated_pushdown=1;
connection slave;
@@ -153,6 +155,7 @@ DROP TABLE federated.t1, federated.t2;
connection default;
+set global federated_pushdown=0;
+
source include/federated_cleanup.inc;
-set global federated_pushdown=0;
1
0