summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorNirbhay Choubey <nirbhay@skysql.com>2014-04-08 10:36:34 -0400
committerNirbhay Choubey <nirbhay@skysql.com>2014-04-08 10:36:34 -0400
commit421326310168e2b0a83eddcf9520336e1d58ea42 (patch)
tree9ec49ac74c451cf03581a619eb92a7afae1c5eef /sql
parent9d2e90f379654fb65c0eab006213d772302bcff7 (diff)
parent41a2ca5c16636c12d5c2adce70ec7ddb7a2fc711 (diff)
downloadmariadb-git-421326310168e2b0a83eddcf9520336e1d58ea42.tar.gz
Merging mariadb-10.0.10.
* bzr merge -rtag:mariadb-10.0.10 maria/10.0.
Diffstat (limited to 'sql')
-rw-r--r--sql/authors.h11
-rw-r--r--sql/contributors.h12
-rw-r--r--sql/create_options.cc1
-rw-r--r--sql/events.cc3
-rw-r--r--sql/field.cc13
-rw-r--r--sql/filesort.cc10
-rw-r--r--sql/filesort_utils.h2
-rw-r--r--sql/ha_partition.cc146
-rw-r--r--sql/ha_partition.h21
-rw-r--r--sql/handler.h14
-rw-r--r--sql/hash_filo.h13
-rw-r--r--sql/hostname.cc8
-rw-r--r--sql/item.cc37
-rw-r--r--sql/item.h20
-rw-r--r--sql/item_func.cc35
-rw-r--r--sql/item_strfunc.cc7
-rw-r--r--sql/item_strfunc.h3
-rw-r--r--sql/item_xmlfunc.cc135
-rw-r--r--sql/item_xmlfunc.h55
-rw-r--r--sql/lock.cc5
-rw-r--r--sql/log.cc32
-rw-r--r--sql/log.h1
-rw-r--r--sql/log_event.cc320
-rw-r--r--sql/log_event.h32
-rw-r--r--sql/mdl.cc7
-rw-r--r--sql/mdl.h16
-rw-r--r--sql/mysqld.cc17
-rw-r--r--sql/mysqld.h9
-rw-r--r--sql/opt_range.cc5
-rw-r--r--sql/partition_info.cc4
-rw-r--r--sql/rpl_gtid.cc189
-rw-r--r--sql/rpl_gtid.h23
-rw-r--r--sql/rpl_parallel.cc109
-rw-r--r--sql/rpl_rli.cc13
-rw-r--r--sql/rpl_rli.h16
-rw-r--r--sql/rpl_utility.h1
-rw-r--r--sql/slave.cc85
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sp_head.h2
-rw-r--r--sql/sql_acl.cc161
-rw-r--r--sql/sql_admin.cc11
-rw-r--r--sql/sql_analyse.cc55
-rw-r--r--sql/sql_analyse.h2
-rw-r--r--sql/sql_audit.cc4
-rw-r--r--sql/sql_audit.h12
-rw-r--r--sql/sql_base.cc87
-rw-r--r--sql/sql_class.cc33
-rw-r--r--sql/sql_class.h30
-rw-r--r--sql/sql_db.cc87
-rw-r--r--sql/sql_delete.cc35
-rw-r--r--sql/sql_derived.cc50
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_insert.cc118
-rw-r--r--sql/sql_insert.h1
-rw-r--r--sql/sql_list.h9
-rw-r--r--sql/sql_load.cc4
-rw-r--r--sql/sql_parse.cc48
-rw-r--r--sql/sql_partition.cc4
-rw-r--r--sql/sql_repl.cc363
-rw-r--r--sql/sql_select.cc86
-rw-r--r--sql/sql_select.h9
-rw-r--r--sql/sql_servers.cc1
-rw-r--r--sql/sql_show.cc35
-rw-r--r--sql/sql_show.h4
-rw-r--r--sql/sql_statistics.cc125
-rw-r--r--sql/sql_statistics.h31
-rw-r--r--sql/sql_string.cc2
-rw-r--r--sql/sql_string.h14
-rw-r--r--sql/sql_table.cc296
-rw-r--r--sql/sql_table.h7
-rw-r--r--sql/sql_test.cc15
-rw-r--r--sql/sql_time.cc3
-rw-r--r--sql/sql_truncate.cc9
-rw-r--r--sql/sql_update.cc134
-rw-r--r--sql/sql_view.cc2
-rw-r--r--sql/sys_vars.cc78
-rw-r--r--sql/table.cc109
-rw-r--r--sql/table.h22
-rw-r--r--sql/table_cache.cc134
-rw-r--r--sql/table_cache.h1
-rw-r--r--sql/unireg.cc87
81 files changed, 2597 insertions, 1160 deletions
diff --git a/sql/authors.h b/sql/authors.h
index 406715ab074..cc9889bcdbc 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -35,6 +35,8 @@ struct show_table_authors_st {
then, not active last.
Names should be encoded using UTF-8.
+
+ See also https://mariadb.com/kb/en/log-of-mariadb-contributions/
*/
struct show_table_authors_st show_table_authors[]= {
@@ -63,7 +65,14 @@ struct show_table_authors_st show_table_authors[]= {
{ "Georg Richter", "Heidelberg, Germany", "New LGPL C connector, PHP connector"},
{ "Jan Lindström", "Ylämylly, Finland", "Working on InnoDB"},
{ "Lixun Peng", "Hangzhou, China", "Multi Source replication" },
+ { "Olivier Bertrand", "Paris, France", "CONNECT storage engine"},
+ { "Kentoku Shiba", "Tokyo, Japan", "Spider storage engine, metadata_lock_info Information schema"},
{ "Percona", "CA, USA", "XtraDB, microslow patches, extensions to slow log"},
+ { "Vicentiu Ciorbaru", "Bucharest, Romania", "Roles"},
+ { "Sudheera Palihakkara", "", "PCRE Regular Expressions" },
+ { "Pavel Ivanov", "USA", "Some patches and bug fixes"},
+ { "Konstantin Osipov", "Moscow, Russia",
+ "Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" },
/* People working on MySQL code base (not NDB) */
{ "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
@@ -82,8 +91,6 @@ struct show_table_authors_st show_table_authors[]= {
"MySQL founder; Small stuff long time ago, Monty ripped it out!" },
{ "Brian (Krow) Aker", "Seattle, WA, USA",
"Architecture, archive, blackhole, federated, bunch of little stuff :)" },
- { "Konstantin Osipov", "Moscow, Russia",
- "Prepared statements (4.1), Cursors (5.0)" },
{ "Venu Anuganti", "", "Client/server protocol (4.1)" },
{ "Omer BarNir", "Sunnyvale, CA, USA",
"Testing (sometimes) and general QA stuff" },
diff --git a/sql/contributors.h b/sql/contributors.h
index 1859c5d48c5..2479f611727 100644
--- a/sql/contributors.h
+++ b/sql/contributors.h
@@ -31,19 +31,29 @@ struct show_table_contributors_st {
Get permission before editing.
Names should be encoded using UTF-8.
+
+ See also https://mariadb.com/kb/en/log-of-mariadb-contributions/
*/
struct show_table_contributors_st show_table_contributors[]= {
- /* MariaDB foundation members, in contribution size order */
+ /* MariaDB foundation members, in contribution, size , time order */
{"Booking.com", "http://www.booking.com", "Founding member of the MariaDB foundation"},
{"SkySQL Ab", "http://www.skysql.com", "Founding member of the MariaDB foundation"},
+ {"Auttomatic", "http://automattic.com", "Member of the MariaDB foundation"},
{"Parallels", "http://www.parallels.com/products/plesk", "Founding member of the MariaDB foundation"},
/* Smaller sponsors, newer per year */
+ {"Verkkokauppa.com", "Finland", "Sponsor of the MariaDB foundation"},
+ {"Webyog", "Bangalor", "Sponsor of the MariaDB foundation"},
+ {"Percona", "USA", "Sponsor of the MariaDB foundation"},
{"Jelastic.com", "Russia", "Sponsor of the MariaDB foundation"},
{"Planetta.net", "Finland", "Sponsor of the MariaDB foundation"},
{"Open query", "Australia", "Sponsor of the MariaDB foundation"},
+ /* Sponsors of important features */
+ {"Google", "USA", "Sponsoring parallel replication and GTID" },
+ {"Facebook", "USA", "Sponsoring non-blocking API, LIMIT ROWS EXAMINED etc"},
+
/* Individual contributors, names in historical order, newer first */
{"Ronald Bradford", "Brisbane, Australia", "EFF contribution for UC2006 Auction"},
{"Sheeri Kritzer", "Boston, Mass. USA", "EFF contribution for UC2006 Auction"},
diff --git a/sql/create_options.cc b/sql/create_options.cc
index d9b1d31fd62..d60639a4f4a 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -395,6 +395,7 @@ static bool resolve_sysvars(handlerton *hton, ha_create_table_option *rules)
char buf[256];
String str(buf, sizeof(buf), system_charset_info);
+ str.length(0);
for (const char **s= optp.typelib->type_names; *s; s++)
{
if (str.append(*s) || str.append(','))
diff --git a/sql/events.cc b/sql/events.cc
index 1e4b56c08e0..944e91a4d21 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -764,6 +764,9 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
NULL, NULL, 0, 0))
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
+
+ if (lower_case_table_names)
+ my_casedn_str(system_charset_info, db);
}
ret= db_repository->fill_schema_events(thd, tables, db);
diff --git a/sql/field.cc b/sql/field.cc
index 922c9aba6c5..68617d0204e 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1175,6 +1175,15 @@ inline ulonglong char_prefix_to_ulonglong(uchar *src)
return uint8korr(src);
}
+/*
+ Compute res = a - b, without losing precision and taking care that these are
+ unsigned numbers.
+*/
+static inline double safe_substract(ulonglong a, ulonglong b)
+{
+ return (a > b)? double(a - b) : -double(b - a);
+}
+
/**
@brief
@@ -1227,10 +1236,10 @@ double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
minp= char_prefix_to_ulonglong(minp_prefix);
maxp= char_prefix_to_ulonglong(maxp_prefix);
double n, d;
- n= mp - minp;
+ n= safe_substract(mp, minp);
if (n < 0)
return 0.0;
- d= maxp - minp;
+ d= safe_substract(maxp, minp);
if (d <= 0)
return 1.0;
return MY_MIN(n/d, 1.0);
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 12b9bb5aadc..776ec064365 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -188,7 +188,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
- *found_rows= HA_POS_ERROR;
param.init_for_filesort(sortlength(thd, sortorder, s_length,
&multi_byte_charset),
@@ -687,7 +686,7 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
- *found_rows= pq ? 0 : HA_POS_ERROR; // don't count unless pq is used
+ *found_rows= 0;
flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
if (flag)
ref_pos= &file->ref[0];
@@ -807,14 +806,9 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
if (write_record)
{
+ (*found_rows)++;
if (pq)
{
- /*
- only count rows when pq is used - otherwise there might be
- other filters *after* the filesort, we don't know the final row
- count here
- */
- (*found_rows)++;
pq->push(ref_pos);
idx= pq->num_elements();
}
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
index 4cccf8ffa02..00fa6f2566b 100644
--- a/sql/filesort_utils.h
+++ b/sql/filesort_utils.h
@@ -95,7 +95,7 @@ public:
bool check_sort_buffer_properties(uint num_records, uint record_length)
{
return (static_cast<uint>(m_idx_array.size()) == num_records &&
- m_record_length == m_record_length);
+ m_record_length == record_length);
}
/// Frees the buffer.
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 5c11d18f3f3..fd6ac3b180a 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -89,7 +89,8 @@ static handler *partition_create_handler(handlerton *hton,
static uint partition_flags();
static uint alter_table_flags(uint flags);
-extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2);
+extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
+extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
/*
If frm_error() is called then we will use this to to find out what file
@@ -5100,7 +5101,10 @@ bool ha_partition::init_record_priority_queue()
uint alloc_len;
uint used_parts= bitmap_bits_set(&m_part_info->read_partitions);
/* Allocate record buffer for each used partition. */
- alloc_len= used_parts * (m_rec_length + PARTITION_BYTES_IN_POS);
+ m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS;
+ if (!m_using_extended_keys)
+ m_priority_queue_rec_len += m_file[0]->ref_length;
+ alloc_len= used_parts * m_priority_queue_rec_len;
/* Allocate a key for temporary use when setting up the scan. */
alloc_len+= table_share->max_key_length;
@@ -5122,12 +5126,24 @@ bool ha_partition::init_record_priority_queue()
{
DBUG_PRINT("info", ("init rec-buf for part %u", i));
int2store(ptr, i);
- ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ ptr+= m_priority_queue_rec_len;
}
m_start_key.key= (const uchar*)ptr;
+
/* Initialize priority queue, initialized to reading forward. */
- if (init_queue(&m_queue, used_parts, 0,
- 0, cmp_key_then_part_id, (void*)m_curr_key_info, 0, 0))
+ int (*cmp_func)(void *, uchar *, uchar *);
+ void *cmp_arg;
+ if (!m_using_extended_keys)
+ {
+ cmp_func= cmp_key_rowid_part_id;
+ cmp_arg= (void*)this;
+ }
+ else
+ {
+ cmp_func= cmp_key_part_id;
+ cmp_arg= (void*)m_curr_key_info;
+ }
+ if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0))
{
my_free(m_ordered_rec_buffer);
m_ordered_rec_buffer= NULL;
@@ -5194,9 +5210,13 @@ int ha_partition::index_init(uint inx, bool sorted)
DBUG_PRINT("info", ("Clustered pk, using pk as secondary cmp"));
m_curr_key_info[1]= table->key_info+table->s->primary_key;
m_curr_key_info[2]= NULL;
+ m_using_extended_keys= TRUE;
}
else
+ {
m_curr_key_info[1]= NULL;
+ m_using_extended_keys= FALSE;
+ }
if (init_record_priority_queue())
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -5337,36 +5357,12 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
}
-/*
- @brief
- Provide ordering by (key_value, partition_id).
-
- @detail
- Ordering by partition id is required so that key scans on key=const
- return rows in rowid order (this is required for some variants of
- index_merge to work).
-
- In ha_partition, rowid is a (partition_id, underlying_table_rowid).
- handle_ordered_index_scan must return rows ordered by (key, rowid).
-
- If two rows have the same key value and come from different partitions,
- it is sufficient to return them in the order of their partition_id.
-*/
-
-extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2)
+/* Compare two part_no partition numbers */
+static int cmp_part_ids(uchar *ref1, uchar *ref2)
{
- my_ptrdiff_t diff1, diff2;
- int res;
-
- if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS,
- ref2 + PARTITION_BYTES_IN_POS)))
- {
- return res;
- }
-
/* The following was taken from ha_partition::cmp_ref */
- diff1= ref2[1] - ref1[1];
- diff2= ref2[0] - ref1[0];
+ my_ptrdiff_t diff1= ref2[1] - ref1[1];
+ my_ptrdiff_t diff2= ref2[0] - ref1[0];
if (!diff1 && !diff2)
return 0;
@@ -5383,6 +5379,45 @@ extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2)
}
+/*
+ @brief
+ Provide ordering by (key_value, part_no).
+*/
+
+extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2)
+{
+ int res;
+ if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS,
+ ref2 + PARTITION_BYTES_IN_POS)))
+ {
+ return res;
+ }
+ return cmp_part_ids(ref1, ref2);
+}
+
+/*
+ @brief
+ Provide ordering by (key_value, underying_table_rowid, part_no).
+*/
+extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2)
+{
+ ha_partition *file= (ha_partition*)ptr;
+ int res;
+
+ if ((res= key_rec_cmp(file->m_curr_key_info, ref1 + PARTITION_BYTES_IN_POS,
+ ref2 + PARTITION_BYTES_IN_POS)))
+ {
+ return res;
+ }
+ if ((res= file->m_file[0]->cmp_ref(ref1 + PARTITION_BYTES_IN_POS + file->m_rec_length,
+ ref2 + PARTITION_BYTES_IN_POS + file->m_rec_length)))
+ {
+ return res;
+ }
+ return cmp_part_ids(ref1, ref2);
+}
+
+
/**
Common routine for a number of index_read variants
@@ -6083,7 +6118,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
i < m_part_spec.start_part;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
}
DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u",
m_part_spec.start_part, i));
@@ -6132,6 +6167,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
if (!error)
{
found= TRUE;
+ if (!m_using_extended_keys)
+ {
+ file->position(rec_buf_ptr);
+ memcpy(rec_buf_ptr + m_rec_length, file->ref, file->ref_length);
+ }
/*
Initialize queue without order first, simply insert
*/
@@ -6148,7 +6188,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
m_key_not_found= true;
saved_error= error;
}
- part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
}
if (found)
{
@@ -6157,7 +6197,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
after that read the first entry and copy it to the buffer to return in.
*/
queue_set_max_at_top(&m_queue, reverse_order);
- queue_set_cmp_arg(&m_queue, (void*)m_curr_key_info);
+ queue_set_cmp_arg(&m_queue, m_using_extended_keys? m_curr_key_info : (void*)this);
m_queue.elements= j - queue_first_element(&m_queue);
queue_fix(&m_queue);
return_top_record(buf);
@@ -6232,7 +6272,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND)
DBUG_RETURN(error);
}
- part_buf+= m_rec_length + PARTITION_BYTES_IN_POS;
+ part_buf += m_priority_queue_rec_len;
}
DBUG_ASSERT(curr_rec_buf);
bitmap_clear_all(&m_key_not_found_partitions);
@@ -6316,6 +6356,7 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
else
error= file->ha_index_next_same(rec_buf, m_start_key.key,
m_start_key.length);
+
if (error)
{
if (error == HA_ERR_END_OF_FILE)
@@ -6333,6 +6374,13 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
}
DBUG_RETURN(error);
}
+
+ if (!m_using_extended_keys)
+ {
+ file->position(rec_buf);
+ memcpy(rec_buf + m_rec_length, file->ref, file->ref_length);
+ }
+
queue_replace_top(&m_queue);
return_top_record(buf);
DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry));
@@ -8518,19 +8566,29 @@ uint ha_partition::min_record_length(uint options) const
int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
{
- uint part_id;
+ int cmp;
my_ptrdiff_t diff1, diff2;
- handler *file;
DBUG_ENTER("ha_partition::cmp_ref");
+ cmp = m_file[0]->cmp_ref((ref1 + PARTITION_BYTES_IN_POS),
+ (ref2 + PARTITION_BYTES_IN_POS));
+ if (cmp)
+ DBUG_RETURN(cmp);
+
if ((ref1[0] == ref2[0]) && (ref1[1] == ref2[1]))
{
- part_id= uint2korr(ref1);
- file= m_file[part_id];
- DBUG_ASSERT(part_id < m_tot_parts);
- DBUG_RETURN(file->cmp_ref((ref1 + PARTITION_BYTES_IN_POS),
- (ref2 + PARTITION_BYTES_IN_POS)));
+ /* This means that the references are same and are in same partition.*/
+ DBUG_RETURN(0);
}
+
+ /*
+ In Innodb we compare with either primary key value or global DB_ROW_ID so
+ it is not possible that the two references are equal and are in different
+ partitions, but in myisam it is possible since we are comparing offsets.
+ Remove this assert if DB_ROW_ID is changed to be per partition.
+ */
+ DBUG_ASSERT(!m_innodb);
+
diff1= ref2[1] - ref1[1];
diff2= ref2[0] - ref1[0];
if (diff1 > 0)
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 75018eca334..1f55301d14e 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -117,6 +117,8 @@ public:
};
+extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
+
class ha_partition :public handler
{
private:
@@ -157,6 +159,22 @@ private:
uchar *m_rec0; // table->record[0]
const uchar *m_err_rec; // record which gave error
QUEUE m_queue; // Prio queue used by sorted read
+
+ /*
+ Length of an element in m_ordered_rec_buffer. The elements are composed of
+
+ [part_no] [table->record copy] [underlying_table_rowid]
+
+ underlying_table_rowid is only stored when the table has no extended keys.
+ */
+ uint m_priority_queue_rec_len;
+
+ /*
+ If true, then sorting records by key value also sorts them by their
+ underlying_table_rowid.
+ */
+ bool m_using_extended_keys;
+
/*
Since the partition handler is a handler on top of other handlers, it
is necessary to keep information about what the underlying handler
@@ -1272,6 +1290,9 @@ public:
m_file[i]->ha_start_of_new_statement();
}
#endif /* WITH_WSREP */
+
+
+ friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
};
#endif /* HA_PARTITION_INCLUDED */
diff --git a/sql/handler.h b/sql/handler.h
index e9491cbf88a..7b49ff1ffd4 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1599,6 +1599,7 @@ struct HA_CREATE_INFO
uint stats_sample_pages;
uint null_bits; /* NULL bits at start of record */
uint options; /* OR of HA_CREATE_ options */
+ uint org_options; /* original options from query */
uint merge_insert_method;
uint extra_size; /* length of extra data segment */
SQL_I_List<TABLE_LIST> merge_list;
@@ -1629,6 +1630,7 @@ struct HA_CREATE_INFO
TABLE *table;
TABLE_LIST *pos_in_locked_tables;
MDL_ticket *mdl_ticket;
+ bool table_was_deleted;
bool tmp_table() { return options & HA_LEX_CREATE_TMP_TABLE; }
};
@@ -2728,6 +2730,18 @@ public:
}
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+
+ /**
+ The cost of reading a set of ranges from the table using an index
+ to access it.
+
+ @param index The index number.
+ @param ranges The number of ranges to be read.
+ @param rows Total number of rows to be read.
+
+ This method can be used to calculate the total cost of scanning a table
+ using an index by calling it using read_time(index, 1, table_size).
+ */
virtual double read_time(uint index, uint ranges, ha_rows rows)
{ return rows2double(ranges+rows); }
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index abba4824c9e..4c8c7575efc 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -199,4 +199,17 @@ public:
}
};
+template <class T> class Hash_filo: public hash_filo
+{
+public:
+ Hash_filo(uint size_arg, uint key_offset_arg, uint key_length_arg,
+ my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
+ CHARSET_INFO *hash_charset_arg) :
+ hash_filo(size_arg, key_offset_arg, key_length_arg,
+ get_key_arg, free_element_arg, hash_charset_arg) {}
+ T* first() { return (T*)hash_filo::first(); }
+ T* last() { return (T*)hash_filo::last(); }
+ T* search(uchar* key, size_t len) { return (T*)hash_filo::search(key, len); }
+};
+
#endif
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 8a4df816057..11cd16ac857 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -126,7 +126,7 @@ void Host_errors::aggregate(const Host_errors *errors)
m_local+= errors->m_local;
}
-static hash_filo *hostname_cache;
+static Hash_filo<Host_entry> *hostname_cache;
ulong host_cache_size;
void hostname_cache_refresh()
@@ -149,7 +149,7 @@ bool hostname_cache_init()
Host_entry tmp;
uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
- if (!(hostname_cache= new hash_filo(host_cache_size,
+ if (!(hostname_cache= new Hash_filo<Host_entry>(host_cache_size,
key_offset, HOST_ENTRY_KEY_SIZE,
NULL, (my_hash_free_key) free,
&my_charset_bin)))
@@ -187,11 +187,11 @@ static void prepare_hostname_cache_key(const char *ip_string,
}
Host_entry *hostname_cache_first()
-{ return (Host_entry *) hostname_cache->first(); }
+{ return hostname_cache->first(); }
static inline Host_entry *hostname_cache_search(const char *ip_key)
{
- return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
+ return hostname_cache->search((uchar *) ip_key, 0);
}
static void add_hostname_impl(const char *ip_key, const char *hostname,
diff --git a/sql/item.cc b/sql/item.cc
index 0fbd58567ef..9b27f730e80 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -41,7 +41,6 @@
// REPORT_EXCEPT_NOT_FOUND,
// find_item_in_list,
// RESOLVED_AGAINST_ALIAS, ...
-#include "log_event.h" // append_query_string
#include "sql_expression_cache.h"
const String my_null_string("NULL", 4, default_charset_info);
@@ -342,12 +341,29 @@ String *Item::val_string_from_decimal(String *str)
}
+/*
+ All val_xxx_from_date() must call this method, to expose consistent behaviour
+ regarding SQL_MODE when converting DATE/DATETIME to other data types.
+*/
+bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime)
+{
+ return get_date(ltime, field_type() == MYSQL_TYPE_TIME
+ ? TIME_TIME_ONLY
+ : sql_mode_for_dates(current_thd));
+}
+
+
+bool Item::is_null_from_temporal()
+{
+ MYSQL_TIME ltime;
+ return get_temporal_with_sql_mode(&ltime);
+}
+
+
String *Item::val_string_from_date(String *str)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, field_type() == MYSQL_TYPE_TIME
- ? TIME_TIME_ONLY
- : sql_mode_for_dates(current_thd)) ||
+ if (get_temporal_with_sql_mode(&ltime) ||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
{
null_value= 1;
@@ -404,7 +420,7 @@ my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, sql_mode_for_dates(current_thd)))
+ if (get_temporal_with_sql_mode(&ltime))
{
my_decimal_set_zero(decimal_value);
null_value= 1; // set NULL, stop processing
@@ -431,7 +447,7 @@ longlong Item::val_int_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
longlong v= TIME_to_ulonglong(&ltime);
return ltime.neg ? -v : v;
@@ -442,7 +458,7 @@ double Item::val_real_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
return TIME_to_double(&ltime);
}
@@ -3749,8 +3765,9 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
case LONG_DATA_VALUE:
{
str->length(0);
- append_query_string(thd, value.cs_info.character_set_client, &str_value,
- str);
+ append_query_string(value.cs_info.character_set_client, str,
+ str_value.ptr(), str_value.length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
break;
}
case NULL_VALUE:
@@ -8497,6 +8514,8 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
{
tmp_field->init(field_arg->field->table);
set_field(tmp_field);
+ // the index is important when read bits set
+ tmp_field->field_index= field_arg->field->field_index;
}
}
return FALSE;
diff --git a/sql/item.h b/sql/item.h
index e3ddf56511e..29e727b8d5f 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -971,6 +971,11 @@ public:
double val_real_from_decimal();
double val_real_from_date();
+ // Get TIME, DATE or DATETIME using proper sql_mode flags for the field type
+ bool get_temporal_with_sql_mode(MYSQL_TIME *ltime);
+ // Check NULL value for a TIME, DATE or DATETIME expression
+ bool is_null_from_temporal();
+
int save_time_in_field(Field *field);
int save_date_in_field(Field *field);
int save_str_value_in_field(Field *field, String *result);
@@ -2933,6 +2938,9 @@ public:
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
+ bool is_null()
+ { return is_null_from_temporal(); }
+ bool get_date_with_sql_mode(MYSQL_TIME *to);
String *val_str(String *str)
{ return val_string_from_date(str); }
longlong val_int()
@@ -2959,6 +2967,14 @@ public:
{
max_length= MAX_DATE_WIDTH;
fixed= 1;
+ /*
+ If date has zero month or day, it can return NULL in case of
+ NO_ZERO_DATE or NO_ZERO_IN_DATE.
+ We can't just check the current sql_mode here in constructor,
+ because sql_mode can change in case of prepared statements
+ between PREPARE and EXECUTE.
+ */
+ maybe_null= !ltime->month || !ltime->day;
}
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
void print(String *str, enum_query_type query_type);
@@ -2995,6 +3011,8 @@ public:
{
max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
fixed= 1;
+ // See the comment on maybe_null in Item_date_literal
+ maybe_null= !ltime->month || !ltime->day;
}
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
void print(String *str, enum_query_type query_type);
@@ -4009,7 +4027,7 @@ public:
bool walk(Item_processor processor, bool walk_subquery, uchar *args)
{
- return arg->walk(processor, walk_subquery, args) ||
+ return (arg && arg->walk(processor, walk_subquery, args)) ||
(this->*processor)(args);
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 8937c1c47e4..c3fd95c2b2d 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2666,6 +2666,9 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
volatile double value_div_tmp= value / tmp;
volatile double value_mul_tmp= value * tmp;
+ if (!dec_negative && my_isinf(tmp)) // "dec" is too large positive number
+ return value;
+
if (dec_negative && my_isinf(tmp))
tmp2= 0.0;
else if (!dec_negative && my_isinf(value_mul_tmp))
@@ -6750,22 +6753,18 @@ Item_func_sp::execute_impl(THD *thd)
{
bool err_status= TRUE;
Sub_statement_state statement_state;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
-#endif
enum enum_sp_data_access access=
(m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess;
DBUG_ENTER("Item_func_sp::execute_impl");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (context->security_ctx)
{
/* Set view definer security context */
thd->security_ctx= context->security_ctx;
}
-#endif
if (sp_check_access(thd))
goto error;
@@ -6793,9 +6792,7 @@ Item_func_sp::execute_impl(THD *thd)
thd->restore_sub_statement_state(&statement_state);
error:
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
thd->security_ctx= save_security_ctx;
-#endif
DBUG_RETURN(err_status);
}
@@ -6866,11 +6863,9 @@ Item_func_sp::sp_check_access(THD *thd)
{
DBUG_ENTER("Item_func_sp::sp_check_access");
DBUG_ASSERT(m_sp);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_routine_access(thd, EXECUTE_ACL,
m_sp->m_db.str, m_sp->m_name.str, 0, FALSE))
DBUG_RETURN(TRUE);
-#endif
DBUG_RETURN(FALSE);
}
@@ -6882,7 +6877,29 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0);
-
+
+ /*
+ Checking privileges to execute the function while creating view and
+ executing the function of select.
+ */
+ if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) ||
+ (thd->lex->sql_command == SQLCOM_CREATE_VIEW))
+ {
+ Security_context *save_security_ctx= thd->security_ctx;
+ if (context->security_ctx)
+ thd->security_ctx= context->security_ctx;
+
+ res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str,
+ m_name->m_name.str, 0, FALSE);
+ thd->security_ctx= save_security_ctx;
+
+ if (res)
+ {
+ context->process_error(thd);
+ DBUG_RETURN(res);
+ }
+ }
+
/*
We must call init_result_field before Item_func::fix_fields()
to make m_sp and result_field members available to fix_length_and_dec(),
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 57af832c151..1ae080ba22d 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -567,7 +567,7 @@ String *Item_func_decode_histogram::val_str(String *str)
int type;
tmp.length(0);
- if (!(res= args[1]->val_str(&tmp)) ||
+ if (!(res= args[0]->val_str(&tmp)) ||
(type= find_type(res->c_ptr_safe(),
&hystorgam_types_typelib, MYF(0))) <= 0)
{
@@ -577,7 +577,7 @@ String *Item_func_decode_histogram::val_str(String *str)
type--;
tmp.length(0);
- if (!(res= args[0]->val_str(&tmp)))
+ if (!(res= args[1]->val_str(&tmp)))
{
null_value= 1;
return 0;
@@ -3554,6 +3554,8 @@ String *Item_func_weight_string::val_str(String *str)
nweights ? nweights : tmp_length,
(const uchar *) res->ptr(), res->length(),
flags);
+ DBUG_ASSERT(frm_length <= tmp_length);
+
tmp_value.length(frm_length);
null_value= 0;
return &tmp_value;
@@ -4279,6 +4281,7 @@ bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
void Item_func_dyncol_create::fix_length_and_dec()
{
+ max_length= MAX_BLOB_WIDTH;
maybe_null= TRUE;
collation.set(&my_charset_bin);
decimals= 0;
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 7b2591e9346..ff8a916d200 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -1084,7 +1084,7 @@ class Item_func_uncompressed_length : public Item_int_func
public:
Item_func_uncompressed_length(Item *a):Item_int_func(a){}
const char *func_name() const{return "uncompressed_length";}
- void fix_length_and_dec() { max_length=10; }
+ void fix_length_and_dec() { max_length=10; maybe_null= true; }
longlong val_int();
};
@@ -1174,6 +1174,7 @@ public:
String *val_str(String *);
void fix_length_and_dec()
{
+ max_length= MAX_BLOB_WIDTH;
maybe_null= 1;
collation.set(&my_charset_bin);
decimals= 0;
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 75f0ed9ef5a..30db7e635e2 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -2600,16 +2600,24 @@ my_xpath_parse(MY_XPATH *xpath, const char *str, const char *strend)
void Item_xml_str_func::fix_length_and_dec()
{
+ max_length= MAX_BLOB_WIDTH;
+ agg_arg_charsets_for_comparison(collation, args, arg_count);
+}
+
+
+bool Item_xml_str_func::fix_fields(THD *thd, Item **ref)
+{
String *xp, tmp;
MY_XPATH xpath;
int rc;
+ if (Item_str_func::fix_fields(thd, ref))
+ return true;
+
status_var_increment(current_thd->status_var.feature_xml);
nodeset_func= 0;
- if (agg_arg_charsets_for_comparison(collation, args, arg_count))
- return;
if (collation.collation->mbminlen > 1)
{
@@ -2617,23 +2625,23 @@ void Item_xml_str_func::fix_length_and_dec()
my_printf_error(ER_UNKNOWN_ERROR,
"Character set '%s' is not supported by XPATH",
MYF(0), collation.collation->csname);
- return;
+ return true;
}
if (!args[1]->const_item())
{
my_printf_error(ER_UNKNOWN_ERROR,
"Only constant XPATH queries are supported", MYF(0));
- return;
+ return true;
}
if (!(xp= args[1]->val_str(&tmp)))
- return;
+ return false; // Will return NULL
my_xpath_init(&xpath);
xpath.cs= collation.collation;
xpath.debug= 0;
- xpath.pxml= &pxml;
- pxml.set_charset(collation.collation);
+ xpath.pxml= xml.parsed();
+ xml.set_charset(collation.collation);
rc= my_xpath_parse(&xpath, xp->ptr(), xp->ptr() + xp->length());
@@ -2643,13 +2651,24 @@ void Item_xml_str_func::fix_length_and_dec()
set_if_smaller(clen, 32);
my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'",
MYF(0), clen, xpath.lasttok.beg);
- return;
+ return true;
}
- nodeset_func= xpath.item;
- if (nodeset_func)
- nodeset_func->fix_fields(current_thd, &nodeset_func);
- max_length= MAX_BLOB_WIDTH;
+ /*
+ Parsing XML is a heavy operation, so if the first argument is constant,
+ then parse XML only one time and cache the parsed representation
+ together with raw text representation.
+
+ Note, we cannot cache the entire function result even if
+ the first and the second arguments are constants, because
+ the XPath expression may have user and SP variable references,
+ so the function result can vary between executions.
+ */
+ if ((args[0]->const_item() && get_xml(&xml, true)) ||
+ !(nodeset_func= xpath.item))
+ return false; // Will return NULL
+
+ return nodeset_func->fix_fields(thd, &nodeset_func);
}
@@ -2780,25 +2799,24 @@ int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len)
Parse raw XML
SYNOPSYS
-
RETURN
- Currently pointer to parsed XML on success
- 0 on parse error
+ false on success
+ true on error
*/
-String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
+bool Item_xml_str_func::XML::parse()
{
MY_XML_PARSER p;
MY_XML_USER_DATA user_data;
int rc;
- parsed_xml_buf->length(0);
+ m_parsed_buf.length(0);
/* Prepare XML parser */
my_xml_parser_create(&p);
p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION;
user_data.level= 0;
- user_data.pxml= parsed_xml_buf;
+ user_data.pxml= &m_parsed_buf;
user_data.parent= 0;
my_xml_set_enter_handler(&p, xml_enter);
my_xml_set_value_handler(&p, xml_value);
@@ -2807,10 +2825,10 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
/* Add root node */
p.current_node_type= MY_XML_NODE_TAG;
- xml_enter(&p, raw_xml->ptr(), 0);
+ xml_enter(&p, m_raw_ptr->ptr(), 0);
/* Execute XML parser */
- if ((rc= my_xml_parse(&p, raw_xml->ptr(), raw_xml->length())) != MY_XML_OK)
+ if ((rc= my_xml_parse(&p, m_raw_ptr->ptr(), m_raw_ptr->length())) != MY_XML_OK)
{
char buf[128];
my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %lu: %s",
@@ -2820,10 +2838,41 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE,
ER(ER_WRONG_VALUE), "XML", buf);
+ m_raw_ptr= (String *) 0;
}
my_xml_parser_free(&p);
- return rc == MY_XML_OK ? parsed_xml_buf : 0;
+ return rc != MY_XML_OK;
+}
+
+
+/*
+ Parse the raw XML from the given source,
+ optionally cache the raw XML,
+ remember the pointer to the raw XML.
+*/
+bool Item_xml_str_func::XML::parse(String *raw_xml, bool cache)
+{
+ m_raw_ptr= raw_xml;
+ if (cache)
+ {
+ m_cached= true;
+ if (m_raw_ptr != &m_raw_buf && m_raw_buf.copy(*m_raw_ptr))
+ {
+ m_raw_ptr= (String *) 0;
+ return true;
+ }
+ m_raw_ptr= &m_raw_buf;
+ }
+ return parse();
+}
+
+
+const MY_XML_NODE *Item_xml_str_func::XML::node(uint idx)
+{
+ const MY_XML_NODE *nodebeg= (MY_XML_NODE*) m_parsed_buf.ptr();
+ DBUG_ASSERT(idx < m_parsed_buf.length() / sizeof (MY_XML_NODE));
+ return nodebeg + idx;
}
@@ -2831,10 +2880,8 @@ String *Item_func_xml_extractvalue::val_str(String *str)
{
String *res;
null_value= 0;
- if (!nodeset_func ||
- !(res= args[0]->val_str(str)) ||
- !parse_xml(res, &pxml) ||
- !(res= nodeset_func->val_str(&tmp_value)))
+ if (!nodeset_func || get_xml(&xml) ||
+ !(res= nodeset_func->val_str(str)))
{
null_value= 1;
return 0;
@@ -2843,22 +2890,37 @@ String *Item_func_xml_extractvalue::val_str(String *str)
}
+bool Item_func_xml_update::collect_result(String *str,
+ const MY_XML_NODE *cut,
+ const String *replace)
+{
+ uint offs= cut->type == MY_XML_NODE_TAG ? 1 : 0;
+ const char *end= cut->tagend + offs;
+ str->length(0);
+ str->set_charset(collation.collation);
+ return
+ /* Put the XML part preceeding the replaced piece */
+ str->append(xml.raw()->ptr(), cut->beg - xml.raw()->ptr() - offs) ||
+ /* Put the replacement */
+ str->append(replace->ptr(), replace->length()) ||
+ /* Put the XML part following the replaced piece */
+ str->append(end, xml.raw()->ptr() + xml.raw()->length() - end);
+}
+
+
String *Item_func_xml_update::val_str(String *str)
{
- String *res, *nodeset, *rep;
+ String *nodeset, *rep;
null_value= 0;
- if (!nodeset_func ||
- !(res= args[0]->val_str(str)) ||
+ if (!nodeset_func || get_xml(&xml) ||
!(rep= args[2]->val_str(&tmp_value3)) ||
- !parse_xml(res, &pxml) ||
!(nodeset= nodeset_func->val_nodeset(&tmp_value2)))
{
null_value= 1;
return 0;
}
- MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml.ptr();
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length());
@@ -2866,10 +2928,10 @@ String *Item_func_xml_update::val_str(String *str)
if (fltend - fltbeg != 1)
{
/* TODO: perhaps add a warning that more than one tag selected */
- return res;
+ return xml.raw();
}
- nodebeg+= fltbeg->num;
+ const MY_XML_NODE *nodebeg= xml.node(fltbeg->num);
if (!nodebeg->level)
{
@@ -2881,12 +2943,5 @@ String *Item_func_xml_update::val_str(String *str)
return rep;
}
- tmp_value.length(0);
- tmp_value.set_charset(collation.collation);
- uint offs= nodebeg->type == MY_XML_NODE_TAG ? 1 : 0;
- tmp_value.append(res->ptr(), nodebeg->beg - res->ptr() - offs);
- tmp_value.append(rep->ptr(), rep->length());
- const char *end= nodebeg->tagend + offs;
- tmp_value.append(end, res->ptr() + res->length() - end);
- return &tmp_value;
+ return collect_result(str, nodebeg, rep) ? (String *) NULL : str;
}
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index e818a6da408..637f505e12e 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -26,11 +26,55 @@
#endif
+typedef struct my_xml_node_st MY_XML_NODE;
+
+
class Item_xml_str_func: public Item_str_func
{
protected:
- String tmp_value, pxml;
+ /*
+ A helper class to store raw and parsed XML.
+ */
+ class XML
+ {
+ bool m_cached;
+ String *m_raw_ptr; // Pointer to text representation
+ String m_raw_buf; // Cached text representation
+ String m_parsed_buf; // Array of MY_XML_NODEs, pointing to raw_buffer
+ bool parse();
+ void reset()
+ {
+ m_cached= false;
+ m_raw_ptr= (String *) 0;
+ }
+ public:
+ XML() { reset(); }
+ void set_charset(CHARSET_INFO *cs) { m_parsed_buf.set_charset(cs); }
+ String *raw() { return m_raw_ptr; }
+ String *parsed() { return &m_parsed_buf; }
+ const MY_XML_NODE *node(uint idx);
+ bool cached() { return m_cached; }
+ bool parse(String *raw, bool cache);
+ bool parse(Item *item, bool cache)
+ {
+ String *res;
+ if (!(res= item->val_str(&m_raw_buf)))
+ {
+ m_raw_ptr= (String *) 0;
+ m_cached= cache;
+ return true;
+ }
+ return parse(res, cache);
+ }
+ };
Item *nodeset_func;
+ XML xml;
+ bool get_xml(XML *xml, bool cache= false)
+ {
+ if (!cache && xml->cached())
+ return xml->raw() == 0;
+ return xml->parse(args[0], cache);
+ }
public:
Item_xml_str_func(Item *a, Item *b):
Item_str_func(a,b)
@@ -42,8 +86,12 @@ public:
{
maybe_null= TRUE;
}
+ bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
- String *parse_xml(String *raw_xml, String *parsed_xml_buf);
+ bool const_item() const
+ {
+ return const_item_cache && (!nodeset_func || nodeset_func->const_item());
+ }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -63,6 +111,9 @@ public:
class Item_func_xml_update: public Item_xml_str_func
{
String tmp_value2, tmp_value3;
+ bool collect_result(String *str,
+ const MY_XML_NODE *cut,
+ const String *replace);
public:
Item_func_xml_update(Item *a,Item *b,Item *c) :Item_xml_str_func(a,b,c) {}
const char *func_name() const { return "updatexml"; }
diff --git a/sql/lock.cc b/sql/lock.cc
index 62540bcb55a..793b56f965d 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -807,7 +807,6 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
@param thd Thread handle.
@param db The database name.
- This function cannot be called while holding LOCK_open mutex.
To avoid deadlocks, we do not try to obtain exclusive metadata
locks in LOCK TABLES mode, since in this mode there may be
other metadata locks already taken by the current connection,
@@ -859,9 +858,7 @@ bool lock_schema_name(THD *thd, const char *db)
@param name Object name in the schema.
This function assumes that no metadata locks were acquired
- before calling it. Additionally, it cannot be called while
- holding LOCK_open mutex. Both these invariants are enforced by
- asserts in MDL_context::acquire_locks().
+ before calling it. It is enforced by asserts in MDL_context::acquire_locks().
To avoid deadlocks, we do not try to obtain exclusive metadata
locks in LOCK TABLES mode, since in this mode there may be
other metadata locks already taken by the current connection,
diff --git a/sql/log.cc b/sql/log.cc
index 7dcd1548fbb..0ac5a4818aa 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -670,25 +670,19 @@ int check_if_log_table(const TABLE_LIST *table,
{
int result= 0;
if (table->db_length == 5 &&
- !(lower_case_table_names ?
- my_strcasecmp(system_charset_info, table->db, "mysql") :
- strcmp(table->db, "mysql")))
+ !my_strcasecmp(table_alias_charset, table->db, "mysql"))
{
const char *table_name= table->table_name;
if (table->table_name_length == 11 &&
- !(lower_case_table_names ?
- my_strcasecmp(system_charset_info,
- table_name, "general_log") :
- strcmp(table_name, "general_log")))
+ !my_strcasecmp(table_alias_charset, table_name, "general_log"))
{
result= QUERY_LOG_GENERAL;
goto end;
}
- if (table->table_name_length == 8 && !(lower_case_table_names ?
- my_strcasecmp(system_charset_info, table_name, "slow_log") :
- strcmp(table_name, "slow_log")))
+ if (table->table_name_length == 8 &&
+ !my_strcasecmp(table_alias_charset, table_name, "slow_log"))
{
result= QUERY_LOG_SLOW;
goto end;
@@ -2211,6 +2205,21 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
DBUG_RETURN(error);
}
+
+void binlog_reset_cache(THD *thd)
+{
+ binlog_cache_mngr *const cache_mngr= opt_bin_log ?
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton) : 0;
+ DBUG_ENTER("binlog_reset_cache");
+ if (cache_mngr)
+ {
+ thd->binlog_remove_pending_rows_event(TRUE, TRUE);
+ cache_mngr->reset(true, true);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
@@ -3750,7 +3759,8 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
if (!bytes_read)
break; // end of file
mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
- if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
+ if (mysql_file_write(file, io_buf, bytes_read,
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
goto err;
}
/* The following will either truncate the file or fill the end with \n' */
diff --git a/sql/log.h b/sql/log.h
index fb430e4c206..bf11edfd429 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -1029,6 +1029,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
void make_default_log_name(char **out, const char* log_ext, bool once);
+void binlog_reset_cache(THD *thd);
extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
extern LOGGER logger;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 9e9f2dd25f8..9886e90bb41 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -690,37 +690,35 @@ char *str_to_hex(char *to, const char *from, uint len)
#ifndef MYSQL_CLIENT
/**
- Append a version of the 'from' string suitable for use in a query to
+ Append a version of the 'str' string suitable for use in a query to
the 'to' string. To generate a correct escaping, the character set
information in 'csinfo' is used.
*/
-int
-append_query_string(THD *thd, CHARSET_INFO *csinfo,
- String const *from, String *to)
+int append_query_string(CHARSET_INFO *csinfo, String *to,
+ const char *str, size_t len, bool no_backslash)
{
char *beg, *ptr;
uint32 const orig_len= to->length();
- if (to->reserve(orig_len + from->length() * 2 + 4))
+ if (to->reserve(orig_len + len * 2 + 4))
return 1;
beg= (char*) to->ptr() + to->length();
ptr= beg;
if (csinfo->escape_with_backslash_is_dangerous)
- ptr= str_to_hex(ptr, from->ptr(), from->length());
+ ptr= str_to_hex(ptr, str, len);
else
{
*ptr++= '\'';
- if (!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
+ if (!no_backslash)
{
- ptr+= escape_string_for_mysql(csinfo, ptr, 0,
- from->ptr(), from->length());
+ ptr+= escape_string_for_mysql(csinfo, ptr, 0, str, len);
}
else
{
- const char *frm_str= from->ptr();
+ const char *frm_str= str;
- for (; frm_str < (from->ptr() + from->length()); frm_str++)
+ for (; frm_str < (str + len); frm_str++)
{
/* Using '' way to represent "'" */
if (*frm_str == '\'')
@@ -4156,11 +4154,11 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
nothing to do.
*/
/*
- We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
- slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
+ We do not replicate MODE_NO_DIR_IN_CREATE. That is, if the master is a
+ slave which runs with SQL_MODE=MODE_NO_DIR_IN_CREATE, this should not
force us to ignore the dir too. Imagine you are a ring of machines, and
one has a disk problem so that you temporarily need
- IGNORE_DIR_IN_CREATE on this machine; you don't want it to propagate
+ MODE_NO_DIR_IN_CREATE on this machine; you don't want it to propagate
elsewhere (you don't want all slaves to start ignoring the dirs).
*/
if (sql_mode_inited)
@@ -4454,7 +4452,7 @@ Default database: '%s'. Query: '%s'",
end:
if (sub_id && !thd->is_slave_error)
- rpl_global_gtid_slave_state.update_state_hash(sub_id, &gtid);
+ rpl_global_gtid_slave_state.update_state_hash(sub_id, &gtid, rgi);
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
@@ -6835,7 +6833,8 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi)
sub_id_list[i],
false, false)))
return ret;
- rpl_global_gtid_slave_state.update_state_hash(sub_id_list[i], &list[i]);
+ rpl_global_gtid_slave_state.update_state_hash(sub_id_list[i], &list[i],
+ NULL);
}
}
ret= Log_event::do_apply_event(rgi);
@@ -7355,7 +7354,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
thd->mdl_context.release_transactional_locks();
if (!res && sub_id)
- rpl_global_gtid_slave_state.update_state_hash(sub_id, &gtid);
+ rpl_global_gtid_slave_state.update_state_hash(sub_id, &gtid, rgi);
/*
Increment the global status commit count variable
@@ -9227,7 +9226,8 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
m_type(event_type), m_extra_row_data(0)
#ifdef HAVE_REPLICATION
, m_curr_row(NULL), m_curr_row_end(NULL),
- m_key(NULL), m_key_info(NULL), m_key_nr(0)
+ m_key(NULL), m_key_info(NULL), m_key_nr(0),
+ master_had_triggers(0)
#endif
{
/*
@@ -9276,7 +9276,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
m_extra_row_data(0)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
, m_curr_row(NULL), m_curr_row_end(NULL),
- m_key(NULL), m_key_info(NULL), m_key_nr(0)
+ m_key(NULL), m_key_info(NULL), m_key_nr(0),
+ master_had_triggers(0)
#endif
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
@@ -9531,10 +9532,30 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
}
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+
+/**
+ Restores empty table list as it was before trigger processing.
+
+ @note We have a lot of ASSERTS that check the lists when we close tables.
+ There was the same problem with MERGE MYISAM tables and so here we try to
+ go the same way.
+*/
+static void restore_empty_query_table_list(LEX *lex)
+{
+#ifdef RBR_TRIGGERS
+ if (lex->first_not_own_table())
+ (*lex->first_not_own_table()->prev_global)= NULL;
+ lex->query_tables= NULL;
+ lex->query_tables_last= &lex->query_tables;
+#endif //RBR_TRIGGERS
+}
+
+
int Rows_log_event::do_apply_event(rpl_group_info *rgi)
{
Relay_log_info const *rli= rgi->rli;
+ TABLE* table;
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
int error= 0;
/*
@@ -9614,6 +9635,28 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
/* A small test to verify that objects have consistent types */
DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+ if (slave_run_triggers_for_rbr)
+ {
+ LEX *lex= thd->lex;
+ uint8 new_trg_event_map= get_trg_event_map();
+
+ /*
+ Trigger's procedures work with global table list. So we have to add
+ rgi->tables_to_lock content there to get trigger's in the list.
+
+ Then restore_empty_query_table_list() restore the list as it was
+ */
+ DBUG_ASSERT(lex->query_tables == NULL);
+ if ((lex->query_tables= rgi->tables_to_lock))
+ rgi->tables_to_lock->prev_global= &lex->query_tables;
+
+ for (TABLE_LIST *tables= rgi->tables_to_lock; tables;
+ tables= tables->next_global)
+ {
+ tables->trg_event_map= new_trg_event_map;
+ lex->query_tables_last= &tables->next_global;
+ }
+ }
if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))
{
uint actual_error= thd->get_stmt_da()->sql_errno();
@@ -9643,8 +9686,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
- rgi->slave_close_thread_tables(thd);
- DBUG_RETURN(actual_error);
+ /* remove trigger's tables */
+ error= actual_error;
+ goto err;
}
/*
@@ -9688,8 +9732,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
having severe errors which should not be skiped.
*/
thd->is_slave_error= 1;
- rgi->slave_close_thread_tables(thd);
- DBUG_RETURN(ERR_BAD_TABLE_DEF);
+ /* remove trigger's tables */
+ error= ERR_BAD_TABLE_DEF;
+ goto err;
}
DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
" - conv_table: %p",
@@ -9715,21 +9760,35 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
TABLE_LIST *ptr= rgi->tables_to_lock;
for (uint i=0 ; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
+ {
rgi->m_table_map.set_table(ptr->table_id, ptr->table);
+ /*
+ Following is passing flag about triggers on the server. The problem was
+ to pass it between table map event and row event. I do it via extended
+ TABLE_LIST (RPL_TABLE_LIST) but row event uses only TABLE so I need to
+ find somehow the corresponding TABLE_LIST.
+ */
+ if (m_table_id == ptr->table_id)
+ {
+ ptr->table->master_had_triggers=
+ ((RPL_TABLE_LIST*)ptr)->master_had_triggers;
+ }
+ }
#ifdef HAVE_QUERY_CACHE
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
#endif
}
- TABLE*
- table=
- m_table= rgi->m_table_map.get_table(m_table_id);
-
- DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu", (ulong) m_table, m_table_id));
+ table= m_table= rgi->m_table_map.get_table(m_table_id);
+ DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu%s",
+ (ulong) m_table, m_table_id,
+ table && master_had_triggers ?
+ " (master had triggers)" : ""));
if (table)
{
+ master_had_triggers= table->master_had_triggers;
bool transactional_table= table->file->has_transactions();
/*
table == NULL means that this table should not be replicated
@@ -9788,6 +9847,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
rgi->set_row_stmt_start_timestamp();
+ THD_STAGE_INFO(thd, stage_executing);
while (error == 0 && m_curr_row < m_rows_end)
{
/* in_use can have been set to NULL in close_tables_for_reopen */
@@ -9893,9 +9953,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
thd->reset_current_stmt_binlog_format_row();
thd->is_slave_error= 1;
- DBUG_RETURN(error);
+ /* remove trigger's tables */
+ goto err;
}
+ /* remove trigger's tables */
+ if (slave_run_triggers_for_rbr)
+ restore_empty_query_table_list(thd->lex);
if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
slave_rows_error_report(ERROR_LEVEL,
thd->is_error() ? 0 : error,
@@ -9903,6 +9967,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
DBUG_RETURN(error);
+
+err:
+ if (slave_run_triggers_for_rbr)
+ restore_empty_query_table_list(thd->lex);
+ rgi->slave_close_thread_tables(thd);
+ DBUG_RETURN(error);
}
Log_event::enum_skip_reason
@@ -10045,7 +10115,7 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi)
DBUG_RETURN(error);
}
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+#endif //defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
@@ -10376,6 +10446,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
{
uchar cbuf[MAX_INT_WIDTH];
uchar *cbuf_end;
+ DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
DBUG_ASSERT(m_table_id != ~0UL);
/*
In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
@@ -10396,6 +10467,11 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
m_data_size+= (cbuf_end - cbuf) + m_colcnt; // COLCNT and column types
+#ifdef RBR_TRIGGERS
+ if (tbl->triggers)
+ m_flags|= TM_BIT_HAS_TRIGGERS_F;
+#endif //RBR_TRIGGERS
+
/* If malloc fails, caught in is_valid() */
if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
{
@@ -10440,6 +10516,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
if (m_table->field[i]->maybe_null())
m_null_bits[(i / 8)]+= 1 << (i % 8);
+ DBUG_VOID_RETURN;
}
#endif /* !defined(MYSQL_CLIENT) */
@@ -10807,7 +10884,15 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
table_list->updating= 1;
table_list->required_type= FRMTYPE_TABLE;
- DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
+
+ DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name,
+ table_list->table_id));
+#ifdef RBR_TRIGGERS
+ table_list->master_had_triggers= ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? 1 : 0);
+ DBUG_PRINT("debug", ("table->master_had_triggers=%d",
+ (int)table_list->master_had_triggers));
+#endif //RBR_TRIGGERS
+
enum_tbl_map_status tblmap_status= check_table_map(rgi, table_list);
if (tblmap_status == OK_TO_PROCESS)
{
@@ -10979,8 +11064,10 @@ void Table_map_log_event::print(FILE *, PRINT_EVENT_INFO *print_event_info)
{
print_header(&print_event_info->head_cache, print_event_info, TRUE);
my_b_printf(&print_event_info->head_cache,
- "\tTable_map: %`s.%`s mapped to number %lu\n",
- m_dbnam, m_tblnam, (ulong) m_table_id);
+ "\tTable_map: %`s.%`s mapped to number %lu%s\n",
+ m_dbnam, m_tblnam, m_table_id,
+ ((m_flags & TM_BIT_HAS_TRIGGERS_F) ?
+ " (has triggers)" : ""));
print_base64(&print_event_info->body_cache, print_event_info, TRUE);
}
}
@@ -11056,6 +11143,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
/*
NDB specific: update from ndb master wrapped as Write_rows
so that the event should be applied to replace slave's row
+
+ Also following is needed in case if we have AFTER DELETE triggers.
*/
m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
/*
@@ -11070,6 +11159,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
from the start.
*/
}
+ if (slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers )
+ m_table->prepare_triggers_for_insert_stmt_or_event();
/* Honor next number column if present */
m_table->next_number_field= m_table->found_next_number_field;
@@ -11115,6 +11206,29 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+bool Rows_log_event::process_triggers(trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1)
+{
+#ifdef RBR_TRIGGERS
+ bool result;
+ DBUG_ENTER("Rows_log_event::process_triggers");
+ if (slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_YES)
+ {
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type, old_row_is_record1);
+ reenable_binlog(thd);
+ }
+ else
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type, old_row_is_record1);
+
+ DBUG_RETURN(result);
+#else
+ return TRUE;
+#endif //RBR_TRIGGERS
+}
/*
Check if there are more UNIQUE keys after the given key.
*/
@@ -11197,6 +11311,8 @@ Rows_log_event::write_row(rpl_group_info *rgi,
TABLE *table= m_table; // pointer to event's table
int error;
int UNINIT_VAR(keynum);
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && table->triggers;
auto_afree_ptr<char> key(NULL);
prepare_record(table, m_width,
@@ -11206,13 +11322,17 @@ Rows_log_event::write_row(rpl_group_info *rgi,
if ((error= unpack_current_row(rgi)))
DBUG_RETURN(error);
- if (m_curr_row == m_rows_buf)
+ if (m_curr_row == m_rows_buf && !invoke_triggers)
{
- /* this is the first row to be inserted, we estimate the rows with
+ /*
+ This table has no triggers so we can do bulk insert.
+
+ This is the first row to be inserted, we estimate the rows with
the size of the first row and use that value to initialize
- storage engine for bulk insertion */
+ storage engine for bulk insertion.
+ */
ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
- m_table->file->ha_start_bulk_insert(estimated_rows);
+ table->file->ha_start_bulk_insert(estimated_rows);
}
@@ -11222,6 +11342,12 @@ Rows_log_event::write_row(rpl_group_info *rgi,
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE))
+ {
+ DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
+ }
+
/*
Try to write record. If a corresponding record already exists in the table,
we try to change it using ha_update_row() if possible. Otherwise we delete
@@ -11348,38 +11474,61 @@ Rows_log_event::write_row(rpl_group_info *rgi,
!table->file->referenced_by_foreign_key())
{
DBUG_PRINT("info",("Updating row using ha_update_row()"));
- error=table->file->ha_update_row(table->record[1],
- table->record[0]);
- switch (error) {
-
- case HA_ERR_RECORD_IS_THE_SAME:
- DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
- " ha_update_row()"));
- error= 0;
-
- case 0:
- break;
-
- default:
- DBUG_PRINT("info",("ha_update_row() returns error %d",error));
- table->file->print_error(error, MYF(0));
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ else
+ {
+ error= table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ switch (error) {
+
+ case HA_ERR_RECORD_IS_THE_SAME:
+ DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+ " ha_update_row()"));
+ error= 0;
+
+ case 0:
+ break;
+
+ default:
+ DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
+ if (invoke_triggers && !error &&
+ (process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE) ||
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
}
-
+
DBUG_RETURN(error);
}
else
{
DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ else
{
- DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, TRUE))
+ DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
}
/* Will retry ha_write_row() with the offending row removed. */
}
}
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
DBUG_RETURN(error);
}
@@ -11426,6 +11575,16 @@ void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
}
#endif
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Write_rows_log_event::get_trg_event_map()
+{
+ return (static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)) |
+ static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE)) |
+ static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE)));
+}
+#endif
+
/**************************************************************************
Delete_rows_log_event member functions
**************************************************************************/
@@ -12050,6 +12209,10 @@ Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability
*/
return 0;
}
+#ifdef RBR_TRIGGERS
+ if (slave_run_triggers_for_rbr && !master_had_triggers)
+ m_table->prepare_triggers_for_delete_stmt_or_event();
+#endif //RBR_TRIGGERS
return find_key();
}
@@ -12070,6 +12233,8 @@ Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability
int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
int error;
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
DBUG_ASSERT(m_table != NULL);
#ifdef WITH_WSREP
@@ -12099,7 +12264,14 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
if (WSREP(thd)) thd_proc_info(thd,"Delete_rows_log_event::ha_delete_row()");
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
- error= m_table->file->ha_delete_row(m_table->record[0]);
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ if (!error)
+ error= m_table->file->ha_delete_row(m_table->record[0]);
+ if (invoke_triggers && !error &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
m_table->file->ha_index_or_rnd_end();
}
#ifdef WITH_WSREP
@@ -12119,6 +12291,13 @@ void Delete_rows_log_event::print(FILE *file,
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Delete_rows_log_event::get_trg_event_map()
+{
+ return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE));
+}
+#endif
+
/**************************************************************************
Update_rows_log_event member functions
**************************************************************************/
@@ -12201,6 +12380,9 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability
if ((err= find_key()))
return err;
+ if (slave_run_triggers_for_rbr && !master_had_triggers)
+ m_table->prepare_triggers_for_update_stmt_or_event();
+
return 0;
}
@@ -12220,6 +12402,8 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
int
Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
DBUG_ASSERT(m_table != NULL);
#ifdef WITH_WSREP
@@ -12299,10 +12483,22 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
if (WSREP(thd)) thd_proc_info(thd,"Update_rows_log_event::ha_update_row()");
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
+
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE))
+ {
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ goto err;
+ }
+
error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
if (error == HA_ERR_RECORD_IS_THE_SAME)
error= 0;
+ if (invoke_triggers && !error &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
#ifdef WITH_WSREP
if (WSREP(thd)) thd_proc_info(thd, tmp);
#endif /* WITH_WSREP */
@@ -12321,6 +12517,12 @@ void Update_rows_log_event::print(FILE *file,
}
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Update_rows_log_event::get_trg_event_map()
+{
+ return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE));
+}
+#endif
Incident_log_event::Incident_log_event(const char *buf, uint event_len,
const Format_description_log_event *descr_event)
diff --git a/sql/log_event.h b/sql/log_event.h
index a9d1b08171f..bba1b907a57 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -4049,7 +4049,9 @@ public:
enum
{
TM_NO_FLAGS = 0U,
- TM_BIT_LEN_EXACT_F = (1U << 0)
+ TM_BIT_LEN_EXACT_F = (1U << 0),
+ // MariaDB flags (we starts from the other end)
+ TM_BIT_HAS_TRIGGERS_F= (1U << 14)
};
flag_set get_flags(flag_set flag) const { return m_flags & flag; }
@@ -4254,6 +4256,10 @@ public:
const uchar* get_extra_row_data() const { return m_extra_row_data; }
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual uint8 get_trg_event_map()= 0;
+#endif
+
protected:
/*
The constructors are protected since you're supposed to inherit
@@ -4307,6 +4313,7 @@ protected:
uchar *m_extra_row_data; /* Pointer to extra row data if any */
/* If non null, first byte is length */
+
/* helper functions */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4315,6 +4322,7 @@ protected:
uchar *m_key; /* Buffer to keep key value during searches */
KEY *m_key_info; /* Pointer to KEY info for m_key_nr */
uint m_key_nr; /* Key number */
+ bool master_had_triggers; /* set after tables opening */
int find_key(); // Find a best key to use in find_row()
int find_row(rpl_group_info *);
@@ -4329,7 +4337,10 @@ protected:
return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
&m_curr_row_end, &m_master_reclength, m_rows_end);
}
-#endif
+ bool process_triggers(trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1);
+#endif //defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
private:
@@ -4433,6 +4444,10 @@ public:
}
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
private:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
@@ -4507,6 +4522,10 @@ public:
return Rows_log_event::is_valid() && m_cols_ai.bitmap;
}
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
protected:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
@@ -4571,7 +4590,11 @@ public:
cols, fields, before_record);
}
#endif
-
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
protected:
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
@@ -4743,9 +4766,6 @@ private:
bool slave_execute_deferred_events(THD *thd);
#endif
-int append_query_string(THD *thd, CHARSET_INFO *csinfo,
- String const *from, String *to);
-
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
const char **group_relay_log_name,
ulonglong *relay_log_pos);
diff --git a/sql/mdl.cc b/sql/mdl.cc
index ff53793554a..8360243b4da 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -2007,8 +2007,6 @@ bool MDL_lock::has_pending_conflicting_lock(enum_mdl_type type)
{
bool result;
- mysql_mutex_assert_not_owner(&LOCK_open);
-
mysql_prlock_rdlock(&m_rwlock);
result= (m_waiting.bitmap() & incompatible_granted_types_bitmap()[type]);
mysql_prlock_unlock(&m_rwlock);
@@ -2183,7 +2181,6 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
/* Don't take chances in production. */
mdl_request->ticket= NULL;
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
Check whether the context already holds a shared lock on the object,
@@ -2273,7 +2270,6 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
{
MDL_ticket *ticket;
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
By submitting mdl_request->type to MDL_ticket::create()
we effectively downgrade the cloned lock to the level of
@@ -2934,7 +2930,6 @@ void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
lock->key.db_name(), lock->key.name()));
DBUG_ASSERT(this == ticket->get_ctx());
- mysql_mutex_assert_not_owner(&LOCK_open);
lock->remove_ticket(&MDL_lock::m_granted, ticket);
@@ -3032,8 +3027,6 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name)
void MDL_ticket::downgrade_lock(enum_mdl_type type)
{
- mysql_mutex_assert_not_owner(&LOCK_open);
-
/*
Do nothing if already downgraded. Used when we FLUSH TABLE under
LOCK TABLES and a table is listed twice in LOCK TABLES list.
diff --git a/sql/mdl.h b/sql/mdl.h
index 13c098e26d8..1601d5a2634 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -519,17 +519,7 @@ public:
virtual bool inspect_edge(MDL_context *dest) = 0;
virtual ~MDL_wait_for_graph_visitor();
- MDL_wait_for_graph_visitor() :m_lock_open_count(0) {}
-public:
- /**
- XXX, hack: During deadlock search, we may need to
- inspect TABLE_SHAREs and acquire LOCK_open. Since
- LOCK_open is not a recursive mutex, count here how many
- times we "took" it (but only take and release once).
- Not using a native recursive mutex or rwlock in 5.5 for
- LOCK_open since it has significant performance impacts.
- */
- uint m_lock_open_count;
+ MDL_wait_for_graph_visitor() {}
};
/**
@@ -996,10 +986,6 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd);
*/
extern "C" int thd_is_connected(MYSQL_THD thd);
-#ifndef DBUG_OFF
-extern mysql_mutex_t LOCK_open;
-#endif
-
/*
Start-up parameter for the maximum size of the unused MDL_lock objects cache
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 0bd06fd57e7..5d9ff0bfa87 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -496,6 +496,9 @@ ulong open_files_limit, max_binlog_size;
ulong slave_trans_retries;
uint slave_net_timeout;
ulong slave_exec_mode_options;
+#ifdef RBR_TRIGGERS
+ulong slave_run_triggers_for_rbr= 0;
+#endif //RBR_TRIGGERS
ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
ulonglong slave_type_conversions_options;
ulong thread_cache_size=0;
@@ -570,6 +573,7 @@ ulong opt_slave_domain_parallel_threads= 0;
ulong opt_binlog_commit_wait_count= 0;
ulong opt_binlog_commit_wait_usec= 0;
ulong opt_slave_parallel_max_queued= 131072;
+my_bool opt_gtid_ignore_duplicates= FALSE;
const double log_10[] = {
1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
@@ -1044,7 +1048,7 @@ PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread,
key_COND_rpl_thread_pool,
key_COND_parallel_entry, key_COND_group_commit_orderer,
key_COND_prepare_ordered;
-PSI_cond_key key_COND_wait_gtid;
+PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
static PSI_cond_info all_server_conds[]=
{
@@ -1101,7 +1105,8 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_parallel_entry, "COND_parallel_entry", 0},
{ &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0},
{ &key_COND_prepare_ordered, "COND_prepare_ordered", 0},
- { &key_COND_wait_gtid, "COND_wait_gtid", 0}
+ { &key_COND_wait_gtid, "COND_wait_gtid", 0},
+ { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
};
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
@@ -2855,9 +2860,7 @@ void unlink_thd(THD *thd)
thd_cleanup(thd);
dec_connection_count(thd);
- mysql_mutex_lock(&LOCK_status);
- add_to_status(&global_status_var, &thd->status_var);
- mysql_mutex_unlock(&LOCK_status);
+ thd->add_status_to_global();
mysql_mutex_lock(&LOCK_thread_count);
thd->unlink();
@@ -10250,6 +10253,7 @@ PSI_stage_info stage_waiting_for_prior_transaction_to_commit= { 0, "Waiting for
PSI_stage_info stage_waiting_for_room_in_worker_thread= { 0, "Waiting for room in worker thread event queue", 0};
PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0};
PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0};
+PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process GTID received on multiple master connections", 0};
#ifdef HAVE_PSI_INTERFACE
@@ -10368,7 +10372,8 @@ PSI_stage_info *all_server_stages[]=
& stage_waiting_to_finalize_termination,
& stage_waiting_to_get_readlock,
& stage_master_gtid_wait_primary,
- & stage_master_gtid_wait
+ & stage_master_gtid_wait,
+ & stage_gtid_wait_other_connection
};
PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 1a1183d1819..f1dde5cc03e 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -98,6 +98,11 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
extern ulong slave_retried_transactions;
+#ifdef RBR_TRIGGERS
+extern ulong slave_run_triggers_for_rbr;
+#else
+#define slave_run_triggers_for_rbr 0
+#endif //RBR_TRIGGERS
extern ulonglong slave_type_conversions_options;
extern my_bool read_only, opt_readonly;
extern my_bool lower_case_file_system;
@@ -184,6 +189,7 @@ extern ulong opt_slave_domain_parallel_threads;
extern ulong opt_slave_parallel_max_queued;
extern ulong opt_binlog_commit_wait_count;
extern ulong opt_binlog_commit_wait_usec;
+extern my_bool opt_gtid_ignore_duplicates;
extern ulong back_log;
extern ulong executed_events;
extern char language[FN_REFLEN];
@@ -304,7 +310,7 @@ extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue,
key_COND_rpl_thread_pool,
key_COND_parallel_entry, key_COND_group_commit_orderer;
-extern PSI_cond_key key_COND_wait_gtid;
+extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
@@ -445,6 +451,7 @@ extern PSI_stage_info stage_waiting_for_prior_transaction_to_commit;
extern PSI_stage_info stage_waiting_for_room_in_worker_thread;
extern PSI_stage_info stage_master_gtid_wait_primary;
extern PSI_stage_info stage_master_gtid_wait;
+extern PSI_stage_info stage_gtid_wait_other_connection;
#ifdef HAVE_PSI_STATEMENT_INTERFACE
/**
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index b08ecc399bf..cbc3d285d0a 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3168,7 +3168,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
}
- if (optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE))
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE) &&
+ head->stat_records() != 0)
{
/* Try creating index_merge/ROR-union scan. */
SEL_IMERGE *imerge;
@@ -3408,7 +3409,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
table->cond_selectivity= 1.0;
- if (table_records == 0)
+ if (!cond || table_records == 0)
DBUG_RETURN(FALSE);
if (table->pos_in_table_list->schema_table)
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 1ae62011b43..98e796879ad 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1574,9 +1574,7 @@ end:
*/
static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem)
{
-#ifdef HAVE_READLINK
- if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
-#endif
+ if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
{
if (part_elem->data_file_name)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index e51dee20c19..17c3b15c902 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -33,7 +33,8 @@ const LEX_STRING rpl_gtid_slave_state_table_name=
void
-rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid)
+rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
+ rpl_group_info *rgi)
{
int err;
/*
@@ -44,7 +45,7 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid)
it is even committed.
*/
mysql_mutex_lock(&LOCK_slave_state);
- err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no);
+ err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi);
mysql_mutex_unlock(&LOCK_slave_state);
if (err)
{
@@ -74,19 +75,170 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi)
if ((sub_id= rgi->gtid_sub_id))
{
rgi->gtid_sub_id= 0;
- if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false))
- DBUG_RETURN(1);
- update_state_hash(sub_id, &rgi->current_gtid);
+ if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE)
+ {
+ if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false))
+ DBUG_RETURN(1);
+ update_state_hash(sub_id, &rgi->current_gtid, rgi);
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
}
DBUG_RETURN(0);
}
+/*
+ Check GTID event execution when --gtid-ignore-duplicates.
+
+ The idea with --gtid-ignore-duplicates is that we allow multiple master
+ connections (in multi-source replication) to all receive the same GTIDs and
+ event groups. Only one instance of each is applied; we use the sequence
+ number in the GTID to decide whether a GTID has already been applied.
+
+ So if the seq_no of a GTID (or a higher sequence number) has already been
+ applied, then the event should be skipped. If not then the event should be
+ applied.
+
+ To avoid two master connections tring to apply the same event
+ simultaneously, only one is allowed to work in any given domain at any point
+ in time. The associated Relay_log_info object is called the owner of the
+ domain (and there can be multiple parallel worker threads working in that
+ domain for that Relay_log_info). Any other Relay_log_info/master connection
+ must wait for the domain to become free, or for their GTID to have been
+ applied, before being allowed to proceed.
+
+ Returns:
+ 0 This GTID is already applied, it should be skipped.
+ 1 The GTID is not yet applied; this rli is now the owner, and must apply
+ the event and release the domain afterwards.
+ -1 Error (out of memory to allocate a new element for the domain).
+*/
+int
+rpl_slave_state::check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi)
+{
+ uint32 domain_id= gtid->domain_id;
+ uint32 seq_no= gtid->seq_no;
+ rpl_slave_state::element *elem;
+ int res;
+ bool did_enter_cond;
+ PSI_stage_info old_stage;
+ THD *thd;
+ Relay_log_info *rli= rgi->rli;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ if (!(elem= get_element(domain_id)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ res= -1;
+ goto err;
+ }
+ /*
+ Note that the elem pointer does not change once inserted in the hash. So
+ we can re-use the pointer without looking it up again in the hash after
+ each lock release and re-take.
+ */
+
+ did_enter_cond= false;
+ for (;;)
+ {
+ if (elem->highest_seq_no >= seq_no)
+ {
+ /* This sequence number is already applied, ignore it. */
+ res= 0;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_IGNORE;
+ break;
+ }
+ if (!elem->owner_rli)
+ {
+ /* The domain became free, grab it and apply the event. */
+ elem->owner_rli= rli;
+ elem->owner_count= 1;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_OWNER;
+ res= 1;
+ break;
+ }
+ if (elem->owner_rli == rli)
+ {
+ /* Already own this domain, increment reference count and apply event. */
+ ++elem->owner_count;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_OWNER;
+ res= 1;
+ break;
+ }
+ thd= rgi->thd;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ res= -1;
+ break;
+ }
+ /*
+ Someone else is currently processing this GTID (or an earlier one).
+ Wait for them to complete (or fail), and then check again.
+ */
+ if (!did_enter_cond)
+ {
+ thd->ENTER_COND(&elem->COND_gtid_ignore_duplicates, &LOCK_slave_state,
+ &stage_gtid_wait_other_connection, &old_stage);
+ did_enter_cond= true;
+ }
+ mysql_cond_wait(&elem->COND_gtid_ignore_duplicates,
+ &LOCK_slave_state);
+ }
+
+err:
+ if (did_enter_cond)
+ thd->EXIT_COND(&old_stage);
+ else
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return res;
+}
+
+
+void
+rpl_slave_state::release_domain_owner(rpl_group_info *rgi)
+{
+ element *elem= NULL;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ if (!(elem= get_element(rgi->current_gtid.domain_id)))
+ {
+ /*
+ We cannot really deal with error here, as we are already called in an
+ error handling case (transaction failure and rollback).
+
+ However, get_element() only fails if the element did not exist already
+ and could not be allocated due to out-of-memory - and if it did not
+ exist, then we would not get here in the first place.
+ */
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return;
+ }
+
+ if (rgi->gtid_ignore_duplicate_state == rpl_group_info::GTID_DUPLICATE_OWNER)
+ {
+ uint32 count= elem->owner_count;
+ DBUG_ASSERT(count > 0);
+ DBUG_ASSERT(elem->owner_rli == rgi->rli);
+ --count;
+ elem->owner_count= count;
+ if (count == 0)
+ {
+ elem->owner_rli= NULL;
+ mysql_cond_broadcast(&elem->COND_gtid_ignore_duplicates);
+ }
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
+ mysql_mutex_unlock(&LOCK_slave_state);
+}
+
+
static void
rpl_slave_state_free_element(void *arg)
{
struct rpl_slave_state::element *elem= (struct rpl_slave_state::element *)arg;
mysql_cond_destroy(&elem->COND_wait_gtid);
+ mysql_cond_destroy(&elem->COND_gtid_ignore_duplicates);
my_free(elem);
}
@@ -147,7 +299,7 @@ rpl_slave_state::deinit()
int
rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
- uint64 seq_no)
+ uint64 seq_no, rpl_group_info *rgi)
{
element *elem= NULL;
list_element *list_elem= NULL;
@@ -170,6 +322,25 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
mysql_cond_broadcast(&elem->COND_wait_gtid);
}
+ if (rgi)
+ {
+ if (rgi->gtid_ignore_duplicate_state==rpl_group_info::GTID_DUPLICATE_OWNER)
+ {
+ Relay_log_info *rli= rgi->rli;
+ uint32 count= elem->owner_count;
+ DBUG_ASSERT(count > 0);
+ DBUG_ASSERT(elem->owner_rli == rli);
+ --count;
+ elem->owner_count= count;
+ if (count == 0)
+ {
+ elem->owner_rli= NULL;
+ mysql_cond_broadcast(&elem->COND_gtid_ignore_duplicates);
+ }
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
+ }
+
if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME))))
return 1;
list_elem->server_id= server_id;
@@ -199,7 +370,11 @@ rpl_slave_state::get_element(uint32 domain_id)
elem->domain_id= domain_id;
elem->highest_seq_no= 0;
elem->gtid_waiter= NULL;
+ elem->owner_rli= NULL;
+ elem->owner_count= 0;
mysql_cond_init(key_COND_wait_gtid, &elem->COND_wait_gtid, 0);
+ mysql_cond_init(key_COND_gtid_ignore_duplicates,
+ &elem->COND_gtid_ignore_duplicates, 0);
if (my_hash_insert(&hash, (uchar *)elem))
{
my_free(elem);
@@ -821,7 +996,7 @@ rpl_slave_state::load(THD *thd, char *state_from_master, size_t len,
if (gtid_parser_helper(&state_from_master, end, &gtid) ||
!(sub_id= next_sub_id(gtid.domain_id)) ||
record_gtid(thd, &gtid, sub_id, false, in_statement) ||
- update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no))
+ update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL))
return 1;
if (state_from_master == end)
break;
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index 54f352661a7..3e9e2fce25f 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -91,6 +91,9 @@ struct gtid_waiting {
};
+class Relay_log_info;
+struct rpl_group_info;
+
/*
Replication slave state.
@@ -131,6 +134,19 @@ struct rpl_slave_state
uint64 min_wait_seq_no;
mysql_cond_t COND_wait_gtid;
+ /*
+ For --gtid-ignore-duplicates. The Relay_log_info that currently owns
+ this domain, and the number of worker threads that are active in it.
+
+ The idea is that only one of multiple master connections is allowed to
+ actively apply events for a given domain. Other connections must either
+ discard the events (if the seq_no in GTID shows they have already been
+ applied), or wait to see if the current owner will apply it.
+ */
+ const Relay_log_info *owner_rli;
+ uint32 owner_count;
+ mysql_cond_t COND_gtid_ignore_duplicates;
+
list_element *grab_list() { list_element *l= list; list= NULL; return l; }
void add(list_element *l)
{
@@ -155,7 +171,8 @@ struct rpl_slave_state
void deinit();
void truncate_hash();
ulong count() const { return hash.records; }
- int update(uint32 domain_id, uint32 server_id, uint64 sub_id, uint64 seq_no);
+ int update(uint32 domain_id, uint32 server_id, uint64 sub_id,
+ uint64 seq_no, rpl_group_info *rgi);
int truncate_state_table(THD *thd);
int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
bool in_transaction, bool in_statement);
@@ -171,8 +188,10 @@ struct rpl_slave_state
element *get_element(uint32 domain_id);
int put_back_list(uint32 domain_id, list_element *list);
- void update_state_hash(uint64 sub_id, rpl_gtid *gtid);
+ void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi);
int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi);
+ int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi);
+ void release_domain_owner(rpl_group_info *rgi);
};
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 27f17668849..9c4c819c022 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -20,6 +20,8 @@
struct rpl_parallel_thread_pool global_rpl_thread_pool;
+static void signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi,
+ int err);
static int
rpt_handle_event(rpl_parallel_thread::queued_event *qev,
@@ -94,10 +96,11 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
static void
-finish_event_group(THD *thd, int err, uint64 sub_id,
- rpl_parallel_entry *entry, rpl_group_info *rgi)
+finish_event_group(THD *thd, uint64 sub_id, rpl_parallel_entry *entry,
+ rpl_group_info *rgi)
{
wait_for_commit *wfc= &rgi->commit_orderer;
+ int err;
/*
Remove any left-over registration to wait for a prior commit to
@@ -120,10 +123,10 @@ finish_event_group(THD *thd, int err, uint64 sub_id,
waiting for us will in any case receive the error back from their
wait_for_prior_commit() call.
*/
- if (err)
+ if (rgi->worker_error)
wfc->unregister_wait_for_prior_commit();
- else
- err= wfc->wait_for_prior_commit(thd);
+ else if ((err= wfc->wait_for_prior_commit(thd)))
+ signal_error_to_sql_driver_thread(thd, rgi, err);
thd->wait_for_commit_ptr= NULL;
/*
@@ -150,7 +153,7 @@ finish_event_group(THD *thd, int err, uint64 sub_id,
not yet started should just skip their group, preparing for stop of the
SQL driver thread.
*/
- if (unlikely(rgi->is_error) &&
+ if (unlikely(rgi->worker_error) &&
entry->stop_on_error_sub_id == (uint64)ULONGLONG_MAX)
entry->stop_on_error_sub_id= sub_id;
/*
@@ -163,14 +166,14 @@ finish_event_group(THD *thd, int err, uint64 sub_id,
thd->clear_error();
thd->get_stmt_da()->reset_diagnostics_area();
- wfc->wakeup_subsequent_commits(err);
+ wfc->wakeup_subsequent_commits(rgi->worker_error);
}
static void
-signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi)
+signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi, int err)
{
- rgi->is_error= true;
+ rgi->worker_error= err;
rgi->cleanup_context(thd, true);
rgi->rli->abort_slave= true;
rgi->rli->stop_for_until= false;
@@ -202,7 +205,7 @@ handle_rpl_parallel_thread(void *arg)
struct rpl_parallel_thread::queued_event *events;
bool group_standalone= true;
bool in_event_group= false;
- bool group_skip_for_stop= false;
+ bool skip_event_group= false;
rpl_group_info *group_rgi= NULL;
group_commit_orderer *gco, *tmp_gco;
uint64 event_gtid_sub_id= 0;
@@ -239,6 +242,39 @@ handle_rpl_parallel_thread(void *arg)
thd_proc_info(thd, "Waiting for work from main SQL threads");
thd->set_time();
thd->variables.lock_wait_timeout= LONG_TIMEOUT;
+ /*
+ For now, we need to run the replication parallel worker threads in
+ READ COMMITTED. This is needed because gap locks are not symmetric.
+ For example, a gap lock from a DELETE blocks an insert intention lock,
+ but not vice versa. So an INSERT followed by DELETE can group commit
+ on the master, but if we are unlucky with thread scheduling we can
+ then deadlock on the slave because the INSERT ends up waiting for a
+ gap lock from the DELETE (and the DELETE in turn waits for the INSERT
+ in wait_for_prior_commit()). See also MDEV-5914.
+
+ It should be mostly safe to run in READ COMMITTED in the slave anyway.
+ The commit order is already fixed from on the master, so we do not
+ risk logging into the binlog in an incorrect order between worker
+ threads (one that would cause different results if executed on a
+ lower-level slave that uses this slave as a master). The only
+ potential problem is with transactions run in a different master
+ connection (using multi-source replication), or run directly on the
+ slave by an application; when using READ COMMITTED we are not
+ guaranteed serialisability of binlogged statements.
+
+ In practice, this is unlikely to be an issue. In GTID mode, such
+ parallel transactions from multi-source or application must in any
+ case use a different replication domain, in which case binlog order
+ by definition must be independent between the different domain. Even
+ in non-GTID mode, normally one will assume that the external
+ transactions are not conflicting with those applied by the slave, so
+ that isolation level should make no difference. It would be rather
+ strange if the result of applying query events from one master would
+ depend on the timing and nature of other queries executed from
+ different multi-source connections or done directly on the slave by
+ an application. Still, something to be aware of.
+ */
+ thd->variables.tx_isolation= ISO_READ_COMMITTED;
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
rpt->thd= thd;
@@ -294,7 +330,6 @@ handle_rpl_parallel_thread(void *arg)
continue;
}
- err= 0;
group_rgi= rgi;
gco= rgi->gco;
/* Handle a new event group, which will be initiated by a GTID event. */
@@ -304,6 +339,7 @@ handle_rpl_parallel_thread(void *arg)
PSI_stage_info old_stage;
uint64 wait_count;
+ thd->tx_isolation= (enum_tx_isolation)thd->variables.tx_isolation;
in_event_group= true;
/*
If the standalone flag is set, then this event group consists of a
@@ -346,12 +382,12 @@ handle_rpl_parallel_thread(void *arg)
did_enter_cond= true;
do
{
- if (thd->check_killed() && !rgi->is_error)
+ if (thd->check_killed() && !rgi->worker_error)
{
DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior_killed");
thd->send_kill_message();
slave_output_error_info(rgi->rli, thd);
- signal_error_to_sql_driver_thread(thd, rgi);
+ signal_error_to_sql_driver_thread(thd, rgi, 1);
/*
Even though we were killed, we need to continue waiting for the
prior event groups to signal that we can continue. Otherwise we
@@ -385,13 +421,13 @@ handle_rpl_parallel_thread(void *arg)
point where we can safely stop. So set a flag that will cause us
to skip, rather than execute, the following events.
*/
- group_skip_for_stop= true;
+ skip_event_group= true;
}
else
- group_skip_for_stop= false;
+ skip_event_group= false;
if (unlikely(entry->stop_on_error_sub_id <= rgi->wait_commit_sub_id))
- group_skip_for_stop= true;
+ skip_event_group= true;
else if (rgi->wait_commit_sub_id > entry->last_committed_sub_id)
{
/*
@@ -417,9 +453,31 @@ handle_rpl_parallel_thread(void *arg)
*/
rgi->cleanup_context(thd, true);
thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
- thd->wait_for_commit_ptr->wakeup_subsequent_commits(err);
+ thd->wait_for_commit_ptr->wakeup_subsequent_commits(rgi->worker_error);
}
thd->wait_for_commit_ptr= &rgi->commit_orderer;
+
+ if (opt_gtid_ignore_duplicates)
+ {
+ int res=
+ rpl_global_gtid_slave_state.check_duplicate_gtid(&rgi->current_gtid,
+ rgi);
+ if (res < 0)
+ {
+ /* Error. */
+ slave_output_error_info(rgi->rli, thd);
+ signal_error_to_sql_driver_thread(thd, rgi, 1);
+ }
+ else if (!res)
+ {
+ /* GTID already applied by another master connection, skip. */
+ skip_event_group= true;
+ }
+ else
+ {
+ /* We have to apply the event. */
+ }
+ }
}
group_ending= event_type == XID_EVENT ||
@@ -438,7 +496,7 @@ handle_rpl_parallel_thread(void *arg)
processing between the event groups as a simple way to ensure that
everything is stopped and cleaned up correctly.
*/
- if (!rgi->is_error && !group_skip_for_stop)
+ if (!rgi->worker_error && !skip_event_group)
err= rpt_handle_event(events, rpt);
else
err= thd->wait_for_prior_commit();
@@ -452,19 +510,19 @@ handle_rpl_parallel_thread(void *arg)
events->next= qevs_to_free;
qevs_to_free= events;
- if (err)
+ if (unlikely(err) && !rgi->worker_error)
{
slave_output_error_info(rgi->rli, thd);
- signal_error_to_sql_driver_thread(thd, rgi);
+ signal_error_to_sql_driver_thread(thd, rgi, err);
}
if (end_of_group)
{
in_event_group= false;
- finish_event_group(thd, err, event_gtid_sub_id, entry, rgi);
+ finish_event_group(thd, event_gtid_sub_id, entry, rgi);
rgi->next= rgis_to_free;
rgis_to_free= rgi;
group_rgi= rgi= NULL;
- group_skip_for_stop= false;
+ skip_event_group= false;
DEBUG_SYNC(thd, "rpl_parallel_end_of_group");
}
@@ -518,15 +576,14 @@ handle_rpl_parallel_thread(void *arg)
half-processed event group.
*/
mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
- thd->wait_for_prior_commit();
- finish_event_group(thd, 1, group_rgi->gtid_sub_id,
+ signal_error_to_sql_driver_thread(thd, group_rgi, 1);
+ finish_event_group(thd, group_rgi->gtid_sub_id,
group_rgi->parallel_entry, group_rgi);
- signal_error_to_sql_driver_thread(thd, group_rgi);
in_event_group= false;
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
rpt->free_rgi(group_rgi);
group_rgi= NULL;
- group_skip_for_stop= false;
+ skip_event_group= false;
}
if (!in_event_group)
{
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 0fae3a3bb89..0d0c8c9df70 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1435,7 +1435,8 @@ rpl_load_gtid_slave_state(THD *thd)
if ((err= rpl_global_gtid_slave_state.update(tmp_entry.gtid.domain_id,
tmp_entry.gtid.server_id,
tmp_entry.sub_id,
- tmp_entry.gtid.seq_no)))
+ tmp_entry.gtid.seq_no,
+ NULL)))
{
mysql_mutex_unlock(&rpl_global_gtid_slave_state.LOCK_slave_state);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1488,10 +1489,11 @@ rpl_group_info::reinit(Relay_log_info *rli)
tables_to_lock_count= 0;
trans_retries= 0;
last_event_start_time= 0;
- is_error= false;
+ worker_error= 0;
row_stmt_start_timestamp= 0;
long_find_row_note_printed= false;
did_mark_start_commit= false;
+ gtid_ignore_duplicate_state= GTID_DUPLICATE_NULL;
commit_orderer.reinit();
}
@@ -1631,6 +1633,13 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
/*
+ Ensure we always release the domain for others to process, when using
+ --gtid-ignore-duplicates.
+ */
+ if (gtid_ignore_duplicate_state != GTID_DUPLICATE_NULL)
+ rpl_global_gtid_slave_state.release_domain_owner(this);
+
+ /*
Reset state related to long_find_row notes in the error log:
- timestamp
- flag that decides whether the slave prints or not
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 6db4ce5d61b..48193afce4d 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -569,12 +569,26 @@ struct rpl_group_info
*/
char future_event_master_log_name[FN_REFLEN];
bool is_parallel_exec;
- bool is_error;
+ int worker_error;
/*
Set true when we signalled that we reach the commit phase. Used to avoid
counting one event group twice.
*/
bool did_mark_start_commit;
+ enum {
+ GTID_DUPLICATE_NULL=0,
+ GTID_DUPLICATE_IGNORE=1,
+ GTID_DUPLICATE_OWNER=2
+ };
+ /*
+ When --gtid-ignore-duplicates, this is set to one of the above three
+ values:
+ GTID_DUPLICATE_NULL - Not using --gtid-ignore-duplicates.
+ GTID_DUPLICATE_IGNORE - This gtid already applied, skip the event group.
+ GTID_DUPLICATE_OWNER - We are the current owner of the domain, and must
+ apply the event group and then release the domain.
+ */
+ uint8 gtid_ignore_duplicate_state;
/*
Runtime state for printing a note when slave is taking
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 1a00a58d453..7568a2d786c 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -238,6 +238,7 @@ struct RPL_TABLE_LIST
bool m_tabledef_valid;
table_def m_tabledef;
TABLE *m_conv_table;
+ bool master_had_triggers;
};
diff --git a/sql/slave.cc b/sql/slave.cc
index ecdc6c5d32f..7d24093f27b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2050,6 +2050,39 @@ after_set_capability:
}
}
+ query_str.length(0);
+ if (query_str.append(STRING_WITH_LEN("SET @slave_gtid_ignore_duplicates="),
+ system_charset_info) ||
+ query_str.append_ulonglong(opt_gtid_ignore_duplicates != false))
+ {
+ err_code= ER_OUTOFMEMORY;
+ errmsg= "The slave I/O thread stops because a fatal out-of-memory error "
+ "is encountered when it tries to set @slave_gtid_ignore_duplicates.";
+ sprintf(err_buff, "%s Error: Out of memory", errmsg);
+ goto err;
+ }
+
+ rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code,
+ "Setting @slave_gtid_ignore_duplicates failed with "
+ "error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @slave_gtid_ignore_duplicates.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+
if (mi->rli.until_condition == Relay_log_info::UNTIL_GTID)
{
query_str.length(0);
@@ -3478,18 +3511,46 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
*/
}
- /*
- For GTID, allocate a new sub_id for the given domain_id.
- The sub_id must be allocated in increasing order of binlog order.
- */
- if (typ == GTID_EVENT &&
- event_group_new_gtid(serial_rgi, static_cast<Gtid_log_event *>(ev)))
+ if (typ == GTID_EVENT)
{
- sql_print_error("Error reading relay log event: %s",
- "slave SQL thread aborted because of out-of-memory error");
- mysql_mutex_unlock(&rli->data_lock);
- delete ev;
- DBUG_RETURN(1);
+ Gtid_log_event *gev= static_cast<Gtid_log_event *>(ev);
+
+ /*
+ For GTID, allocate a new sub_id for the given domain_id.
+ The sub_id must be allocated in increasing order of binlog order.
+ */
+ if (event_group_new_gtid(serial_rgi, gev))
+ {
+ sql_print_error("Error reading relay log event: %s", "slave SQL thread "
+ "aborted because of out-of-memory error");
+ mysql_mutex_unlock(&rli->data_lock);
+ delete ev;
+ DBUG_RETURN(1);
+ }
+
+ if (opt_gtid_ignore_duplicates)
+ {
+ serial_rgi->current_gtid.domain_id= gev->domain_id;
+ serial_rgi->current_gtid.server_id= gev->server_id;
+ serial_rgi->current_gtid.seq_no= gev->seq_no;
+ int res= rpl_global_gtid_slave_state.check_duplicate_gtid
+ (&serial_rgi->current_gtid, serial_rgi);
+ if (res < 0)
+ {
+ sql_print_error("Error processing GTID event: %s", "slave SQL "
+ "thread aborted because of out-of-memory error");
+ mysql_mutex_unlock(&rli->data_lock);
+ delete ev;
+ DBUG_RETURN(1);
+ }
+ /*
+ If we need to skip this event group (because the GTID was already
+ applied), then do it using the code for slave_skip_counter, which
+ is able to handle skipping until the end of the event group.
+ */
+ if (!res)
+ rli->slave_skip_counter= 1;
+ }
}
serial_rgi->future_event_relay_log_pos= rli->future_event_relay_log_pos;
@@ -4107,6 +4168,7 @@ err:
if (mi->using_gtid != Master_info::USE_GTID_NO)
flush_master_info(mi, TRUE, TRUE);
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
+ thd->add_status_to_global();
mysql_mutex_lock(&mi->run_lock);
err_during_init:
@@ -4611,6 +4673,7 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME,
if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
flush_relay_log_info(rli);
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
+ thd->add_status_to_global();
mysql_mutex_lock(&rli->run_lock);
err_during_init:
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 84e2c3069c3..92f7ac020f5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -27,7 +27,7 @@
// prepare_create_field
#include "sql_acl.h" // *_ACL
#include "sql_array.h" // Dynamic_array
-#include "log_event.h" // append_query_string, Query_log_event
+#include "log_event.h" // Query_log_event
#include "sql_derived.h" // mysql_handle_derived
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -160,7 +160,8 @@ sp_get_item_value(THD *thd, Item *item, String *str)
buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous)
buf.append(' ');
- append_query_string(thd, cs, result, &buf);
+ append_query_string(cs, &buf, result->ptr(), result->length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
buf.append(" COLLATE '");
buf.append(item->collation.collation->name);
buf.append('\'');
diff --git a/sql/sp_head.h b/sql/sp_head.h
index c1fb455e103..cc598186d08 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -122,6 +122,8 @@ public:
sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name)
: m_db(db), m_name(name), m_explicit_name(use_explicit_name)
{
+ if (lower_case_table_names && m_db.str)
+ m_db.length= my_casedn_str(files_charset_info, m_db.str);
m_qname.str= 0;
m_qname.length= 0;
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 91bdb174d51..cd7ad3f8665 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -727,7 +727,7 @@ static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
-static hash_filo *acl_cache;
+static Hash_filo<acl_entry> *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
static bool check_is_role(TABLE *form);
@@ -924,7 +924,7 @@ my_bool acl_init(bool dont_read_acl_tables)
my_bool return_val;
DBUG_ENTER("acl_init");
- acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
+ acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
(my_hash_get_key) acl_entry_get_key,
(my_hash_free_key) free,
&my_charset_utf8_bin);
@@ -2182,8 +2182,7 @@ ulong acl_get(const char *host, const char *ip,
key_length= (size_t) (end-key);
mysql_mutex_lock(&acl_cache->lock);
- if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
- key_length)))
+ if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
{
db_access=entry->access;
mysql_mutex_unlock(&acl_cache->lock);
@@ -4568,7 +4567,7 @@ static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
*/
static void propagate_role_grants(ACL_ROLE *role,
enum PRIVS_TO_MERGE::what what,
- const char *db, const char *name)
+ const char *db= 0, const char *name= 0)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -5221,6 +5220,8 @@ static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
if (!first)
return 0;
+ DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
+
if (merged == NULL)
{
/*
@@ -5267,8 +5268,6 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
- DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
-
Dynamic_array<GRANT_NAME *> grants;
/* first, collect routine privileges granted to roles in question */
@@ -5354,6 +5353,13 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
return !changed; // don't recurse into the subgraph if privs didn't change
}
+static bool merge_one_role_privileges(ACL_ROLE *grantee)
+{
+ PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
+ grantee->counter= 1;
+ return merge_role_privileges(0, grantee, &data);
+}
+
/*****************************************************************
End of the role privilege propagation and graph traversal code
******************************************************************/
@@ -5831,19 +5837,42 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
DBUG_RETURN(result);
}
-static void append_user(String *str, const char *u, const char *h)
+/**
+ append a user or role name to a buffer that will be later used as an error message
+*/
+static void append_user(THD *thd, String *str,
+ const LEX_STRING *u, const LEX_STRING *h)
{
if (str->length())
str->append(',');
- str->append('\'');
- str->append(u);
+ append_query_string(system_charset_info, str, u->str, u->length,
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
/* hostname part is not relevant for roles, it is always empty */
- if (*h)
+ if (u->length == 0 || h->length != 0)
{
- str->append(STRING_WITH_LEN("'@'"));
- str->append(h);
+ str->append('@');
+ append_query_string(system_charset_info, str, h->str, h->length,
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
}
- str->append('\'');
+}
+
+static void append_user(THD *thd, String *str, LEX_USER *user)
+{
+ append_user(thd, str, & user->user, & user->host);
+}
+
+/**
+ append a string to a buffer that will be later used as an error message
+
+ @note
+ a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
+ neither quoted nor escaped.
+*/
+static void append_str(String *str, const char *s, size_t l)
+{
+ if (str->length())
+ str->append(',');
+ str->append(s, l);
}
static int can_grant_role_callback(ACL_USER_BASE *grantee,
@@ -5961,13 +5990,15 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
if (!thd->security_ctx->priv_role[0])
{
my_error(ER_INVALID_ROLE, MYF(0), "NONE");
- append_user(&wrong_users, "NONE", "");
+ append_str(&wrong_users, STRING_WITH_LEN("NONE"));
result= 1;
continue;
}
if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
{
- append_user(&wrong_users, thd->security_ctx->priv_role, "");
+ LEX_STRING ls= { thd->security_ctx->priv_role,
+ strlen(thd->security_ctx->priv_role) };
+ append_user(thd, &wrong_users, &ls, &empty_lex_str);
result= 1;
continue;
}
@@ -5975,7 +6006,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
/* can not grant current_role to current_role */
if (granted_role->user.str == current_role.str)
{
- append_user(&wrong_users, thd->security_ctx->priv_role, "");
+ append_user(thd, &wrong_users, &role_as_user->user, &empty_lex_str);
result= 1;
continue;
}
@@ -6002,7 +6033,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
{
if (is_invalid_role_name(username.str))
{
- append_user(&wrong_users, username.str, "");
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
result= 1;
continue;
}
@@ -6028,7 +6059,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
false, create_new_user,
no_auto_create_user))
{
- append_user(&wrong_users, username.str, hostname.str);
+ append_user(thd, &wrong_users, &username, &hostname);
result= 1;
continue;
}
@@ -6040,7 +6071,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
if (!grantee)
{
- append_user(&wrong_users, username.str, hostname.str);
+ append_user(thd, &wrong_users, &username, &hostname);
result= 1;
continue;
}
@@ -6062,7 +6093,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
if (role_as_user &&
traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
{
- append_user(&wrong_users, username.str, "");
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
result= 1;
undo_add_role_user_mapping(grantee, role);
continue;
@@ -6074,7 +6105,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
/* grant was already removed or never existed */
if (!hash_entry)
{
- append_user(&wrong_users, username.str, hostname.str);
+ append_user(thd, &wrong_users, &username, &hostname);
result= 1;
continue;
}
@@ -6095,7 +6126,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
thd->lex->with_admin_option,
hash_entry, revoke))
{
- append_user(&wrong_users, username.str, "");
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
result= 1;
if (!revoke)
{
@@ -6116,8 +6147,8 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
Only need to propagate grants when granting/revoking a role to/from
a role
*/
- if (role_as_user)
- propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL, 0, 0);
+ if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
+ propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -6265,7 +6296,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (Str->is_role())
propagate_role_grants(find_acl_role(Str->user.str),
db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
- db, 0);
+ db);
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -7119,16 +7150,22 @@ bool check_grant_db(THD *thd, const char *db)
{
Security_context *sctx= thd->security_ctx;
char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
- char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2];
+ char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
uint len, UNINIT_VAR(len2);
bool error= TRUE;
- end= strmov(helping, sctx->priv_user) + 1;
- end= strnmov(end, db, helping + sizeof(helping) - end);
+ tmp_db= strmov(helping, sctx->priv_user) + 1;
+ end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
if (end >= helping + sizeof(helping)) // db name was truncated
return 1; // no privileges for an invalid db name
+ if (lower_case_table_names)
+ {
+ end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
+ db=tmp_db;
+ }
+
len= (uint) (end - helping) + 1;
/*
@@ -8671,7 +8708,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
if (drop)
{
/* all grants must be revoked from this role by now. propagate this */
- propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL, 0, 0);
+ propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
// delete the role from cross-reference arrays
for (uint i=0; i < acl_role->role_grants.elements; i++)
@@ -9137,28 +9174,6 @@ end:
DBUG_RETURN(result);
}
-static void append_user(String *str, LEX_USER *user)
-{
- if (str->length())
- str->append(',');
- str->append('\'');
- str->append(user->user.str);
- /* hostname part is not relevant for roles, it is always empty */
- if (!user->is_role())
- {
- str->append(STRING_WITH_LEN("'@'"));
- str->append(user->host.str);
- }
- str->append('\'');
-}
-
-static void append_str(String *str, const char *s, size_t l)
-{
- if (str->length())
- str->append(',');
- str->append(s, l);
-}
-
/*
Create a list of users.
@@ -9212,7 +9227,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
if (handle_as_role && is_invalid_role_name(user_name->user.str))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
}
@@ -9226,7 +9241,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
*/
if (handle_grant_data(tables, 0, user_name, NULL))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
@@ -9235,7 +9250,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
some_users_created= TRUE;
if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
}
@@ -9260,7 +9275,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
&user_name->user, true,
NULL, false))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
if (grantee)
undo_add_role_user_mapping(grantee, role);
result= TRUE;
@@ -9333,14 +9348,14 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
if (handle_as_role != user_name->is_role())
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
}
if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
}
@@ -9413,13 +9428,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
tmp_user_to= user_list++;
if (!(user_from= get_current_user(thd, tmp_user_from, false)))
{
- append_user(&wrong_users, user_from);
+ append_user(thd, &wrong_users, user_from);
result= TRUE;
continue;
}
if (!(user_to= get_current_user(thd, tmp_user_to, false)))
{
- append_user(&wrong_users, user_to);
+ append_user(thd, &wrong_users, user_to);
result= TRUE;
continue;
}
@@ -9434,7 +9449,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
handle_grant_data(tables, 0, user_from, user_to) <= 0)
{
/* NOTE TODO renaming roles is not yet implemented */
- append_user(&wrong_users, user_from);
+ append_user(thd, &wrong_users, user_from);
result= TRUE;
continue;
}
@@ -9678,7 +9693,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
*/
if (lex_user->is_role())
{
- propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL, 0, 0);
+ propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
}
}
@@ -11751,9 +11766,6 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
mpvio->cached_client_reply.pkt= 0;
mpvio->packets_read++;
- if (mpvio->make_it_fail)
- goto err;
-
DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
}
@@ -11788,21 +11800,13 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
else
*buf= mpvio->thd->net.read_pos;
- if (mpvio->make_it_fail)
- goto err;
-
DBUG_RETURN((int)pkt_len);
err:
if (mpvio->status == MPVIO_EXT::FAILURE)
{
if (!mpvio->thd->is_error())
- {
- if (mpvio->make_it_fail)
- login_failed_error(mpvio->thd);
- else
- my_error(ER_HANDSHAKE_ERROR, MYF(0));
- }
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
}
DBUG_RETURN(-1);
}
@@ -12067,7 +12071,12 @@ bool acl_authenticate(THD *thd, uint connect_errors,
auth_plugin_name= &mpvio.acl_user->plugin;
res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
-
+ if (mpvio.make_it_fail && res == CR_OK)
+ {
+ mpvio.status= MPVIO_EXT::FAILURE;
+ res= CR_ERROR;
+ }
+
Security_context *sctx= thd->security_ctx;
const ACL_USER *acl_user= mpvio.acl_user;
@@ -12360,6 +12369,8 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
DBUG_RETURN(CR_OK);
#endif
+ DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
+
if (pkt_len == 0) /* no password */
DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 6989b76accb..518ebdc511d 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -736,6 +736,17 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
compl_result_code= update_statistics_for_table(thd, table->table);
if (compl_result_code)
result_code= HA_ADMIN_FAILED;
+ else
+ {
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Engine-independent statistics collected"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ }
}
if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter)
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index ac3f7f25518..9b7ea58bee6 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -1080,7 +1080,7 @@ int collect_string(String *element,
else
info->found = 1;
info->str->append('\'');
- if (append_escaped(info->str, element))
+ if (info->str->append_for_single_quote(element))
return 1;
info->str->append('\'');
return 0;
@@ -1239,56 +1239,3 @@ uint check_ulonglong(const char *str, uint length)
return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
} /* check_ulonlong */
-
-/*
- Quote special characters in a string.
-
- SYNOPSIS
- append_escaped(to_str, from_str)
- to_str (in) A pointer to a String.
- from_str (to) A pointer to an allocated string
-
- DESCRIPTION
- append_escaped() takes a String type variable, where it appends
- escaped the second argument. Only characters that require escaping
- will be escaped.
-
- RETURN VALUES
- 0 Success
- 1 Out of memory
-*/
-
-bool append_escaped(String *to_str, String *from_str)
-{
- char *from, *end, c;
-
- if (to_str->realloc(to_str->length() + from_str->length()))
- return 1;
-
- from= (char*) from_str->ptr();
- end= from + from_str->length();
- for (; from < end; from++)
- {
- c= *from;
- switch (c) {
- case '\0':
- c= '0';
- break;
- case '\032':
- c= 'Z';
- break;
- case '\\':
- case '\'':
- break;
- default:
- goto normal_character;
- }
- if (to_str->append('\\'))
- return 1;
-
- normal_character:
- if (to_str->append(c))
- return 1;
- }
- return 0;
-}
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 8bac29de5a3..3d3662c3f4f 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -365,6 +365,4 @@ public:
List<Item> &field_list);
};
-bool append_escaped(String *to_str, String *from_str);
-
#endif /* SQL_ANALYSE_INCLUDED */
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index a92f69f3da4..84f2d95c5da 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -84,7 +84,7 @@ static void general_class_handler(THD *thd, uint event_subtype, va_list ap)
event.general_rows= (unsigned long long) va_arg(ap, ha_rows);
event.database= va_arg(ap, const char *);
event.database_length= va_arg(ap, unsigned int);
- event.query_id= (unsigned long long) thd->query_id;
+ event.query_id= (unsigned long long) (thd ? thd->query_id : 0);
event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
}
@@ -134,7 +134,7 @@ static void table_class_handler(THD *thd, uint event_subclass, va_list ap)
event.new_database_length= va_arg(ap, unsigned int);
event.new_table= va_arg(ap, const char *);
event.new_table_length= va_arg(ap, unsigned int);
- event.query_id= (unsigned long long) thd->query_id;
+ event.query_id= (unsigned long long) (thd ? thd->query_id : 0);
event_class_dispatch(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 85e7abb23e9..68106f099cc 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -96,11 +96,13 @@ void mysql_audit_general_log(THD *thd, time_t time,
{
CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client
: global_system_variables.character_set_client;
+ const char *db= thd ? thd->db : "";
+ size_t db_length= thd ? thd->db_length : 0;
mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, MYSQL_AUDIT_GENERAL_LOG,
0, time, user, userlen, cmd, cmdlen,
query, querylen, clientcs, (ha_rows) 0,
- thd->db, thd->db_length);
+ db, db_length);
}
}
@@ -129,6 +131,8 @@ void mysql_audit_general(THD *thd, uint event_subtype,
char user_buff[MAX_USER_HOST_SIZE];
CSET_STRING query;
ha_rows rows;
+ const char *db;
+ size_t db_length;
if (thd)
{
@@ -136,18 +140,22 @@ void mysql_audit_general(THD *thd, uint event_subtype,
user= user_buff;
userlen= make_user_name(thd, user_buff);
rows= thd->get_stmt_da()->current_row_for_warning();
+ db= thd->db;
+ db_length= thd->db_length;
}
else
{
user= 0;
userlen= 0;
rows= 0;
+ db= "";
+ db_length= 0;
}
mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
error_code, time, user, userlen, msg, msglen,
query.str(), query.length(), query.charset(), rows,
- thd->db, thd->db_length);
+ db, db_length);
}
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 986d2b77288..2f304b21706 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -266,7 +266,7 @@ uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
NOTES
One gets only a list of tables for which one has any kind of privilege.
db and table names are allocated in result struct, so one doesn't need
- a lock on LOCK_open when traversing the return list.
+ a lock when traversing the return list.
RETURN VALUES
NULL Error (Probably OOM)
@@ -312,13 +312,13 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
share->db.str)+1,
share->table_name.str);
(*start_list)->in_use= 0;
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TABLE *table;
while ((table= it++))
if (table->in_use)
++(*start_list)->in_use;
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
(*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
*start_list=0;
@@ -339,7 +339,6 @@ void intern_close_table(TABLE *table)
table->s ? table->s->db.str : "?",
table->s ? table->s->table_name.str : "?",
(long) table));
- mysql_mutex_assert_not_owner(&LOCK_open);
free_io_cache(table);
delete table->triggers;
@@ -372,7 +371,7 @@ void free_io_cache(TABLE *table)
@param share Table share.
- @pre Caller should have LOCK_open mutex.
+ @pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex.
*/
void kill_delayed_threads_for_table(TABLE_SHARE *share)
@@ -380,7 +379,7 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share)
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TABLE *tab;
- mysql_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_owner(&share->tdc.LOCK_table_share);
if (!delayed_insert_threads)
return;
@@ -660,6 +659,12 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd)
{
DBUG_ENTER("mark_temp_tables_as_free_for_reuse");
+ if (thd->query_id == 0)
+ {
+ /* Thread has not executed any statement and has not used any tmp tables */
+ DBUG_VOID_RETURN;
+ }
+
thd->lock_temporary_tables();
for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
{
@@ -772,8 +777,6 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
static void close_open_tables(THD *thd)
{
- mysql_mutex_assert_not_owner(&LOCK_open);
-
DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
while (thd->open_tables)
@@ -820,7 +823,6 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
memcpy(key, share->table_cache_key.str, key_length);
- mysql_mutex_assert_not_owner(&LOCK_open);
for (TABLE **prev= &thd->open_tables; *prev; )
{
TABLE *table= *prev;
@@ -1016,7 +1018,6 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
table->s->table_name.str, (long) table));
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
The metadata lock must be released after giving back
@@ -1047,7 +1048,10 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
table->file->ha_reset();
}
- /* Do this *before* entering the LOCK_open critical section. */
+ /*
+ Do this *before* entering the TABLE_SHARE::tdc.LOCK_table_share
+ critical section.
+ */
if (table->file != NULL)
table->file->unbind_psi();
@@ -1347,6 +1351,17 @@ retry:
DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name));
for (TABLE_LIST *tl= table_list;;)
{
+ if (tl &&
+ tl->select_lex && tl->select_lex->master_unit() &&
+ tl->select_lex->master_unit()->executed)
+ {
+ /*
+ There is no sense to check tables of already executed parts
+ of the query
+ */
+ tl= tl->next_global;
+ continue;
+ }
/*
Table is unique if it is present only once in the global list
of tables and once in the list of table locks.
@@ -1361,9 +1376,7 @@ retry:
/* Skip if table alias does not match. */
if (check_alias)
{
- if (lower_case_table_names ?
- my_strcasecmp(files_charset_info, t_alias, res->alias) :
- strcmp(t_alias, res->alias))
+ if (my_strcasecmp(table_alias_charset, t_alias, res->alias))
goto next;
}
@@ -2726,6 +2739,38 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
reset();
}
+
+/**
+ Remove all meta data locks associated with table and release locked
+ table mode if there is no locked tables anymore
+*/
+
+void
+Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
+{
+ /*
+ Ensure we are in locked table mode.
+ As this function is only called on error condition it's better
+ to check this condition here than in the caller.
+ */
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES)
+ return;
+
+ if (mdl_ticket)
+ {
+ /*
+ Under LOCK TABLES we may have several instances of table open
+ and locked and therefore have to remove several metadata lock
+ requests associated with them.
+ */
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ }
+
+ if (thd->lock->table_count == 0)
+ unlock_locked_tables(thd);
+}
+
+
/*
Free memory allocated for storing locks
*/
@@ -5420,9 +5465,6 @@ bool restart_trans_for_tables(THD *thd, TABLE_LIST *table)
{
DBUG_ENTER("restart_trans_for_tables");
- if (!thd->locked_tables_mode)
- DBUG_RETURN(FALSE);
-
for (; table; table= table->next_global)
{
if (table->placeholder())
@@ -8325,9 +8367,16 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
meaningful message than ER_BAD_TABLE_ERROR.
*/
if (!table_name)
- my_message(ER_NO_TABLES_USED, ER(ER_NO_TABLES_USED), MYF(0));
+ my_error(ER_NO_TABLES_USED, MYF(0));
+ else if (!db_name && !thd->db)
+ my_error(ER_NO_DB_ERROR, MYF(0));
else
- my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name);
+ {
+ char name[FN_REFLEN];
+ my_snprintf(name, sizeof(name), "%s.%s",
+ db_name ? db_name : thd->db, table_name);
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
+ }
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 7b48d86603e..6a9c5dacf5a 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1715,9 +1715,7 @@ void THD::init_for_queries()
void THD::change_user(void)
{
- mysql_mutex_lock(&LOCK_status);
- add_to_status(&global_status_var, &status_var);
- mysql_mutex_unlock(&LOCK_status);
+ add_status_to_global();
cleanup();
reset_killed();
@@ -1748,6 +1746,7 @@ void THD::cleanup(void)
#endif
mysql_ha_cleanup(this);
+ locked_tables_list.unlock_locked_tables(this);
close_temporary_tables(this);
@@ -1755,8 +1754,6 @@ void THD::cleanup(void)
trans_rollback(this);
xid_cache_delete(&transaction.xid_state);
- locked_tables_list.unlock_locked_tables(this);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
@@ -2700,12 +2697,6 @@ select_result::select_result()
thd=current_thd;
}
-void select_result::send_error(uint errcode,const char *err)
-{
- my_message(errcode, err, MYF(0));
-}
-
-
void select_result::cleanup()
{
/* do nothing */
@@ -2854,20 +2845,6 @@ bool select_send::send_eof()
Handling writing to file
************************************************************************/
-void select_to_file::send_error(uint errcode,const char *err)
-{
- my_message(errcode, err, MYF(0));
- if (file > 0)
- {
- (void) end_io_cache(&cache);
- mysql_file_close(file, MYF(0));
- /* Delete file on error */
- mysql_file_delete(key_select_to_file, path, MYF(0));
- file= -1;
- }
-}
-
-
bool select_to_file::send_eof()
{
int error= MY_TEST(end_io_cache(&cache));
@@ -4499,6 +4476,12 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd)
return(thd->slave_thread);
}
+/* Returns true for a worker thread in parallel replication. */
+extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd)
+{
+ return thd->rgi_slave && thd->rgi_slave->is_parallel_exec;
+}
+
extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
{
return(thd->transaction.all.modified_non_trans_table);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ab11249cafd..4e1caca581a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -92,6 +92,9 @@ enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
SLAVE_EXEC_MODE_IDEMPOTENT,
SLAVE_EXEC_MODE_LAST_BIT };
+enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO,
+ SLAVE_RUN_TRIGGERS_FOR_RBR_YES,
+ SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
enum enum_mark_columns
@@ -768,6 +771,11 @@ typedef struct system_status_var
#define last_system_status_var questions
#define last_cleared_system_status_var memory_used
+void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
+
+void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
+ STATUS_VAR *dec_var);
+
void mark_transaction_to_rollback(THD *thd, bool all);
@@ -1535,6 +1543,7 @@ public:
MYF(MY_THREAD_SPECIFIC));
}
void unlock_locked_tables(THD *thd);
+ void unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket);
~Locked_tables_list()
{
reset();
@@ -3675,6 +3684,13 @@ public:
/* Wake this thread up from wait_for_wakeup_ready(). */
void signal_wakeup_ready();
+ void add_status_to_global()
+ {
+ mysql_mutex_lock(&LOCK_status);
+ add_to_status(&global_status_var, &status_var);
+ mysql_mutex_unlock(&LOCK_status);
+ }
+
wait_for_commit *wait_for_commit_ptr;
int wait_for_prior_commit()
{
@@ -3885,7 +3901,6 @@ public:
{ return fields.elements; }
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
- virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
/**
Check if this query returns a result set and therefore is allowed in
@@ -4012,7 +4027,6 @@ public:
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
{ path[0]=0; }
~select_to_file();
- void send_error(uint errcode,const char *err);
bool send_eof();
void cleanup();
};
@@ -4085,7 +4099,6 @@ class select_insert :public select_result_interceptor {
virtual int send_data(List<Item> &items);
virtual void store_values(List<Item> &values);
virtual bool can_rollback_data() { return 0; }
- void send_error(uint errcode,const char *err);
bool send_eof();
virtual void abort_result_set();
/* not implemented: select_insert is never re-used in prepared statements */
@@ -4123,7 +4136,6 @@ public:
int binlog_show_create_table(TABLE **tables, uint count);
void store_values(List<Item> &values);
- void send_error(uint errcode,const char *err);
bool send_eof();
virtual void abort_result_set();
virtual bool can_rollback_data() { return 1; }
@@ -4641,7 +4653,7 @@ class multi_delete :public select_result_interceptor
bool delete_while_scanning;
/*
error handling (rollback and binlogging) can happen in send_eof()
- so that afterward send_error() needs to find out that.
+ so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;
@@ -4651,7 +4663,6 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
- void send_error(uint errcode,const char *err);
int do_deletes();
int do_table_deletes(TABLE *table, bool ignore);
bool send_eof();
@@ -4687,7 +4698,7 @@ class multi_update :public select_result_interceptor
bool ignore;
/*
error handling (rollback and binlogging) can happen in send_eof()
- so that afterward send_error() needs to find out that.
+ so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;
@@ -4701,7 +4712,6 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
- void send_error(uint errcode,const char *err);
int do_updates();
bool send_eof();
inline ha_rows num_found()
@@ -4875,10 +4885,6 @@ public:
*/
#define CF_SKIP_QUESTIONS (1U << 1)
-void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
-
-void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
- STATUS_VAR *dec_var);
void mark_transaction_to_rollback(THD *thd, bool all);
/* Inline functions */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index fcde2b4acbd..063b90a6780 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -49,7 +49,7 @@
#define MAX_DROP_TABLE_Q_LEN 1024
-const char *del_exts[]= {".BAK", ".TMD",".opt", NullS};
+const char *del_exts[]= {".BAK", ".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
@@ -78,6 +78,29 @@ typedef struct my_dbopt_st
} my_dbopt_t;
+/**
+ Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
+
+ The function allows to compare database names according to the MariaDB
+ rules. The database names db1 and db2 are equal if:
+ - db1 is NULL and db2 is NULL;
+ or
+ - db1 is not-NULL, db2 is not-NULL, db1 is equal to db2 in
+ table_alias_charset
+
+ This is the same rules as we use for filenames.
+*/
+
+static inline bool
+cmp_db_names(const char *db1_name,
+ const char *db2_name)
+{
+ return ((!db1_name && !db2_name) ||
+ (db1_name && db2_name &&
+ my_strcasecmp(table_alias_charset, db1_name, db2_name) == 0));
+}
+
+
/*
Function we use in the creation of our hash to get key.
*/
@@ -159,8 +182,7 @@ bool my_dboptions_cache_init(void)
if (!dboptions_init)
{
dboptions_init= 1;
- error= my_hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
+ error= my_hash_init(&dboptions, table_alias_charset,
32, 0, 0, (my_hash_get_key) dboptions_get_key,
free_dbopt,0);
}
@@ -192,8 +214,7 @@ void my_dbopt_cleanup(void)
{
mysql_rwlock_wrlock(&LOCK_dboptions);
my_hash_free(&dboptions);
- my_hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
+ my_hash_init(&dboptions, table_alias_charset,
32, 0, 0, (my_hash_get_key) dboptions_get_key,
free_dbopt,0);
mysql_rwlock_unlock(&LOCK_dboptions);
@@ -558,7 +579,17 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1);
}
- if (lock_schema_name(thd, db))
+ char db_tmp[SAFE_NAME_LEN], *dbnorm;
+ if (lower_case_table_names)
+ {
+ strmake_buf(db_tmp, db);
+ my_casedn_str(system_charset_info, db_tmp);
+ dbnorm= db_tmp;
+ }
+ else
+ dbnorm= db;
+
+ if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(-1);
/* Check directory */
@@ -762,8 +793,17 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_db");
+ char db_tmp[SAFE_NAME_LEN], *dbnorm;
+ if (lower_case_table_names)
+ {
+ strmake_buf(db_tmp, db);
+ my_casedn_str(system_charset_info, db_tmp);
+ dbnorm= db_tmp;
+ }
+ else
+ dbnorm= db;
- if (lock_schema_name(thd, db))
+ if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(true);
length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
@@ -788,7 +828,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
}
}
- if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables))
+ if (find_db_tables_and_rm_known_files(thd, dirp, dbnorm, path, &tables))
goto exit;
/*
@@ -805,7 +845,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
/* Lock all tables and stored routines about to be dropped. */
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
0) ||
- lock_db_routines(thd, db))
+ lock_db_routines(thd, dbnorm))
goto exit;
if (!in_bootstrap)
@@ -852,10 +892,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
ha_drop_database(path);
tmp_disable_binlog(thd);
- query_cache_invalidate1(thd, db);
- (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
+ query_cache_invalidate1(thd, dbnorm);
+ (void) sp_drop_db_routines(thd, dbnorm); /* @todo Do not ignore errors */
#ifdef HAVE_EVENT_SCHEDULER
- Events::drop_schema_events(thd, db);
+ Events::drop_schema_events(thd, dbnorm);
#endif
reenable_binlog(thd);
@@ -966,7 +1006,7 @@ exit:
SELECT DATABASE() in the future). For this we free() thd->db and set
it to 0.
*/
- if (thd->db && !strcmp(thd->db, db) && !error)
+ if (thd->db && cmp_db_names(thd->db, db) && !error)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
my_dirend(dirp);
DBUG_RETURN(error);
@@ -1300,27 +1340,6 @@ static void backup_current_db_name(THD *thd,
/**
- Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
-
- The function allows to compare database names according to the MySQL
- rules. The database names db1 and db2 are equal if:
- - db1 is NULL and db2 is NULL;
- or
- - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
- db2 in system character set (UTF8).
-*/
-
-static inline bool
-cmp_db_names(const char *db1_name,
- const char *db2_name)
-{
- return ((!db1_name && !db2_name) ||
- (db1_name && db2_name &&
- my_strcasecmp(system_charset_info, db1_name, db2_name) == 0));
-}
-
-
-/**
@brief Change the current database and its attributes unconditionally.
@param thd thread handle
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 014da5cfbaf..04c03e0c23e 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -521,16 +521,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
init_ftfuncs(thd, select_lex, 1);
THD_STAGE_INFO(thd, stage_updating);
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
+ if (table->prepare_triggers_for_delete_stmt_or_event())
{
- /*
- The table has AFTER DELETE triggers that might access to subject table
- and therefore might need delete to be done immediately. So we turn-off
- the batching.
- */
- (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
will_batch= FALSE;
}
else
@@ -942,17 +934,7 @@ multi_delete::initialize_tables(JOIN *join)
transactional_tables= 1;
else
normal_tables= 1;
- if (tbl->triggers &&
- tbl->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to subject
- table and therefore might need delete to be done immediately.
- So we turn-off the batching.
- */
- (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
+ tbl->prepare_triggers_for_delete_stmt_or_event();
tbl->prepare_for_position();
tbl->mark_columns_needed_for_delete();
}
@@ -1069,17 +1051,6 @@ int multi_delete::send_data(List<Item> &values)
}
-void multi_delete::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("multi_delete::send_error");
-
- /* First send error what ever it is ... */
- my_message(errcode, err, MYF(0));
-
- DBUG_VOID_RETURN;
-}
-
-
void multi_delete::abort_result_set()
{
DBUG_ENTER("multi_delete::abort_result_set");
@@ -1322,7 +1293,7 @@ bool multi_delete::send_eof()
}
}
if (local_error != 0)
- error_handled= TRUE; // to force early leave from ::send_error()
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
if (!local_error)
{
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 00ea0977a07..c2941d55dcb 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -68,8 +68,10 @@ mysql_handle_derived(LEX *lex, uint phases)
{
bool res= FALSE;
THD *thd= lex->thd;
+ DBUG_ENTER("mysql_handle_derived");
+ DBUG_PRINT("enter", ("phases: 0x%x", phases));
if (!lex->derived_tables)
- return FALSE;
+ DBUG_RETURN(FALSE);
lex->thd->derived_tables_processing= TRUE;
@@ -127,7 +129,7 @@ mysql_handle_derived(LEX *lex, uint phases)
}
}
lex->thd->derived_tables_processing= FALSE;
- return res;
+ DBUG_RETURN(res);
}
/*
@@ -166,8 +168,10 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
THD *thd= lex->thd;
uint8 allowed_phases= (derived->is_merged_derived() ? DT_PHASES_MERGE :
DT_PHASES_MATERIALIZE);
+ DBUG_ENTER("mysql_handle_single_derived");
+ DBUG_PRINT("enter", ("phases: 0x%x allowed: 0x%x", phases, allowed_phases));
if (!lex->derived_tables)
- return FALSE;
+ DBUG_RETURN(FALSE);
lex->thd->derived_tables_processing= TRUE;
@@ -189,7 +193,7 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
break;
}
lex->thd->derived_tables_processing= FALSE;
- return res;
+ DBUG_RETURN(res);
}
@@ -354,16 +358,17 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
uint tablenr;
SELECT_LEX *parent_lex= derived->select_lex;
Query_arena *arena, backup;
+ DBUG_ENTER("mysql_derived_merge");
if (derived->merged)
- return FALSE;
+ DBUG_RETURN(FALSE);
if (dt_select->uncacheable & UNCACHEABLE_RAND)
{
/* There is random function => fall back to materialization. */
derived->change_refs_to_fields();
derived->set_materialized_derived();
- return FALSE;
+ DBUG_RETURN(FALSE);
}
if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
@@ -467,7 +472,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
exit_merge:
if (arena)
thd->restore_active_arena(arena, &backup);
- return res;
+ DBUG_RETURN(res);
}
@@ -492,14 +497,15 @@ exit_merge:
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
+ DBUG_ENTER("mysql_derived_merge_for_insert");
if (derived->merged_for_insert)
- return FALSE;
+ DBUG_RETURN(FALSE);
if (derived->is_materialized_derived())
- return mysql_derived_prepare(thd, lex, derived);
+ DBUG_RETURN(mysql_derived_prepare(thd, lex, derived));
if (!derived->is_multitable())
{
if (!derived->single_table_updatable())
- return derived->create_field_translation(thd);
+ DBUG_RETURN(derived->create_field_translation(thd));
if (derived->merge_underlying_list)
{
derived->table= derived->merge_underlying_list->table;
@@ -507,7 +513,7 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->merged_for_insert= TRUE;
}
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -761,9 +767,10 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
SELECT_LEX *save_current_select= lex->current_select;
bool res= FALSE;
+ DBUG_ENTER("mysql_derived_optimize");
if (unit->optimized)
- return FALSE;
+ DBUG_RETURN(FALSE);
lex->current_select= first_select;
if (unit->is_union())
@@ -803,7 +810,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
}
err:
lex->current_select= save_current_select;
- return res;
+ DBUG_RETURN(res);
}
@@ -825,11 +832,12 @@ err:
bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
{
+ DBUG_ENTER("mysql_derived_create");
TABLE *table= derived->table;
SELECT_LEX_UNIT *unit= derived->get_unit();
if (table->created)
- return FALSE;
+ DBUG_RETURN(FALSE);
select_union *result= (select_union*)unit->result;
if (table->s->db_type() == TMP_ENGINE_HTON)
{
@@ -839,13 +847,13 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
&result->tmp_table_param.recinfo,
(unit->first_select()->options |
thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS)))
- return(TRUE);
+ DBUG_RETURN(TRUE);
}
if (open_tmp_table(table))
- return TRUE;
+ DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_WRITE_CACHE);
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -874,11 +882,12 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
+ DBUG_ENTER("mysql_derived_fill");
SELECT_LEX_UNIT *unit= derived->get_unit();
bool res= FALSE;
if (unit->executed && !unit->uncacheable && !unit->describe)
- return FALSE;
+ DBUG_RETURN(FALSE);
/*check that table creation passed without problems. */
DBUG_ASSERT(derived->table && derived->table->created);
SELECT_LEX *first_select= unit->first_select();
@@ -920,7 +929,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->cleanup();
lex->current_select= save_current_select;
- return res;
+ DBUG_RETURN(res);
}
@@ -943,6 +952,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
{
+ DBUG_ENTER("mysql_derived_reinit");
st_select_lex_unit *unit= derived->get_unit();
if (derived->table)
@@ -952,6 +962,6 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
/* for derived tables & PS (which can't be reset by Item_subquery) */
unit->reinit_exec_mechanism();
unit->set_thd(thd);
- return FALSE;
+ DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 6b961ac2262..a6c471f67d5 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -1115,8 +1115,6 @@ void mysql_ha_flush(THD *thd)
SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
- mysql_mutex_assert_not_owner(&LOCK_open);
-
/*
Don't try to flush open HANDLERs when we're working with
system tables. The main MDL context is backed up and we can't
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 7901e2b681e..2097f636dd2 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -373,48 +373,6 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
return 0;
}
-/*
- Prepare triggers for INSERT-like statement.
-
- SYNOPSIS
- prepare_triggers_for_insert_stmt()
- table Table to which insert will happen
-
- NOTE
- Prepare triggers for INSERT-like statement by marking fields
- used by triggers and inform handlers that batching of UPDATE/DELETE
- cannot be done if there are BEFORE UPDATE/DELETE triggers.
-*/
-
-void prepare_triggers_for_insert_stmt(TABLE *table)
-{
- if (table->triggers)
- {
- if (table->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to
- subject table and therefore might need delete to be done
- immediately. So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
- if (table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
- }
- table->mark_columns_needed_for_insert();
-}
-
-
/**
Upgrade table-level lock of INSERT statement to TL_WRITE if
a more concurrent lock is infeasible for some reason. This is
@@ -902,7 +860,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
- prepare_triggers_for_insert_stmt(table);
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
if (table_list->prepare_where(thd, 0, TRUE) ||
@@ -3547,7 +3506,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table_list->prepare_check_option(thd));
if (!res)
- prepare_triggers_for_insert_stmt(table);
+ {
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
+ }
DBUG_RETURN(res);
}
@@ -3684,16 +3646,6 @@ void select_insert::store_values(List<Item> &values)
TRG_EVENT_INSERT);
}
-void select_insert::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("select_insert::send_error");
-
- my_message(errcode, err, MYF(0));
-
- DBUG_VOID_RETURN;
-}
-
-
bool select_insert::send_eof()
{
int error;
@@ -4024,6 +3976,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
*/
DBUG_ASSERT(0);
}
+ DBUG_ASSERT(create_table->table == create_info->table);
}
}
else
@@ -4237,8 +4190,9 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
result= store_create_info(thd, &tmp_table_list, &query, create_info,
/* show_database */ TRUE,
- MY_TEST(create_info->options &
- HA_LEX_CREATE_REPLACE));
+ MY_TEST(create_info->org_options &
+ HA_LEX_CREATE_REPLACE) ||
+ create_info->table_was_deleted);
DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */
#ifdef WITH_WSREP
@@ -4268,36 +4222,6 @@ void select_create::store_values(List<Item> &values)
}
-void select_create::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("select_create::send_error");
-
- DBUG_PRINT("info",
- ("Current statement %s row-based",
- thd->is_current_stmt_binlog_format_row() ? "is" : "is NOT"));
- DBUG_PRINT("info",
- ("Current table (at 0x%lu) %s a temporary (or non-existant) table",
- (ulong) table,
- table && !table->s->tmp_table ? "is NOT" : "is"));
- /*
- This will execute any rollbacks that are necessary before writing
- the transcation cache.
-
- We disable the binary log since nothing should be written to the
- binary log. This disabling is important, since we potentially do
- a "roll back" of non-transactional tables by removing the table,
- and the actual rollback might generate events that should not be
- written to the binary log.
-
- */
- tmp_disable_binlog(thd);
- select_insert::send_error(errcode, err);
- reenable_binlog(thd);
-
- DBUG_VOID_RETURN;
-}
-
-
bool select_create::send_eof()
{
if (select_insert::send_eof())
@@ -4390,13 +4314,12 @@ void select_create::abort_result_set()
of the table succeeded or not, since we need to reset the binary
log state.
- However if there was an orignal table that was deleted, as part of
+ However if there was an original table that was deleted, as part of
create or replace table, then we must log the statement.
*/
save_option_bits= thd->variables.option_bits;
- if (!(thd->log_current_statement))
- thd->variables.option_bits&= ~OPTION_BIN_LOG;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
select_insert::abort_result_set();
thd->transaction.stmt.modified_non_trans_table= FALSE;
thd->variables.option_bits= save_option_bits;
@@ -4404,6 +4327,12 @@ void select_create::abort_result_set()
/* possible error of writing binary log is ignored deliberately */
(void) thd->binlog_flush_pending_rows_event(TRUE, TRUE);
+ if (create_info->table_was_deleted)
+ {
+ /* Unlock locked table that was dropped by CREATE */
+ thd->locked_tables_list.unlock_locked_table(thd,
+ create_info->mdl_ticket);
+ }
if (m_plock)
{
mysql_unlock_tables(thd, *m_plock);
@@ -4413,12 +4342,21 @@ void select_create::abort_result_set()
if (table)
{
+ bool tmp_table= table->s->tmp_table;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;
drop_open_table(thd, table, create_table->db, create_table->table_name);
table=0; // Safety
+ if (thd->log_current_statement && mysql_bin_log.is_open())
+ {
+ /* Remove logging of drop, create + insert rows */
+ binlog_reset_cache(thd);
+ /* Original table was deleted. We have to log it */
+ log_drop_table(thd, create_table->db, create_table->db_length,
+ create_table->table_name, create_table->table_name_length,
+ tmp_table);
+ }
}
DBUG_VOID_RETURN;
}
-
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
index 8564f4d103e..cbfc1ea9dcd 100644
--- a/sql/sql_insert.h
+++ b/sql/sql_insert.h
@@ -38,7 +38,6 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
bool is_multi_insert);
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
-void prepare_triggers_for_insert_stmt(TABLE *table);
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
void kill_delayed_threads(void);
diff --git a/sql/sql_list.h b/sql/sql_list.h
index aef2f8d5f25..7538f69766d 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -330,11 +330,12 @@ public:
friend class error_list;
friend class error_list_iterator;
+#ifndef DBUG_OFF
/*
Debugging help: return N-th element in the list, or NULL if the list has
less than N elements.
*/
- inline void *nth_element(int n)
+ void *elem(int n)
{
list_node *node= first;
void *data= NULL;
@@ -350,6 +351,8 @@ public:
}
return data;
}
+#endif
+
#ifdef LIST_EXTRA_DEBUG
/*
Check list invariants and print results into trace. Invariants are:
@@ -528,7 +531,9 @@ public:
}
empty();
}
- inline T *nth_element(int n) { return (T*)base_list::nth_element(n); }
+#ifndef DBUG_OFF
+ T *elem(int n) { return (T*)base_list::elem(n); }
+#endif
};
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index d112b3e689a..bdf26ec0292 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -28,7 +28,6 @@
#include <my_dir.h>
#include "sql_view.h" // check_key_in_view
#include "sql_insert.h" // check_that_all_fields_are_given_values,
- // prepare_triggers_for_insert_stmt,
// write_record
#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
#include "log_event.h" // Delete_file_log_event,
@@ -298,7 +297,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
}
- prepare_triggers_for_insert_stmt(table);
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
uint tot_length=0;
bool use_blobs= 0, use_vars= 0;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 04cdcd10c56..08e8b535f76 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1620,7 +1620,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
lex_start(thd);
/* Must be before we init the table list. */
if (lower_case_table_names)
+ {
table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ db.length= my_casedn_str(files_charset_info, db.str);
+ }
table_list.init_one_table(db.str, db.length, table_name.str,
table_name.length, table_name.str, TL_READ);
/*
@@ -3145,14 +3148,13 @@ case SQLCOM_PREPARE:
/* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
-#ifdef HAVE_READLINK
- /* Fix names if symlinked tables */
+ /* Fix names if symlinked or relocated tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
create_table->table_name))
goto end_with_restore_list;
-#endif
+
/*
If no engine type was given, work out the default now
rather than at parse-time.
@@ -3185,6 +3187,7 @@ case SQLCOM_PREPARE:
CREATE TABLE OR EXISTS failures by dropping the table and
retrying the create.
*/
+ create_info.org_options= create_info.options;
if (thd->slave_thread &&
slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
!(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
@@ -4202,9 +4205,7 @@ end_with_restore_list:
prepared statement- safe.
*/
HA_CREATE_INFO create_info(lex->create_info);
- char *alias;
- if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
- check_db_name(&lex->name))
+ if (check_db_name(&lex->name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
@@ -4228,8 +4229,7 @@ end_with_restore_list:
if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
- res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
- lex->name.str), &create_info, 0);
+ res= mysql_create_db(thd, lex->name.str, &create_info, 0);
break;
}
case SQLCOM_DROP_DB:
@@ -4325,14 +4325,20 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_DB:
{
+ char db_name_buff[NAME_LEN+1];
+ LEX_STRING db_name;
DBUG_EXECUTE_IF("4x_server_emul",
my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
- if (check_db_name(&lex->name))
+
+ db_name.str= db_name_buff;
+ db_name.length= lex->name.length;
+ strmov(db_name.str, lex->name.str);
+ if (check_db_name(&db_name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
break;
}
- res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
+ res= mysqld_show_create_db(thd, &db_name, &lex->name, &lex->create_info);
break;
}
case SQLCOM_CREATE_EVENT:
@@ -5039,6 +5045,10 @@ create_sp_error:
open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
+ if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str,
+ lex->spname->m_name.str, TRUE, FALSE))
+ goto error;
+
/*
By this moment all needed SPs should be in cache so no need to look
into DB.
@@ -5088,11 +5098,6 @@ create_sp_error:
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
}
- if (check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, TRUE, FALSE))
- {
- goto error;
- }
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -7312,8 +7317,14 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
- if (lower_case_table_names && table->table.length)
- table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (lower_case_table_names)
+ {
+ if (table->table.length)
+ table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (ptr->db_length && ptr->db != any_db)
+ ptr->db_length= my_casedn_str(files_charset_info, ptr->db);
+ }
+
ptr->table_name=table->table.str;
ptr->table_name_length=table->table.length;
ptr->lock_type= lock_type;
@@ -8244,6 +8255,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
DBUG_RETURN(TRUE);
+ table->grant.orig_want_privilege= 0;
table->table_in_first_from_clause= 1;
}
/*
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index a9846df3b02..1ce952b9030 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -67,7 +67,6 @@
// table_to_filename
// mysql_*_alter_copy_data
#include "opt_range.h" // store_key_image_to_rec
-#include "sql_analyse.h" // append_escaped
#include "sql_alter.h" // Alter_table_ctx
#include <algorithm>
@@ -1935,10 +1934,9 @@ static int add_uint(File fptr, ulonglong number)
*/
static int add_quoted_string(File fptr, const char *quotestr)
{
- String orgstr(quotestr, system_charset_info);
String escapedstr;
int err= add_string(fptr, "'");
- err+= append_escaped(&escapedstr, &orgstr);
+ err+= escapedstr.append_for_single_quote(quotestr);
err+= add_string(fptr, escapedstr.c_ptr_safe());
return err + add_string(fptr, "'");
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 9e99b7292d0..eaed0660e46 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -115,6 +115,39 @@ fake_event_write(NET *net, String *packet, const char **errmsg)
/*
+ Helper structure, used to pass miscellaneous info from mysql_binlog_send()
+ into the helper functions that it calls.
+*/
+struct binlog_send_info {
+ rpl_binlog_state until_binlog_state;
+ slave_connection_state gtid_state;
+ THD *thd;
+ NET *net;
+ String *packet;
+ char *log_file_name;
+ slave_connection_state *until_gtid_state;
+ Format_description_log_event *fdev;
+ int mariadb_slave_capability;
+ enum_gtid_skip_type gtid_skip_group;
+ enum_gtid_until_state gtid_until_group;
+ ushort flags;
+ uint8 current_checksum_alg;
+ bool slave_gtid_strict_mode;
+ bool send_fake_gtid_list;
+ bool slave_gtid_ignore_duplicates;
+ bool using_gtid_state;
+
+ binlog_send_info(THD *thd_arg, String *packet_arg, ushort flags_arg, char *lfn)
+ : thd(thd_arg), net(&thd_arg->net), packet(packet_arg),
+ log_file_name(lfn), until_gtid_state(NULL), fdev(NULL),
+ gtid_skip_group(GTID_SKIP_NOT), gtid_until_group(GTID_UNTIL_NOT_DONE),
+ flags(flags_arg), current_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
+ slave_gtid_strict_mode(false), send_fake_gtid_list(false),
+ slave_gtid_ignore_duplicates(false)
+ { }
+};
+
+/*
fake_rotate_event() builds a fake (=which does not exist physically in any
binlog) Rotate event, which contains the name of the binlog we are going to
send to the slave (because the slave may not know it if it just asked for
@@ -132,16 +165,16 @@ fake_event_write(NET *net, String *packet, const char **errmsg)
part.
*/
-static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
- ulonglong position, const char** errmsg,
- uint8 checksum_alg_arg)
+static int fake_rotate_event(binlog_send_info *info, ulonglong position,
+ const char** errmsg, uint8 checksum_alg_arg)
{
DBUG_ENTER("fake_rotate_event");
char buf[ROTATE_HEADER_LEN+100];
my_bool do_checksum;
int err;
- char* p = log_file_name+dirname_length(log_file_name);
+ char* p = info->log_file_name+dirname_length(info->log_file_name);
uint ident_len = (uint) strlen(p);
+ String *packet= info->packet;
ha_checksum crc;
if ((err= fake_event_header(packet, ROTATE_EVENT,
@@ -160,22 +193,23 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
}
if ((err= fake_event_footer(packet, do_checksum, crc, errmsg)) ||
- (err= fake_event_write(net, packet, errmsg)))
+ (err= fake_event_write(info->net, packet, errmsg)))
DBUG_RETURN(err);
DBUG_RETURN(0);
}
-static int fake_gtid_list_event(NET* net, String* packet,
+static int fake_gtid_list_event(binlog_send_info *info,
Gtid_list_log_event *glev, const char** errmsg,
- uint8 checksum_alg_arg, uint32 current_pos)
+ uint32 current_pos)
{
my_bool do_checksum;
int err;
ha_checksum crc;
char buf[128];
String str(buf, sizeof(buf), system_charset_info);
+ String* packet= info->packet;
str.length(0);
if (glev->to_packet(&str))
@@ -185,7 +219,7 @@ static int fake_gtid_list_event(NET* net, String* packet,
}
if ((err= fake_event_header(packet, GTID_LIST_EVENT,
str.length(), &do_checksum, &crc,
- errmsg, checksum_alg_arg, current_pos)))
+ errmsg, info->current_checksum_alg, current_pos)))
return err;
packet->append(str);
@@ -195,7 +229,7 @@ static int fake_gtid_list_event(NET* net, String* packet,
}
if ((err= fake_event_footer(packet, do_checksum, crc, errmsg)) ||
- (err= fake_event_write(net, packet, errmsg)))
+ (err= fake_event_write(info->net, packet, errmsg)))
return err;
return 0;
@@ -627,6 +661,19 @@ get_slave_gtid_strict_mode(THD *thd)
}
+static bool
+get_slave_gtid_ignore_duplicates(THD *thd)
+{
+ bool null_value;
+
+ const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_ignore_duplicates") };
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry && entry->val_int(&null_value) && !null_value;
+}
+
+
/*
Get the value of the @slave_until_gtid user variable into the supplied
String (this is the GTID position specified for START SLAVE UNTIL
@@ -914,16 +961,16 @@ give_error_start_pos_missing_in_binlog(int *err, const char **errormsg,
*/
static int
-check_slave_start_position(THD *thd, slave_connection_state *st,
- const char **errormsg, rpl_gtid *error_gtid,
- slave_connection_state *until_gtid_state)
+check_slave_start_position(binlog_send_info *info, const char **errormsg,
+ rpl_gtid *error_gtid)
{
uint32 i;
int err;
slave_connection_state::entry **delete_list= NULL;
uint32 delete_idx= 0;
+ slave_connection_state *st= &info->gtid_state;
- if (rpl_load_gtid_slave_state(thd))
+ if (rpl_load_gtid_slave_state(info->thd))
{
*errormsg= "Failed to load replication slave GTID state";
err= ER_CANNOT_LOAD_SLAVE_GTID_STATE;
@@ -963,6 +1010,7 @@ check_slave_start_position(THD *thd, slave_connection_state *st,
if (!start_at_own_slave_pos)
{
rpl_gtid domain_gtid;
+ slave_connection_state *until_gtid_state= info->until_gtid_state;
rpl_gtid *until_gtid;
if (!mysql_bin_log.lookup_domain_in_binlog_state(slave_gtid->domain_id,
@@ -981,6 +1029,17 @@ check_slave_start_position(THD *thd, slave_connection_state *st,
continue;
}
+ if (info->slave_gtid_ignore_duplicates &&
+ domain_gtid.seq_no < slave_gtid->seq_no)
+ {
+ /*
+ When --gtid-ignore-duplicates, it is ok for the slave to request
+ something that we do not have (yet) - they might already have gotten
+ it through another path in a multi-path replication hierarchy.
+ */
+ continue;
+ }
+
if (until_gtid_state &&
( !(until_gtid= until_gtid_state->find(slave_gtid->domain_id)) ||
(mysql_bin_log.find_in_binlog_state(until_gtid->domain_id,
@@ -1462,13 +1521,11 @@ gtid_state_from_binlog_pos(const char *in_name, uint32 pos, String *out_str)
static bool
-is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
- enum_gtid_until_state gtid_until_group,
- Log_event_type event_type, uint8 current_checksum_alg,
- ushort flags, const char **errmsg,
- rpl_binlog_state *until_binlog_state, uint32 current_pos)
+is_until_reached(binlog_send_info *info, ulong *ev_offset,
+ Log_event_type event_type, const char **errmsg,
+ uint32 current_pos)
{
- switch (gtid_until_group)
+ switch (info->gtid_until_group)
{
case GTID_UNTIL_NOT_DONE:
return false;
@@ -1479,9 +1536,10 @@ is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
case GTID_UNTIL_STOP_AFTER_TRANSACTION:
if (event_type != XID_EVENT &&
(event_type != QUERY_EVENT ||
- !Query_log_event::peek_is_commit_rollback(packet->ptr()+*ev_offset,
- packet->length()-*ev_offset,
- current_checksum_alg)))
+ !Query_log_event::peek_is_commit_rollback
+ (info->packet->ptr()+*ev_offset,
+ info->packet->length()-*ev_offset,
+ info->current_checksum_alg)))
return false;
break;
}
@@ -1493,12 +1551,11 @@ is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
Send a last fake Gtid_list_log_event with a flag set to mark that we
stop due to UNTIL condition.
*/
- if (reset_transmit_packet(thd, flags, ev_offset, errmsg))
+ if (reset_transmit_packet(info->thd, info->flags, ev_offset, errmsg))
return true;
- Gtid_list_log_event glev(until_binlog_state,
+ Gtid_list_log_event glev(&info->until_binlog_state,
Gtid_list_log_event::FLAG_UNTIL_REACHED);
- if (fake_gtid_list_event(net, packet, &glev, errmsg, current_checksum_alg,
- current_pos))
+ if (fake_gtid_list_event(info, &glev, errmsg, current_pos))
return true;
*errmsg= NULL;
return true;
@@ -1512,23 +1569,19 @@ is_until_reached(THD *thd, NET *net, String *packet, ulong *ev_offset,
Returns NULL on success, error message string on error.
*/
static const char *
-send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
- Log_event_type event_type, char *log_file_name,
- IO_CACHE *log, int mariadb_slave_capability,
- ulong ev_offset, uint8 current_checksum_alg,
- bool using_gtid_state, slave_connection_state *gtid_state,
- enum_gtid_skip_type *gtid_skip_group,
- slave_connection_state *until_gtid_state,
- enum_gtid_until_state *gtid_until_group,
- rpl_binlog_state *until_binlog_state,
- bool slave_gtid_strict_mode, rpl_gtid *error_gtid,
- bool *send_fake_gtid_list,
- Format_description_log_event *fdev)
+send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
+ IO_CACHE *log, ulong ev_offset, rpl_gtid *error_gtid)
{
my_off_t pos;
+ String* const packet= info->packet;
size_t len= packet->length();
+ int mariadb_slave_capability= info->mariadb_slave_capability;
+ uint8 current_checksum_alg= info->current_checksum_alg;
+ slave_connection_state *gtid_state= &info->gtid_state;
+ slave_connection_state *until_gtid_state= info->until_gtid_state;
- if (event_type == GTID_LIST_EVENT && using_gtid_state && until_gtid_state)
+ if (event_type == GTID_LIST_EVENT &&
+ info->using_gtid_state && until_gtid_state)
{
rpl_gtid *gtid_list;
uint32 list_len;
@@ -1537,12 +1590,12 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
if (ev_offset > len ||
Gtid_list_log_event::peek(packet->ptr()+ev_offset, len - ev_offset,
current_checksum_alg,
- &gtid_list, &list_len, fdev))
+ &gtid_list, &list_len, info->fdev))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return "Failed to read Gtid_list_log_event: corrupt binlog";
}
- err= until_binlog_state->load(gtid_list, list_len);
+ err= info->until_binlog_state.load(gtid_list, list_len);
my_free(gtid_list);
if (err)
{
@@ -1552,7 +1605,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
}
/* Skip GTID event groups until we reach slave position within a domain_id. */
- if (event_type == GTID_EVENT && using_gtid_state)
+ if (event_type == GTID_EVENT && info->using_gtid_state)
{
uchar flags2;
slave_connection_state::entry *gtid_entry;
@@ -1566,7 +1619,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
Gtid_log_event::peek(packet->ptr()+ev_offset, len - ev_offset,
current_checksum_alg,
&event_gtid.domain_id, &event_gtid.server_id,
- &event_gtid.seq_no, &flags2, fdev))
+ &event_gtid.seq_no, &flags2, info->fdev))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return "Failed to read Gtid_log_event: corrupt binlog";
@@ -1575,7 +1628,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
DBUG_EXECUTE_IF("gtid_force_reconnect_at_10_1_100",
{
rpl_gtid *dbug_gtid;
- if ((dbug_gtid= until_binlog_state->find_nolock(10,1)) &&
+ if ((dbug_gtid= info->until_binlog_state.find_nolock(10,1)) &&
dbug_gtid->seq_no == 100)
{
DBUG_SET("-d,gtid_force_reconnect_at_10_1_100");
@@ -1585,7 +1638,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
}
});
- if (until_binlog_state->update_nolock(&event_gtid, false))
+ if (info->until_binlog_state.update_nolock(&event_gtid, false))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return "Failed in internal GTID book-keeping: Out of memory";
@@ -1618,12 +1671,13 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
/* Skip this event group if we have not yet reached slave start pos. */
if (event_gtid.server_id != gtid->server_id ||
event_gtid.seq_no <= gtid->seq_no)
- *gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
+ info->gtid_skip_group= (flags2 & Gtid_log_event::FL_STANDALONE ?
GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
if (event_gtid.server_id == gtid->server_id &&
event_gtid.seq_no >= gtid->seq_no)
{
- if (slave_gtid_strict_mode && event_gtid.seq_no > gtid->seq_no &&
+ if (info->slave_gtid_strict_mode &&
+ event_gtid.seq_no > gtid->seq_no &&
!(gtid_entry->flags & slave_connection_state::START_OWN_SLAVE_POS))
{
/*
@@ -1645,7 +1699,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
so MASTER_POS_WAIT() and MASTER_GTID_WAIT() can work.
The fake event will be sent at the end of this event group.
*/
- *send_fake_gtid_list= true;
+ info->send_fake_gtid_list= true;
/*
Delete this entry if we have reached slave start position (so we
@@ -1666,7 +1720,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
This domain already reached the START SLAVE UNTIL stop condition,
so skip this event group.
*/
- *gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
+ info->gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
}
else if (event_gtid.server_id == gtid->server_id &&
@@ -1681,9 +1735,9 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
uint64 until_seq_no= gtid->seq_no;
until_gtid_state->remove(gtid);
if (until_gtid_state->count() == 0)
- *gtid_until_group= (flags2 & Gtid_log_event::FL_STANDALONE ?
- GTID_UNTIL_STOP_AFTER_STANDALONE :
- GTID_UNTIL_STOP_AFTER_TRANSACTION);
+ info->gtid_until_group= (flags2 & Gtid_log_event::FL_STANDALONE ?
+ GTID_UNTIL_STOP_AFTER_STANDALONE :
+ GTID_UNTIL_STOP_AFTER_TRANSACTION);
if (event_gtid.seq_no > until_seq_no)
{
/*
@@ -1693,7 +1747,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
should be in, we can just stop now. And we also need to skip this
event group (as it is beyond the UNTIL condition).
*/
- *gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
+ info->gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
}
}
@@ -1707,11 +1761,11 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
Note that slave that understands GTID can also tolerate holes, so there is
no need to supply dummy event.
*/
- switch (*gtid_skip_group)
+ switch (info->gtid_skip_group)
{
case GTID_SKIP_STANDALONE:
if (!Log_event::is_part_of_group(event_type))
- *gtid_skip_group= GTID_SKIP_NOT;
+ info->gtid_skip_group= GTID_SKIP_NOT;
return NULL;
case GTID_SKIP_TRANSACTION:
if (event_type == XID_EVENT ||
@@ -1719,14 +1773,15 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
Query_log_event::peek_is_commit_rollback(packet->ptr() + ev_offset,
len - ev_offset,
current_checksum_alg)))
- *gtid_skip_group= GTID_SKIP_NOT;
+ info->gtid_skip_group= GTID_SKIP_NOT;
return NULL;
case GTID_SKIP_NOT:
break;
}
/* Do not send annotate_rows events unless slave requested it. */
- if (event_type == ANNOTATE_ROWS_EVENT && !(flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+ if (event_type == ANNOTATE_ROWS_EVENT &&
+ !(info->flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
{
if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES)
{
@@ -1820,7 +1875,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
Skip events with the @@skip_replication flag set, if slave requested
skipping of such events.
*/
- if (thd->variables.option_bits & OPTION_SKIP_REPLICATION)
+ if (info->thd->variables.option_bits & OPTION_SKIP_REPLICATION)
{
/*
The first byte of the packet is a '\0' to distinguish it from an error
@@ -1831,17 +1886,17 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
return NULL;
}
- THD_STAGE_INFO(thd, stage_sending_binlog_event_to_slave);
+ THD_STAGE_INFO(info->thd, stage_sending_binlog_event_to_slave);
pos= my_b_tell(log);
if (RUN_HOOK(binlog_transmit, before_send_event,
- (thd, flags, packet, log_file_name, pos)))
+ (info->thd, info->flags, packet, info->log_file_name, pos)))
{
my_errno= ER_UNKNOWN_ERROR;
return "run 'before_send_event' hook failed";
}
- if (my_net_write(net, (uchar*) packet->ptr(), len))
+ if (my_net_write(info->net, (uchar*) packet->ptr(), len))
{
my_errno= ER_UNKNOWN_ERROR;
return "Failed on my_net_write()";
@@ -1850,14 +1905,15 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
DBUG_PRINT("info", ("log event code %d", (*packet)[LOG_EVENT_OFFSET+1] ));
if (event_type == LOAD_EVENT)
{
- if (send_file(thd))
+ if (send_file(info->thd))
{
my_errno= ER_UNKNOWN_ERROR;
return "failed in send_file()";
}
}
- if (RUN_HOOK(binlog_transmit, after_send_event, (thd, flags, packet)))
+ if (RUN_HOOK(binlog_transmit, after_send_event,
+ (info->thd, info->flags, packet)))
{
my_errno= ER_UNKNOWN_ERROR;
return "Failed to run hook 'after_send_event'";
@@ -1878,31 +1934,21 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
IO_CACHE log;
File file = -1;
- String* const packet = &thd->packet;
+ String* const packet= &thd->packet;
int error;
const char *errmsg = "Unknown error", *tmp_msg;
char error_text[MAX_SLAVE_ERRMSG]; // to be send to slave via my_message()
- NET* net = &thd->net;
mysql_mutex_t *log_lock;
mysql_cond_t *log_cond;
- int mariadb_slave_capability;
char str_buf[128];
String connect_gtid_state(str_buf, sizeof(str_buf), system_charset_info);
- bool using_gtid_state;
char str_buf2[128];
String slave_until_gtid_str(str_buf2, sizeof(str_buf2), system_charset_info);
- slave_connection_state gtid_state, until_gtid_state_obj;
- slave_connection_state *until_gtid_state= NULL;
+ slave_connection_state until_gtid_state_obj;
rpl_gtid error_gtid;
- enum_gtid_skip_type gtid_skip_group= GTID_SKIP_NOT;
- enum_gtid_until_state gtid_until_group= GTID_UNTIL_NOT_DONE;
- rpl_binlog_state until_binlog_state;
- bool slave_gtid_strict_mode= false;
- bool send_fake_gtid_list= false;
+ binlog_send_info info(thd, packet, flags, log_file_name);
- uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
- Format_description_log_event *fdev= NULL;
#ifndef DBUG_OFF
int left_events = max_binlog_dump_events;
@@ -1928,16 +1974,17 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
heartbeat_ts= &heartbeat_buf;
set_timespec_nsec(*heartbeat_ts, 0);
}
- mariadb_slave_capability= get_mariadb_slave_capability(thd);
+ info.mariadb_slave_capability= get_mariadb_slave_capability(thd);
connect_gtid_state.length(0);
- using_gtid_state= get_slave_connect_state(thd, &connect_gtid_state);
- DBUG_EXECUTE_IF("simulate_non_gtid_aware_master", using_gtid_state= false;);
- if (using_gtid_state)
+ info.using_gtid_state= get_slave_connect_state(thd, &connect_gtid_state);
+ DBUG_EXECUTE_IF("simulate_non_gtid_aware_master", info.using_gtid_state= false;);
+ if (info.using_gtid_state)
{
- slave_gtid_strict_mode= get_slave_gtid_strict_mode(thd);
+ info.slave_gtid_strict_mode= get_slave_gtid_strict_mode(thd);
+ info.slave_gtid_ignore_duplicates= get_slave_gtid_ignore_duplicates(thd);
if(get_slave_until_gtid(thd, &slave_until_gtid_str))
- until_gtid_state= &until_gtid_state_obj;
+ info.until_gtid_state= &until_gtid_state_obj;
}
DBUG_EXECUTE_IF("binlog_force_reconnect_after_22_events",
@@ -1978,7 +2025,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
}
#endif
- if (!(fdev= new Format_description_log_event(3)))
+ if (!(info.fdev= new Format_description_log_event(3)))
{
errmsg= "Out of memory initializing format_description event";
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
@@ -1999,33 +2046,32 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
}
name=search_file_name;
- if (using_gtid_state)
+ if (info.using_gtid_state)
{
- if (gtid_state.load(connect_gtid_state.c_ptr_quick(),
- connect_gtid_state.length()))
+ if (info.gtid_state.load(connect_gtid_state.c_ptr_quick(),
+ connect_gtid_state.length()))
{
errmsg= "Out of memory or malformed slave request when obtaining start "
"position from GTID state";
my_errno= ER_UNKNOWN_ERROR;
goto err;
}
- if (until_gtid_state &&
- until_gtid_state->load(slave_until_gtid_str.c_ptr_quick(),
- slave_until_gtid_str.length()))
+ if (info.until_gtid_state &&
+ info.until_gtid_state->load(slave_until_gtid_str.c_ptr_quick(),
+ slave_until_gtid_str.length()))
{
errmsg= "Out of memory or malformed slave request when obtaining UNTIL "
"position sent from slave";
my_errno= ER_UNKNOWN_ERROR;
goto err;
}
- if ((error= check_slave_start_position(thd, &gtid_state, &errmsg,
- &error_gtid, until_gtid_state)))
+ if ((error= check_slave_start_position(&info, &errmsg, &error_gtid)))
{
my_errno= error;
goto err;
}
- if ((errmsg= gtid_find_binlog_file(&gtid_state, search_file_name,
- until_gtid_state)))
+ if ((errmsg= gtid_find_binlog_file(&info.gtid_state, search_file_name,
+ info.until_gtid_state)))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
goto err;
@@ -2098,7 +2144,7 @@ impossible position";
given that we want minimum modification of 4.0, we send the normal
and fake Rotates.
*/
- if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg,
+ if (fake_rotate_event(&info, pos, &errmsg,
get_binlog_checksum_value_at_connect(thd)))
{
/*
@@ -2150,14 +2196,14 @@ impossible position";
{
Format_description_log_event *tmp;
- current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
- packet->length() - ev_offset);
- DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
+ info.current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ packet->length() - ev_offset);
+ DBUG_ASSERT(info.current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
if (!is_slave_checksum_aware(thd) &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Slave can not handle replication events with the checksum "
@@ -2170,14 +2216,14 @@ impossible position";
if (!(tmp= new Format_description_log_event(packet->ptr()+ev_offset,
packet->length()-ev_offset,
- fdev)))
+ info.fdev)))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Corrupt Format_description event found or out-of-memory";
goto err;
}
- delete fdev;
- fdev= tmp;
+ delete info.fdev;
+ info.fdev= tmp;
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
/*
@@ -2194,12 +2240,12 @@ impossible position";
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
/* fix the checksum due to latest changes in header */
- if (current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
fix_checksum(packet, ev_offset);
/* send it */
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ if (my_net_write(info.net, (uchar*) packet->ptr(), packet->length()))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -2235,13 +2281,13 @@ impossible position";
We will send one event, the format_description, and then stop.
*/
- if (until_gtid_state && until_gtid_state->count() == 0)
- gtid_until_group= GTID_UNTIL_STOP_AFTER_STANDALONE;
+ if (info.until_gtid_state && info.until_gtid_state->count() == 0)
+ info.gtid_until_group= GTID_UNTIL_STOP_AFTER_STANDALONE;
/* seek to the requested position, to start the requested dump */
my_b_seek(&log, pos); // Seek will done on next read
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!info.net->error && info.net->vio != 0 && !thd->killed)
{
Log_event_type event_type= UNKNOWN_EVENT;
killed_state killed;
@@ -2254,14 +2300,14 @@ impossible position";
bool is_active_binlog= false;
while (!(killed= thd->killed) &&
!(error = Log_event::read_log_event(&log, packet, log_lock,
- current_checksum_alg,
+ info.current_checksum_alg,
log_file_name,
&is_active_binlog)))
{
#ifndef DBUG_OFF
if (max_binlog_dump_events && !left_events--)
{
- net_flush(net);
+ net_flush(info.net);
errmsg = "Debugging binlog dump abort";
my_errno= ER_UNKNOWN_ERROR;
goto err;
@@ -2279,7 +2325,7 @@ impossible position";
{
if (event_type == XID_EVENT)
{
- net_flush(net);
+ net_flush(info.net);
const char act[]=
"now "
"wait_for signal.continue";
@@ -2298,14 +2344,14 @@ impossible position";
{
Format_description_log_event *tmp;
- current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ info.current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
packet->length() - ev_offset);
- DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
+ DBUG_ASSERT(info.current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
if (!is_slave_checksum_aware(thd) &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Slave can not handle replication events with the checksum "
@@ -2318,14 +2364,14 @@ impossible position";
if (!(tmp= new Format_description_log_event(packet->ptr()+ev_offset,
packet->length()-ev_offset,
- fdev)))
+ info.fdev)))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Corrupt Format_description event found or out-of-memory";
goto err;
}
- delete fdev;
- fdev= tmp;
+ delete info.fdev;
+ info.fdev= tmp;
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
}
@@ -2343,36 +2389,28 @@ impossible position";
}
#endif
- if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log,
- mariadb_slave_capability, ev_offset,
- current_checksum_alg, using_gtid_state,
- &gtid_state, &gtid_skip_group,
- until_gtid_state, &gtid_until_group,
- &until_binlog_state,
- slave_gtid_strict_mode, &error_gtid,
- &send_fake_gtid_list, fdev)))
+ if ((tmp_msg= send_event_to_slave(&info, event_type, &log,
+ ev_offset, &error_gtid)))
{
errmsg= tmp_msg;
goto err;
}
- if (unlikely(send_fake_gtid_list) && gtid_skip_group == GTID_SKIP_NOT)
+ if (unlikely(info.send_fake_gtid_list) &&
+ info.gtid_skip_group == GTID_SKIP_NOT)
{
- Gtid_list_log_event glev(&until_binlog_state, 0);
+ Gtid_list_log_event glev(&info.until_binlog_state, 0);
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg) ||
- fake_gtid_list_event(net, packet, &glev, &errmsg,
- current_checksum_alg, my_b_tell(&log)))
+ fake_gtid_list_event(&info, &glev, &errmsg, my_b_tell(&log)))
{
my_errno= ER_UNKNOWN_ERROR;
goto err;
}
- send_fake_gtid_list= false;
+ info.send_fake_gtid_list= false;
}
- if (until_gtid_state &&
- is_until_reached(thd, net, packet, &ev_offset, gtid_until_group,
- event_type, current_checksum_alg, flags, &errmsg,
- &until_binlog_state, my_b_tell(&log)))
+ if (info.until_gtid_state &&
+ is_until_reached(&info, &ev_offset, event_type, &errmsg,
+ my_b_tell(&log)))
{
if (errmsg)
{
@@ -2386,7 +2424,7 @@ impossible position";
{
if (event_type == XID_EVENT)
{
- net_flush(net);
+ net_flush(info.net);
}
});
@@ -2423,7 +2461,7 @@ impossible position";
/*
Block until there is more data in the log
*/
- if (net_flush(net))
+ if (net_flush(info.net))
{
errmsg = "failed on net_flush()";
my_errno= ER_UNKNOWN_ERROR;
@@ -2466,7 +2504,7 @@ impossible position";
mysql_mutex_lock(log_lock);
switch (error= Log_event::read_log_event(&log, packet, (mysql_mutex_t*) 0,
- current_checksum_alg)) {
+ info.current_checksum_alg)) {
case 0:
/* we read successfully, so we'll need to send it to the slave */
mysql_mutex_unlock(log_lock);
@@ -2524,7 +2562,8 @@ impossible position";
thd->EXIT_COND(&old_stage);
goto err;
}
- if (send_heartbeat_event(net, packet, p_coord, current_checksum_alg))
+ if (send_heartbeat_event(info.net, packet, p_coord,
+ info.current_checksum_alg))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -2549,36 +2588,28 @@ impossible position";
if (read_packet)
{
- if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log,
- mariadb_slave_capability, ev_offset,
- current_checksum_alg,
- using_gtid_state, &gtid_state,
- &gtid_skip_group, until_gtid_state,
- &gtid_until_group, &until_binlog_state,
- slave_gtid_strict_mode, &error_gtid,
- &send_fake_gtid_list, fdev)))
+ if ((tmp_msg= send_event_to_slave(&info, event_type, &log,
+ ev_offset, &error_gtid)))
{
errmsg= tmp_msg;
goto err;
}
- if (unlikely(send_fake_gtid_list) && gtid_skip_group == GTID_SKIP_NOT)
+ if (unlikely(info.send_fake_gtid_list)
+ && info.gtid_skip_group == GTID_SKIP_NOT)
{
- Gtid_list_log_event glev(&until_binlog_state, 0);
+ Gtid_list_log_event glev(&info.until_binlog_state, 0);
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg) ||
- fake_gtid_list_event(net, packet, &glev, &errmsg,
- current_checksum_alg, my_b_tell(&log)))
+ fake_gtid_list_event(&info, &glev, &errmsg, my_b_tell(&log)))
{
my_errno= ER_UNKNOWN_ERROR;
goto err;
}
- send_fake_gtid_list= false;
+ info.send_fake_gtid_list= false;
}
- if (until_gtid_state &&
- is_until_reached(thd, net, packet, &ev_offset, gtid_until_group,
- event_type, current_checksum_alg, flags, &errmsg,
- &until_binlog_state, my_b_tell(&log)))
+ if (info.until_gtid_state &&
+ is_until_reached(&info, &ev_offset, event_type, &errmsg,
+ my_b_tell(&log)))
{
if (errmsg)
{
@@ -2633,8 +2664,8 @@ impossible position";
read and send is Format_description_log_event.
*/
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
- fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE,
- &errmsg, current_checksum_alg))
+ fake_rotate_event(&info, BIN_LOG_HEADER_SIZE, &errmsg,
+ info.current_checksum_alg))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
goto err;
@@ -2655,7 +2686,7 @@ end:
thd->current_linfo = 0;
mysql_mutex_unlock(&LOCK_thread_count);
thd->variables.max_allowed_packet= old_max_allowed_packet;
- delete fdev;
+ delete info.fdev;
DBUG_VOID_RETURN;
err:
@@ -2731,7 +2762,7 @@ err:
if (file >= 0)
mysql_file_close(file, MYF(MY_WME));
thd->variables.max_allowed_packet= old_max_allowed_packet;
- delete fdev;
+ delete info.fdev;
my_message(my_errno, error_text, MYF(0));
DBUG_VOID_RETURN;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index cfef5eafa27..240fc953f1d 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -3064,7 +3064,8 @@ void JOIN::exec_inner()
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
- if (curr_join->order && curr_join->filesort_found_rows)
+ if (curr_join->order && curr_join->sortorder &&
+ curr_join->select_options & OPTION_FOUND_ROWS)
{
/* Use info provided by filesort. */
DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
@@ -3820,6 +3821,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
{
conds->update_used_tables();
conds= remove_eq_conds(join->thd, conds, &join->cond_value);
+ if (conds && conds->type() == Item::COND_ITEM &&
+ ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
+ join->cond_equal= &((Item_cond_and*) conds)->cond_equal;
join->select_lex->where= conds;
if (join->cond_value == Item::COND_FALSE)
{
@@ -5556,9 +5560,23 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
/* Estimate of the number matching candidates in the joined table */
inline
-ha_rows matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint)
+double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
+ uint use_cond_selectivity)
{
- ha_rows records= s->found_records;
+ ha_rows records;
+ double dbl_records;
+
+ if (use_cond_selectivity > 1)
+ {
+ TABLE *table= s->table;
+ double sel= table->cond_selectivity;
+ double table_records= table->stat_records();
+ dbl_records= table_records * sel;
+ return dbl_records;
+ }
+
+ records = s->found_records;
+
/*
If there is a filtering condition on the table (i.e. ref analyzer found
at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
@@ -5578,7 +5596,8 @@ ha_rows matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint)
if (s->table->quick_condition_rows != s->found_records)
records= s->table->quick_condition_rows;
- return records;
+ dbl_records= records;
+ return dbl_records;
}
@@ -5621,6 +5640,7 @@ best_access_path(JOIN *join,
POSITION *loose_scan_pos)
{
THD *thd= join->thd;
+ uint use_cond_selectivity= thd->variables.optimizer_use_condition_selectivity;
KEYUSE *best_key= 0;
uint best_max_key_part= 0;
my_bool found_constraint= 0;
@@ -6052,7 +6072,8 @@ best_access_path(JOIN *join,
{
double join_sel= 0.1;
/* Estimate the cost of the hash join access to the table */
- ha_rows rnd_records= matching_candidates_in_table(s, found_constraint);
+ double rnd_records= matching_candidates_in_table(s, found_constraint,
+ use_cond_selectivity);
tmp= s->quick ? s->quick->read_time : s->scan_time();
tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
@@ -6064,7 +6085,7 @@ best_access_path(JOIN *join,
best_time= tmp +
(record_count*join_sel) / TIME_FOR_COMPARE * rnd_records;
best= tmp;
- records= rows2double(rnd_records);
+ records= rnd_records;
best_key= hj_start_key;
best_ref_depends_map= 0;
best_uses_jbuf= TRUE;
@@ -6111,7 +6132,8 @@ best_access_path(JOIN *join,
!(s->table->force_index && best_key && !s->quick) && // (4)
!(best_key && s->table->pos_in_table_list->jtbm_subselect)) // (5)
{ // Check full join
- ha_rows rnd_records= matching_candidates_in_table(s, found_constraint);
+ double rnd_records= matching_candidates_in_table(s, found_constraint,
+ use_cond_selectivity);
/*
Range optimizer never proposes a RANGE if it isn't better
@@ -6139,7 +6161,11 @@ best_access_path(JOIN *join,
else
{
/* Estimate cost of reading table. */
- tmp= s->scan_time();
+ if (s->table->force_index && !best_key) // index scan
+ tmp= s->table->file->read_time(s->ref.key, 1, s->records);
+ else // table scan
+ tmp= s->scan_time();
+
if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache
{
/*
@@ -6184,7 +6210,7 @@ best_access_path(JOIN *join,
will ensure that this will be used
*/
best= tmp;
- records= rows2double(rnd_records);
+ records= rnd_records;
best_key= 0;
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
@@ -7289,6 +7315,10 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
keyuse++;
} while (keyuse->table == table && keyuse->key == key);
}
+ else
+ {
+ sel= 1;
+ }
/*
If the field f from the table is equal to a field from one the
@@ -7588,7 +7618,11 @@ best_extension_by_limited_search(JOIN *join,
if (join->sort_by_table &&
join->sort_by_table !=
join->positions[join->const_tables].table->table)
- /* We have to make a temp table */
+ /*
+ We may have to make a temp table, note that this is only a
+ heuristic since we cannot know for sure at this point.
+ Hence it may be wrong.
+ */
current_read_time+= current_record_count;
if (current_read_time < join->best_read)
{
@@ -8313,7 +8347,7 @@ get_best_combination(JOIN *join)
Save records_read in JOIN_TAB so that select_describe()/etc don't have
to access join->best_positions[].
*/
- j->records_read= (ha_rows)join->best_positions[tablenr].records_read;
+ j->records_read= join->best_positions[tablenr].records_read;
j->cond_selectivity= join->best_positions[tablenr].cond_selectivity;
join->map2table[j->table->tablenr]= j;
@@ -14230,7 +14264,10 @@ optimize_cond(JOIN *join, COND *conds,
Remove all and-levels where CONST item != CONST item
*/
DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
- conds= remove_eq_conds(thd, conds, cond_value) ;
+ conds= remove_eq_conds(thd, conds, cond_value);
+ if (conds && conds->type() == Item::COND_ITEM &&
+ ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
+ join->cond_equal= &((Item_cond_and*) conds)->cond_equal;
DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY););
}
DBUG_RETURN(conds);
@@ -16117,11 +16154,25 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->name= (char*) "distinct_key";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->is_statistics_from_stat_tables= FALSE;
- keyinfo->rec_per_key=0;
keyinfo->read_stats= NULL;
keyinfo->collected_stats= NULL;
/*
+ Needed by non-merged semi-joins: SJ-Materialized table must have a valid
+ rec_per_key array, because it participates in join optimization. Since
+ the table has no data, the only statistics we can provide is "unknown",
+ i.e. zero values.
+
+ (For table record count, we calculate and set JOIN_TAB::found_records,
+ see get_delayed_table_estimates()).
+ */
+ size_t rpk_size= keyinfo->user_defined_key_parts * sizeof(keyinfo->rec_per_key[0]);
+ if (!(keyinfo->rec_per_key= (ulong*) alloc_root(&table->mem_root,
+ rpk_size)))
+ goto err;
+ bzero(keyinfo->rec_per_key, rpk_size);
+
+ /*
Create an extra field to hold NULL bits so that unique indexes on
blobs can distinguish NULL from 0. This extra field is not needed
when we do not use UNIQUE indexes for blobs.
@@ -18540,7 +18591,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
records are read. Because of optimization in some cases it can
provide only select_limit_cnt+1 records.
*/
- if (join->order && join->filesort_found_rows &&
+ if (join->order && join->sortorder &&
join->select_options & OPTION_FOUND_ROWS)
{
DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
@@ -18562,7 +18613,6 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
/* Join over all rows in table; Return number of found rows */
TABLE *table=jt->table;
- join->select_options ^= OPTION_FOUND_ROWS;
if (table->sort.record_pointers ||
(table->sort.io_cache && my_b_inited(table->sort.io_cache)))
{
@@ -20395,11 +20445,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
select, filesort_limit, 0,
&examined_rows, &found_rows);
table->sort.found_records= filesort_retval;
- if (found_rows != HA_POS_ERROR)
- {
- tab->records= found_rows; // For SQL_CALC_ROWS
- join->filesort_found_rows= true;
- }
+ tab->records= found_rows; // For SQL_CALC_ROWS
if (quick_created)
{
diff --git a/sql/sql_select.h b/sql/sql_select.h
index ea60e815c07..271199e3d51 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -288,7 +288,7 @@ typedef struct st_join_table {
double read_time;
/* Copy of POSITION::records_read, set by get_best_combination() */
- double records_read;
+ double records_read;
/* The selectivity of the conditions that can be pushed to the table */
double cond_selectivity;
@@ -1119,12 +1119,6 @@ public:
restore_no_rows_in_result() in ::reinit()
*/
bool no_rows_in_result_called;
-
- /**
- This is set if SQL_CALC_ROWS was calculated by filesort()
- and should be taken from the appropriate JOIN_TAB
- */
- bool filesort_found_rows;
/**
Copy of this JOIN to be used with temporary tables.
@@ -1341,7 +1335,6 @@ public:
emb_sjm_nest= NULL;
sjm_lookup_tables= 0;
- filesort_found_rows= false;
exec_saved_explain= false;
/*
The following is needed because JOIN::cleanup(true) may be called for
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index cf96297391c..637ee78e314 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -38,7 +38,6 @@
#include "unireg.h"
#include "sql_base.h" // close_mysql_tables
#include "records.h" // init_read_record, end_read_record
-#include "hash_filo.h"
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 475b7124a3a..dfc816090fd 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1013,9 +1013,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{
/*
- Use open_tables() directly rather than open_normal_and_derived_tables().
- This ensures that close_thread_tables() is not called if open tables fails
- and the error is ignored. This allows us to handle broken views nicely.
+ Use open_tables() directly rather than
+ open_normal_and_derived_tables(). This ensures that
+ close_thread_tables() is not called if open tables fails and the
+ error is ignored. This allows us to handle broken views nicely.
*/
uint counter;
Show_create_error_handler view_error_suppressor(thd, table_list);
@@ -1109,7 +1110,8 @@ exit:
DBUG_RETURN(error);
}
-bool mysqld_show_create_db(THD *thd, char *dbname,
+bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
+ LEX_STRING *orig_dbname,
HA_CREATE_INFO *create_info)
{
char buff[2048];
@@ -1127,32 +1129,32 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
if (test_all_bits(sctx->master_access, DB_ACLS))
db_access=DB_ACLS;
else
- db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) |
+ db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname->str, 0) |
sctx->master_access);
- if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
+ if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname->str))
{
status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- sctx->priv_user, sctx->host_or_ip, dbname);
+ sctx->priv_user, sctx->host_or_ip, dbname->str);
general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
- sctx->priv_user, sctx->host_or_ip, dbname);
+ sctx->priv_user, sctx->host_or_ip, orig_dbname->str);
DBUG_RETURN(TRUE);
}
#endif
- if (is_infoschema_db(dbname))
+ if (is_infoschema_db(dbname->str))
{
- dbname= INFORMATION_SCHEMA_NAME.str;
+ *dbname= INFORMATION_SCHEMA_NAME;
create.default_table_charset= system_charset_info;
}
else
{
- if (check_db_dir_existence(dbname))
+ if (check_db_dir_existence(dbname->str))
{
- my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
+ my_error(ER_BAD_DB_ERROR, MYF(0), dbname->str);
DBUG_RETURN(TRUE);
}
- load_db_opt_by_name(thd, dbname, &create);
+ load_db_opt_by_name(thd, dbname->str, &create);
}
List<Item> field_list;
field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
@@ -1163,12 +1165,12 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
- protocol->store(dbname, strlen(dbname), system_charset_info);
+ protocol->store(orig_dbname->str, orig_dbname->length, system_charset_info);
buffer.length(0);
buffer.append(STRING_WITH_LEN("CREATE DATABASE "));
if (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)
buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ "));
- append_identifier(thd, &buffer, dbname, strlen(dbname));
+ append_identifier(thd, &buffer, dbname->str, dbname->length);
if (create.default_table_charset)
{
@@ -9384,6 +9386,9 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
db= trg_name->m_db;
db.str= thd->strmake(db.str, db.length);
+ if (lower_case_table_names)
+ db.length= my_casedn_str(files_charset_info, db.str);
+
tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
if (db.str == NULL || tbl_name.str == NULL)
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 0416f2fdaba..708a77d74cd 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -86,7 +86,9 @@ bool append_identifier(THD *thd, String *packet, const char *name,
void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
-bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
+bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name,
+ LEX_STRING *orig_db_name,
+ HA_CREATE_INFO *create);
void mysqld_list_processes(THD *thd,const char *user,bool verbose);
int mysqld_show_status(THD *thd);
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 94cbf3b946a..67e7a9c304b 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -346,6 +346,8 @@ protected:
if ((err= stat_file->ha_update_row(record[1], record[0])) &&
err != HA_ERR_RECORD_IS_THE_SAME)
return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
return FALSE;
}
@@ -529,6 +531,8 @@ public:
store_stat_fields();
if ((err= stat_file->ha_write_row(record[0])))
return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
}
return FALSE;
}
@@ -583,6 +587,8 @@ public:
int err;
if ((err= stat_file->ha_delete_row(record[0])))
return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
return FALSE;
}
};
@@ -3508,6 +3514,8 @@ double get_column_range_cardinality(Field *field,
Histogram *hist= &col_stats->histogram;
if (hist->is_available())
{
+ store_key_image_to_rec(field, (uchar *) min_endp->key,
+ min_endp->length);
double pos= field->pos_in_interval(col_stats->min_value,
col_stats->max_value);
res= col_non_nulls *
@@ -3557,3 +3565,120 @@ double get_column_range_cardinality(Field *field,
}
return res;
}
+
+
+
+/*
+ Estimate selectivity of "col=const" using a histogram
+
+ @param pos Position of the "const" between column's min_value and
+ max_value. This is a number in [0..1] range.
+ @param avg_sel Average selectivity of condition "col=const" in this table.
+ It is calcuated as (#non_null_values / #distinct_values).
+
+ @return
+ Expected condition selectivity (a number between 0 and 1)
+
+ @notes
+ [re_zero_length_buckets] If a bucket with zero value-length is in the
+ middle of the histogram, we will not have min==max. Example: suppose,
+ pos_value=0x12, and the histogram is:
+
+ #n #n+1 #n+2
+ ... 0x10 0x12 0x12 0x14 ...
+ |
+ +------------- bucket with zero value-length
+
+ Here, we will get min=#n+1, max=#n+2, and use the multi-bucket formula.
+
+ The problem happens at the histogram ends. if pos_value=0, and the
+ histogram is:
+
+ 0x00 0x10 ...
+
+ then min=0, max=0. This means pos_value is contained within bucket #0,
+ but on the other hand, histogram data says that the bucket has only one
+ value.
+*/
+
+double Histogram::point_selectivity(double pos, double avg_sel)
+{
+ double sel;
+ /* Find the bucket that contains the value 'pos'. */
+ uint min= find_bucket(pos, TRUE);
+ uint pos_value= (uint) (pos * prec_factor());
+
+ /* Find how many buckets this value occupies */
+ uint max= min;
+ while (max + 1 < get_width() && get_value(max + 1) == pos_value)
+ max++;
+
+ /*
+ A special case: we're looking at a single bucket, and that bucket has
+ zero value-length. Use the multi-bucket formula (attempt to use
+ single-bucket formula will cause divison by zero).
+
+ For more details see [re_zero_length_buckets] above.
+ */
+ if (max == min && get_value(max) == ((max==0)? 0 : get_value(max-1)))
+ max++;
+
+ if (max > min)
+ {
+ /*
+ The value occupies multiple buckets. Use start_bucket ... end_bucket as
+ selectivity.
+ */
+ double bucket_sel= 1.0/(get_width() + 1);
+ sel= bucket_sel * (max - min + 1);
+ }
+ else
+ {
+ /*
+ The value 'pos' fits within one single histogram bucket.
+
+ Histogram buckets have the same numbers of rows, but they cover
+ different ranges of values.
+
+ We assume that values are uniformly distributed across the [0..1] value
+ range.
+ */
+
+ /*
+ If all buckets covered value ranges of the same size, the width of
+ value range would be:
+ */
+ double avg_bucket_width= 1.0 / (get_width() + 1);
+
+ /*
+ Let's see what is the width of value range that our bucket is covering.
+ (min==max currently. they are kept in the formula just in case we
+ will want to extend it to handle multi-bucket case)
+ */
+ double inv_prec_factor= (double) 1.0 / prec_factor();
+ double current_bucket_width=
+ (max + 1 == get_width() ? 1.0 : (get_value(max) * inv_prec_factor)) -
+ (min == 0 ? 0.0 : (get_value(min-1) * inv_prec_factor));
+
+ DBUG_ASSERT(current_bucket_width); /* We shouldn't get a one zero-width bucket */
+
+ /*
+ So:
+ - each bucket has the same #rows
+ - values are unformly distributed across the [min_value,max_value] domain.
+
+ If a bucket has value range that's N times bigger then average, than
+ each value will have to have N times fewer rows than average.
+ */
+ sel= avg_sel * avg_bucket_width / current_bucket_width;
+
+ /*
+ (Q: if we just follow this proportion we may end up in a situation
+ where number of different values we expect to find in this bucket
+ exceeds the number of rows that this histogram has in a bucket. Are
+ we ok with this or we would want to have certain caps?)
+ */
+ }
+ return sel;
+}
+
diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h
index 68aacd69d98..331e3559203 100644
--- a/sql/sql_statistics.h
+++ b/sql/sql_statistics.h
@@ -113,7 +113,7 @@ class Histogram
private:
Histogram_type type;
- uint8 size;
+ uint8 size; /* Size of values array, in bytes */
uchar *values;
uint prec_factor()
@@ -142,6 +142,7 @@ public:
private:
uint get_value(uint i)
{
+ DBUG_ASSERT(i < get_width());
switch (type) {
case SINGLE_PREC_HB:
return (uint) (((uint8 *) values)[i]);
@@ -151,6 +152,7 @@ private:
return 0;
}
+ /* Find the bucket which value 'pos' falls into. */
uint find_bucket(double pos, bool first)
{
uint val= (uint) (pos * prec_factor());
@@ -169,6 +171,10 @@ private:
else
break;
}
+
+ if (val > get_value(i) && i < (get_width() - 1))
+ i++;
+
if (val == get_value(i))
{
if (first)
@@ -234,24 +240,11 @@ public:
sel= bucket_sel * (max - min + 1);
return sel;
}
-
- double point_selectivity(double pos, double avg_sel)
- {
- double sel;
- double bucket_sel= 1.0/(get_width() + 1);
- uint min= find_bucket(pos, TRUE);
- uint max= min;
- while (max + 1 < get_width() && get_value(max + 1) == get_value(max))
- max++;
- double inv_prec_factor= (double) 1.0 / prec_factor();
- double width= (max + 1 == get_width() ?
- 1.0 : get_value(max) * inv_prec_factor) -
- (min == 0 ?
- 0.0 : get_value(min-1) * inv_prec_factor);
- sel= avg_sel * (bucket_sel * (max + 1 - min)) / width;
- return sel;
- }
-
+
+ /*
+ Estimate selectivity of "col=const" using a histogram
+ */
+ double point_selectivity(double pos, double avg_sel);
};
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index a2d4349f747..bcc811e426d 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1014,7 +1014,7 @@ outp:
/*
Append characters to a single-quoted string '...', escaping special
- characters as necessary.
+ characters with backslashes as necessary.
Does not add the enclosing quotes, this is left up to caller.
*/
#define APPEND(X) if (append(X)) return 1; else break
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 7687490c8aa..bc9e7f11bd6 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -359,7 +359,7 @@ public:
}
bool append(const String &s);
bool append(const char *s);
- bool append(LEX_STRING *ls)
+ bool append(const LEX_STRING *ls)
{
return append(ls->str, ls->length);
}
@@ -496,7 +496,16 @@ public:
return FALSE;
}
void print(String *print);
+
bool append_for_single_quote(const char *st, uint len);
+ bool append_for_single_quote(const String *s)
+ {
+ return append_for_single_quote(s->ptr(), s->length());
+ }
+ bool append_for_single_quote(const char *st)
+ {
+ return append_for_single_quote(st, strlen(st));
+ }
/* Swap two string objects. Efficient way to exchange data without memcpy. */
void swap(String &s);
@@ -559,4 +568,7 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs,
return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
}
+int append_query_string(CHARSET_INFO *csinfo, String *to,
+ const char *str, size_t len, bool no_backslash);
+
#endif /* SQL_STRING_INCLUDED */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index ff2e7939fb8..2ffe201e796 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2288,6 +2288,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
for (table= tables; table; table= table->next_local)
{
bool is_trans= 0;
+ bool table_creation_was_logged= 1;
char *db=table->db;
size_t db_length= table->db_length;
handlerton *table_type= 0;
@@ -2316,6 +2317,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
error= 1;
else
{
+ table_creation_was_logged= table->table->s->table_creation_was_logged;
if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1)
{
DBUG_ASSERT(thd->in_sub_stmt);
@@ -2336,7 +2338,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
. "DROP" was executed but a temporary table was affected (.i.e
!error).
*/
- if (!dont_log_query)
+#ifndef DONT_LOG_DROP_OF_TEMPORARY_TABLES
+ table_creation_was_logged= 1;
+#endif
+ if (!dont_log_query && table_creation_was_logged)
{
/*
If there is an error, we don't know the type of the engine
@@ -2560,6 +2565,18 @@ err:
error= 1;
}
+ /*
+ We are always logging drop of temporary tables.
+ The reason is to handle the following case:
+ - Use statement based replication
+ - CREATE TEMPORARY TABLE foo (logged)
+ - set row based replication
+ - DROP TEMPORAY TABLE foo (needs to be logged)
+ This should be fixed so that we remember if creation of the
+ temporary table was logged and only log it if the creation was
+ logged.
+ */
+
if (non_trans_tmp_table_deleted ||
trans_tmp_table_deleted || non_tmp_table_deleted)
{
@@ -2648,6 +2665,46 @@ end:
DBUG_RETURN(error);
}
+/**
+ Log the drop of a table.
+
+ @param thd Thread handler
+ @param db_name Database name
+ @param table_name Table name
+ @param temporary_table 1 if table was a temporary table
+
+ This code is only used in the case of failed CREATE OR REPLACE TABLE
+ when the original table was dropped but we could not create the new one.
+*/
+
+bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
+ const char *table_name, size_t table_name_length,
+ bool temporary_table)
+{
+ char buff[NAME_LEN*2 + 80];
+ String query(buff, sizeof(buff), system_charset_info);
+ bool error;
+ DBUG_ENTER("log_drop_table");
+
+ if (!mysql_bin_log.is_open())
+ DBUG_RETURN(0);
+
+ query.length(0);
+ query.append(STRING_WITH_LEN("DROP "));
+ if (temporary_table)
+ query.append(STRING_WITH_LEN("TEMPORARY "));
+ query.append(STRING_WITH_LEN("TABLE IF EXISTS "));
+ append_identifier(thd, &query, db_name, db_name_length);
+ query.append(".");
+ append_identifier(thd, &query, table_name, table_name_length);
+ query.append(STRING_WITH_LEN("/* Generated to handle "
+ "failed CREATE OR REPLACE */"));
+ error= thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query.ptr(), query.length(),
+ FALSE, FALSE, temporary_table, 0);
+ DBUG_RETURN(error);
+}
+
/**
Quickly remove a table.
@@ -4035,30 +4092,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(TRUE);
}
- uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
- key->key_create_info.comment.str,
- key->key_create_info.comment.str +
- key->key_create_info.comment.length,
- INDEX_COMMENT_MAXLEN);
-
- if (tmp_len < key->key_create_info.comment.length)
- {
- if (thd->is_strict_mode())
- {
- my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0),
- key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
- DBUG_RETURN(-1);
- }
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT),
- key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
- /* do not push duplicate warnings */
- if (!thd->get_stmt_da()->has_sql_condition(warn_buff, strlen(warn_buff)))
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_TOO_LONG_INDEX_COMMENT, warn_buff);
-
- key->key_create_info.comment.length= tmp_len;
- }
+ if (validate_comment_length(thd, &key->key_create_info.comment,
+ INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT,
+ key_info->name))
+ DBUG_RETURN(TRUE);
key_info->comment.length= key->key_create_info.comment.length;
if (key_info->comment.length > 0)
@@ -4141,6 +4178,43 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(FALSE);
}
+/**
+ check comment length of table, column, index and partition
+
+ If comment lenght is more than the standard length
+ truncate it and store the comment lenght upto the standard
+ comment length size
+
+ @param thd Thread handle
+ @param[in,out] comment Comment
+ @param max_len Maximum allowed comment length
+ @param err_code Error message
+ @param name Name of commented object
+
+ @return Operation status
+ @retval true Error found
+ @retval false On Success
+*/
+bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+ uint err_code, const char *name)
+{
+ DBUG_ENTER("validate_comment_length");
+ uint tmp_len= my_charpos(system_charset_info, comment->str,
+ comment->str + comment->length, max_len);
+ if (tmp_len < comment->length)
+ {
+ if (thd->is_strict_mode())
+ {
+ my_error(err_code, MYF(0), name, static_cast<ulong>(max_len));
+ DBUG_RETURN(true);
+ }
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, err_code,
+ ER(err_code), name, static_cast<ulong>(max_len));
+ comment->length= tmp_len;
+ }
+ DBUG_RETURN(false);
+}
+
/*
Set table default charset, if not set
@@ -4349,6 +4423,45 @@ handler *mysql_create_frm_image(THD *thd,
char *part_syntax_buf;
uint syntax_len;
handlerton *engine_type;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ partition_element *part_elem;
+
+ while ((part_elem= part_it++))
+ {
+ if (part_elem->part_comment)
+ {
+ LEX_STRING comment= {
+ part_elem->part_comment, strlen(part_elem->part_comment)
+ };
+ if (validate_comment_length(thd, &comment,
+ TABLE_PARTITION_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_PARTITION_COMMENT,
+ part_elem->partition_name))
+ DBUG_RETURN(NULL);
+ part_elem->part_comment[comment.length]= '\0';
+ }
+ if (part_elem->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *subpart_elem;
+ while ((subpart_elem= sub_it++))
+ {
+ if (subpart_elem->part_comment)
+ {
+ LEX_STRING comment= {
+ subpart_elem->part_comment, strlen(subpart_elem->part_comment)
+ };
+ if (validate_comment_length(thd, &comment,
+ TABLE_PARTITION_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_PARTITION_COMMENT,
+ subpart_elem->partition_name))
+ DBUG_RETURN(NULL);
+ subpart_elem->part_comment[comment.length]= '\0';
+ }
+ }
+ }
+ }
+
if (create_info->tmp_table())
{
my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
@@ -4505,6 +4618,9 @@ err:
Create a table
@param thd Thread object
+ @param orig_db Database for error messages
+ @param orig_table_name Table name for error messages
+ (it's different from table_name for ALTER TABLE)
@param db Database
@param table_name Table name
@param path Path to table (i.e. to its .FRM file without
@@ -4533,6 +4649,7 @@ err:
static
int create_table_impl(THD *thd,
+ const char *orig_db, const char *orig_table_name,
const char *db, const char *table_name,
const char *path,
HA_CREATE_INFO *create_info,
@@ -4552,7 +4669,7 @@ int create_table_impl(THD *thd,
DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d",
db, table_name, internal_tmp_table));
- if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+ if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
{
if (create_info->data_file_name)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -4578,6 +4695,7 @@ int create_table_impl(THD *thd,
TABLE *tmp_table;
if ((tmp_table= find_temporary_table(thd, db, table_name)))
{
+ bool table_creation_was_logged= tmp_table->s->table_creation_was_logged;
if (create_info->options & HA_LEX_CREATE_REPLACE)
{
bool is_trans;
@@ -4595,6 +4713,19 @@ int create_table_impl(THD *thd,
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
goto err;
}
+ /*
+ We have to log this query, even if it failed later to ensure the
+ drop is done.
+ */
+#ifndef DONT_LOG_DROP_OF_TEMPORARY_TABLES
+ table_creation_was_logged= 1;
+#endif
+ if (table_creation_was_logged)
+ {
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
+ thd->log_current_statement= 1;
+ create_info->table_was_deleted= 1;
+ }
}
}
else
@@ -4627,13 +4758,14 @@ int create_table_impl(THD *thd,
*/
thd->variables.option_bits|= OPTION_KEEP_LOG;
thd->log_current_statement= 1;
+ create_info->table_was_deleted= 1;
+ DBUG_EXECUTE_IF("send_kill_after_delete", thd->killed= KILL_QUERY; );
/*
- The test of query_tables is to ensure we have any tables in the
- select part
+ Restart statement transactions for the case of CREATE ... SELECT.
*/
- if (thd->lex->query_tables &&
- restart_trans_for_tables(thd, thd->lex->query_tables->next_global))
+ if (thd->lex->select_lex.item_list.elements &&
+ restart_trans_for_tables(thd, thd->lex->query_tables))
goto err;
}
else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
@@ -4697,8 +4829,9 @@ int create_table_impl(THD *thd,
}
else
{
- file= mysql_create_frm_image(thd, db, table_name, create_info, alter_info,
- create_table_mode, key_info, key_count, frm);
+ file= mysql_create_frm_image(thd, orig_db, orig_table_name, create_info,
+ alter_info, create_table_mode, key_info,
+ key_count, frm);
if (!file)
goto err;
if (rea_create_table(thd, frm, path, db, table_name, create_info,
@@ -4706,6 +4839,7 @@ int create_table_impl(THD *thd,
goto err;
}
+ create_info->table= 0;
if (!frm_only && create_info->tmp_table())
{
/*
@@ -4726,6 +4860,7 @@ int create_table_impl(THD *thd,
*is_trans= table->file->has_transactions();
thd->thread_specific_used= TRUE;
+ create_info->table= table; // Store pointer to table
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
else if (thd->work_part_info && frm_only)
@@ -4768,6 +4903,7 @@ int create_table_impl(THD *thd,
err:
THD_STAGE_INFO(thd, stage_after_create);
delete file;
+ DBUG_PRINT("exit", ("return: %d", error));
DBUG_RETURN(error);
warn:
@@ -4811,9 +4947,9 @@ int mysql_create_table_no_lock(THD *thd,
}
}
- res= create_table_impl(thd, db, table_name, path, create_info,
- alter_info, create_table_mode, is_trans,
- &not_used_1, &not_used_2, &frm);
+ res= create_table_impl(thd, db, table_name, db, table_name, path,
+ create_info, alter_info, create_table_mode,
+ is_trans, &not_used_1, &not_used_2, &frm);
my_free(const_cast<uchar*>(frm.str));
return res;
}
@@ -4838,19 +4974,23 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
bool result= 0;
int create_table_mode;
TABLE_LIST *pos_in_locked_tables= 0;
+ MDL_ticket *mdl_ticket= 0;
DBUG_ENTER("mysql_create_table");
DBUG_ASSERT(create_table == thd->lex->query_tables);
/* Open or obtain an exclusive metadata lock on table being created */
- if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))
+ if (open_and_lock_tables(thd, create_table, FALSE, 0))
{
/* is_error() may be 0 if table existed and we generated a warning */
DBUG_RETURN(thd->is_error());
}
/* The following is needed only in case of lock tables */
- if ((create_info->table= thd->lex->query_tables->table))
+ if ((create_info->table= create_table->table))
+ {
pos_in_locked_tables= create_info->table->pos_in_locked_tables;
+ mdl_ticket= create_table->table->mdl_ticket;
+ }
/* Got lock. */
DEBUG_SYNC(thd, "locked_table_name");
@@ -4893,11 +5033,30 @@ err:
/* In RBR we don't need to log CREATE TEMPORARY TABLE */
if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table())
DBUG_RETURN(result);
+
/* Write log if no error or if we already deleted a table */
if (!result || thd->log_current_statement)
+ {
+ if (result && create_info->table_was_deleted)
+ {
+ /*
+ Possible locked table was dropped. We should remove meta data locks
+ associated with it and do UNLOCK_TABLES if no more locked tables.
+ */
+ thd->locked_tables_list.unlock_locked_table(thd, mdl_ticket);
+ }
+ else if (!result && create_info->tmp_table() && create_info->table)
+ {
+ /*
+ Remember that tmp table creation was logged so that we know if
+ we should log a delete of it.
+ */
+ create_info->table->s->table_creation_was_logged= 1;
+ }
if (write_bin_log(thd, result ? FALSE : TRUE, thd->query(),
thd->query_length(), is_trans))
result= 1;
+ }
DBUG_RETURN(result);
}
@@ -4975,7 +5134,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
char *from_base= from, *to_base= to;
- char tmp_name[SAFE_NAME_LEN+1];
+ char tmp_name[SAFE_NAME_LEN+1], tmp_db_name[SAFE_NAME_LEN+1];
handler *file;
int error=0;
ulonglong save_bits= thd->variables.option_bits;
@@ -5012,13 +5171,19 @@ mysql_rename_table(handlerton *base, const char *old_db,
{
strmov(tmp_name, old_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
- flags & FN_FROM_IS_TMP);
+ strmov(tmp_db_name, old_db);
+ my_casedn_str(files_charset_info, tmp_db_name);
+
+ build_table_filename(lc_from, sizeof(lc_from) - 1, tmp_db_name, tmp_name,
+ "", flags & FN_FROM_IS_TMP);
from_base= lc_from;
strmov(tmp_name, new_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
+ strmov(tmp_db_name, new_db);
+ my_casedn_str(files_charset_info, tmp_db_name);
+
+ build_table_filename(lc_to, sizeof(lc_to) - 1, tmp_db_name, tmp_name, "",
flags & FN_TO_IS_TMP);
to_base= lc_to;
}
@@ -5302,7 +5467,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
char buf[2048];
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
- Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN |
+ MYSQL_OPEN_IGNORE_KILLED);
bool new_table= FALSE; // Whether newly created table is open.
if (create_res != 0)
@@ -5311,6 +5477,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
Table or view with same name already existed and we where using
IF EXISTS. Continue without logging anything.
*/
+ do_logging= 0;
goto err;
}
if (!table->table)
@@ -5348,14 +5515,16 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
int result __attribute__((unused))=
store_create_info(thd, table, &query,
create_info, FALSE /* show_database */,
- MY_TEST(create_info->options &
- HA_LEX_CREATE_REPLACE));
+ MY_TEST(create_info->org_options &
+ HA_LEX_CREATE_REPLACE) ||
+ create_info->table_was_deleted);
DBUG_ASSERT(result == 0); // store_create_info() always return 0
do_logging= FALSE;
if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
{
res= 1;
+ do_logging= 0;
goto err;
}
@@ -5379,13 +5548,38 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
*/
}
else
+ {
+ DBUG_PRINT("info",
+ ("res: %d tmp_table: %d create_info->table: %p",
+ res, create_info->tmp_table(), local_create_info.table));
+ if (!res && create_info->tmp_table() && local_create_info.table)
+ {
+ /*
+ Remember that tmp table creation was logged so that we know if
+ we should log a delete of it.
+ */
+ local_create_info.table->s->table_creation_was_logged= 1;
+ }
do_logging= TRUE;
+ }
err:
- if (do_logging &&
- write_bin_log(thd, res ? FALSE : TRUE, thd->query(),
- thd->query_length(), is_trans))
- res= 1;
+ if (do_logging)
+ {
+ if (res && create_info->table_was_deleted)
+ {
+ /*
+ Table was not deleted. Original table was deleted.
+ We have to log it.
+ */
+ log_drop_table(thd, table->db, table->db_length,
+ table->table_name, table->table_name_length,
+ create_info->tmp_table());
+ }
+ else if (write_bin_log(thd, res ? FALSE : TRUE, thd->query(),
+ thd->query_length(), is_trans))
+ res= 1;
+ }
DBUG_RETURN(res);
#ifdef WITH_WSREP
@@ -8463,7 +8657,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
tmp_disable_binlog(thd);
create_info->options|=HA_CREATE_TMP_ALTER;
- error= create_table_impl(thd, alter_ctx.new_db, alter_ctx.tmp_name,
+ error= create_table_impl(thd,
+ alter_ctx.db, alter_ctx.table_name,
+ alter_ctx.new_db, alter_ctx.tmp_name,
alter_ctx.get_tmp_path(),
create_info, alter_info,
C_ALTER_TABLE_FRM_ONLY, NULL,
@@ -8759,6 +8955,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
mysql_lock_remove(thd, thd->lock, table);
}
}
+ new_table->s->table_creation_was_logged=
+ table->s->table_creation_was_logged;
/* Remove link to old table and rename the new one */
close_temporary_table(thd, table, true, true);
/* Should pass the 'new_name' as we store table name in the cache */
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 7255ce68743..cd1c4293c39 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -240,6 +240,9 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view,
bool log_query, bool dont_free_locks);
+bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
+ const char *table_name, size_t table_name_length,
+ bool temporary_table);
bool quick_rm_table(THD *thd, handlerton *base, const char *db,
const char *table_name, uint flags);
void close_cached_table(THD *thd, TABLE *table);
@@ -264,10 +267,6 @@ bool sync_ddl_log();
void release_ddl_log();
void execute_ddl_log_recovery();
bool execute_ddl_log_entry(THD *thd, uint first_entry);
-bool validate_comment_length(THD *thd, const char *comment_str,
- size_t *comment_len, uint max_len,
- uint err_code, const char *comment_name);
-bool check_duplicate_warning(THD *thd, char *msg, ulong length);
template<typename T> class List;
void promote_first_timestamp_column(List<Create_field> *column_definitions);
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 8588d6564ca..ae16a281277 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -88,9 +88,9 @@ static void print_cached_tables(void)
puts("DB Table Version Thread Open Lock");
tdc_it.init();
- mysql_mutex_lock(&LOCK_open);
while ((share= tdc_it.next()))
{
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
while ((entry= it++))
{
@@ -102,8 +102,8 @@ static void print_cached_tables(void)
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
- mysql_mutex_unlock(&LOCK_open);
tdc_it.deinit();
printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
fflush(stdout);
@@ -383,6 +383,15 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm)
}
/* purecov: end */
+/*
+ Debugging help: force List<...>::elem function not be removed as unused.
+*/
+Item* (List<Item>:: *dbug_list_item_elem_ptr)(int)= &List<Item>::elem;
+Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(int)=
+ &List<Item_equal>::elem;
+TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(int) =
+ &List<TABLE_LIST>::elem;
+
#endif
typedef struct st_debug_lock
@@ -606,7 +615,6 @@ Next alarm time: %lu\n",
(ulong)alarm_info.next_alarm_time);
#endif
display_table_locks();
- fflush(stdout);
#ifdef HAVE_MALLINFO
struct mallinfo info= mallinfo();
printf("\nMemory status:\n\
@@ -638,4 +646,5 @@ Estimated memory (with thread stack): %ld\n",
Events::dump_internal_status();
#endif
puts("");
+ fflush(stdout);
}
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 2c4998fdf5e..9b68aba5b30 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -921,6 +921,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
my_bool neg= 0;
enum enum_mysql_timestamp_type time_type= ltime->time_type;
+ if ((ulong) interval.day > MAX_DAY_NUMBER)
+ goto invalid_date;
+
if (time_type != MYSQL_TIMESTAMP_TIME)
ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index fd2d20c1813..c7981ae36d5 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -192,7 +192,7 @@ int Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
bool is_tmp_table)
{
int error= 0;
- uint flags;
+ uint flags= 0;
DBUG_ENTER("Sql_cmd_truncate_table::handler_truncate");
/*
@@ -259,6 +259,7 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
bool error= TRUE;
TABLE_SHARE *share= table->s;
handlerton *table_type= table->s->db_type();
+ TABLE *new_table;
DBUG_ENTER("recreate_temporary_table");
table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
@@ -269,11 +270,13 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
dd_recreate_table(thd, share->db.str, share->table_name.str,
share->normalized_path.str);
- if (open_table_uncached(thd, table_type, share->path.str, share->db.str,
- share->table_name.str, true, true))
+ if ((new_table= open_table_uncached(thd, table_type, share->path.str,
+ share->db.str,
+ share->table_name.str, true, true)))
{
error= FALSE;
thd->thread_specific_used= TRUE;
+ new_table->s->table_creation_was_logged= share->table_creation_was_logged;
}
else
rm_temporary_table(table_type, share->path.str);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 528e76d8d32..e9e8033c4fd 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -502,7 +502,7 @@ int mysql_update(THD *thd,
if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set))
{
- if (order && (need_sort || used_key_is_modified))
+ if (order && need_sort)
query_plan.using_filesort= true;
else
query_plan.using_io_buffer= true;
@@ -703,16 +703,8 @@ int mysql_update(THD *thd,
transactional_table= table->file->has_transactions();
thd->abort_on_warning= !ignore && thd->is_strict_mode();
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
+ if (table->prepare_triggers_for_update_stmt_or_event())
{
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
will_batch= FALSE;
}
else
@@ -1229,6 +1221,87 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
return false;
}
+/**
+ Check if there is enough privilege on specific table used by the
+ main select list of multi-update directly or indirectly (through
+ a view).
+
+ @param[in] thd Thread context.
+ @param[in] table Table list element for the table.
+ @param[in] tables_for_update Bitmap with tables being updated.
+ @param[in/out] updated_arg Set to true if table in question is
+ updated, also set to true if it is
+ a view and one of its underlying
+ tables is updated. Should be
+ initialized to false by the caller
+ before a sequence of calls to this
+ function.
+
+ @note To determine which tables/views are updated we have to go from
+ leaves to root since tables_for_update contains map of leaf
+ tables being updated and doesn't include non-leaf tables
+ (fields are already resolved to leaf tables).
+
+ @retval false - Success, all necessary privileges on all tables are
+ present or might be present on column-level.
+ @retval true - Failure, some necessary privilege on some table is
+ missing.
+*/
+
+static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table,
+ table_map tables_for_update,
+ bool *updated_arg)
+{
+ if (table->view)
+ {
+ bool updated= false;
+ /*
+ If it is a mergeable view then we need to check privileges on its
+ underlying tables being merged (including views). We also need to
+ check if any of them is updated in order to find if this view is
+ updated.
+ If it is a non-mergeable view then it can't be updated.
+ */
+ DBUG_ASSERT(table->merge_underlying_list ||
+ (!table->updatable &&
+ !(table->table->map & tables_for_update)));
+
+ for (TABLE_LIST *tbl= table->merge_underlying_list; tbl;
+ tbl= tbl->next_local)
+ {
+ if (multi_update_check_table_access(thd, tbl, tables_for_update,
+ &updated))
+ {
+ tbl->hide_view_error(thd);
+ return true;
+ }
+ }
+ if (check_table_access(thd, updated ? UPDATE_ACL: SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ }
+ else
+ {
+ /* Must be a base or derived table. */
+ const bool updated= table->table->map & tables_for_update;
+ if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ if (!table->derived)
+ {
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ table->table->grant.want_privilege= (SELECT_ACL &
+ ~table->table->grant.privilege);
+ }
+ }
+ return false;
+}
+
/*
make update specific preparation and checks after opening tables
@@ -1363,19 +1436,17 @@ int mysql_multi_update_prepare(THD *thd)
tl->table->reginfo.lock_type= tl->lock_type;
}
}
+
+ /*
+ Check access privileges for tables being updated or read.
+ Note that unlike in the above loop we need to iterate here not only
+ through all leaf tables but also through all view hierarchy.
+ */
for (tl= table_list; tl; tl= tl->next_local)
{
- /* Check access privileges for table */
- if (!tl->is_derived())
- {
- uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL;
- if (check_access(thd, want_privilege, tl->db,
- &tl->grant.privilege,
- &tl->grant.m_internal,
- 0, 0) ||
- check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
- DBUG_RETURN(TRUE);
- }
+ bool not_used= false;
+ if (multi_update_check_table_access(thd, tl, tables_for_update, &not_used))
+ DBUG_RETURN(TRUE);
}
/* check single table update for view compound from several tables */
@@ -1614,17 +1685,7 @@ int multi_update::prepare(List<Item> &not_used_values,
table->no_keyread=1;
table->covering_keys.clear_all();
table->pos_in_table_list= tl;
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
+ table->prepare_triggers_for_update_stmt_or_event();
}
}
@@ -2099,13 +2160,6 @@ int multi_update::send_data(List<Item> &not_used_values)
}
-void multi_update::send_error(uint errcode,const char *err)
-{
- /* First send error what ever it is ... */
- my_error(errcode, MYF(0), err);
-}
-
-
void multi_update::abort_result_set()
{
/* the error was handled or nothing deleted and no side effects return */
@@ -2436,7 +2490,7 @@ bool multi_update::send_eof()
thd->transaction.stmt.modified_non_trans_table);
if (local_error != 0)
- error_handled= TRUE; // to force early leave from ::send_error()
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
if (local_error > 0) // if the above log write did not fail ...
{
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 4ca165a6d19..a18193c6eb6 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1652,7 +1652,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
{
if (non_existant_views.length())
non_existant_views.append(',');
- non_existant_views.append(String(view->table_name,system_charset_info));
+ non_existant_views.append(name);
}
else
{
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 345fc9f1dfa..3d283a5f32b 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1778,10 +1778,11 @@ fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
static Sys_var_ulong Sys_slave_parallel_threads(
"slave_parallel_threads",
- "Alpha feature, to only be used by developers doing testing! "
"If non-zero, number of threads to spawn to apply in parallel events "
"on the slave that were group-committed on the master or were logged "
- "with GTID in different replication domains.",
+ "with GTID in different replication domains. Note that these threads "
+ "are in addition to the IO and SQL threads, which are always created "
+ "by a replication slave",
GLOBAL_VAR(opt_slave_parallel_threads), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0,16383), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
NOT_IN_BINLOG, ON_CHECK(check_slave_parallel_threads),
@@ -1839,6 +1840,50 @@ static Sys_var_ulong Sys_slave_parallel_max_queued(
"--slave-parallel-threads > 0.",
GLOBAL_VAR(opt_slave_parallel_max_queued), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0,2147483647), DEFAULT(131072), BLOCK_SIZE(1));
+
+
+static bool
+check_gtid_ignore_duplicates(sys_var *self, THD *thd, set_var *var)
+{
+ bool running;
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (running)
+ return true;
+
+ return false;
+}
+
+static bool
+fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool running;
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
+ return running ? true : false;
+}
+
+
+static Sys_var_mybool Sys_gtid_ignore_duplicates(
+ "gtid_ignore_duplicates",
+ "When set, different master connections in multi-source replication are "
+ "allowed to receive and process event groups with the same GTID (when "
+ "using GTID mode). Only one will be applied, any others will be "
+ "ignored. Within a given replication domain, just the sequence number "
+ "will be used to decide whether a given GTID has been already applied; "
+ "this means it is the responsibility of the user to ensure that GTID "
+ "sequence numbers are strictly increasing.",
+ GLOBAL_VAR(opt_gtid_ignore_duplicates), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_gtid_ignore_duplicates),
+ ON_UPDATE(fix_gtid_ignore_duplicates));
#endif
@@ -2692,11 +2737,11 @@ static Sys_var_mybool Sys_slave_compressed_protocol(
static const char *slave_exec_mode_names[]= {"STRICT", "IDEMPOTENT", 0};
static Sys_var_enum Slave_exec_mode(
"slave_exec_mode",
- "Modes for how replication events should be executed. Legal values "
+ "How replication events should be executed. Legal values "
"are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, "
"replication will not stop for operations that are idempotent. "
"For example, in row based replication attempts to delete rows that "
- "doesn't exist will be ignored."
+ "doesn't exist will be ignored. "
"In STRICT mode, replication will stop on any unexpected difference "
"between the master and the slave",
GLOBAL_VAR(slave_exec_mode_options), CMD_LINE(REQUIRED_ARG),
@@ -2704,14 +2749,31 @@ static Sys_var_enum Slave_exec_mode(
static Sys_var_enum Slave_ddl_exec_mode(
"slave_ddl_exec_mode",
- "Modes for how replication events should be executed. Legal values "
+ "How replication events should be executed. Legal values "
"are STRICT and IDEMPOTENT (default). In IDEMPOTENT mode, "
"replication will not stop for DDL operations that are idempotent. "
- "This means that CREATE TABLE is treated CREATE TABLE OR REPLACE and "
- "DROP TABLE is threated as DROP TABLE IF EXISTS. ",
+ "This means that CREATE TABLE is treated as CREATE TABLE OR REPLACE and "
+ "DROP TABLE is treated as DROP TABLE IF EXISTS.",
GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG),
slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
+#ifdef RBR_TRIGGERS
+static const char *slave_run_triggers_for_rbr_names[]=
+ {"NO", "YES", "LOGGING", 0};
+static Sys_var_enum Slave_run_triggers_for_rbr(
+ "slave_run_triggers_for_rbr",
+ "Modes for how triggers in row-base replication on slave side will be "
+ "executed. Legal values are NO (default), YES and LOGGING. NO means "
+ "that trigger for RBR will not be running on slave. YES and LOGGING "
+ "means that triggers will be running on slave, if there was not "
+ "triggers running on the master for the statement. LOGGING also means "
+ "results of that the executed triggers work will be written to "
+ "the binlog.",
+ GLOBAL_VAR(slave_run_triggers_for_rbr), CMD_LINE(REQUIRED_ARG),
+ slave_run_triggers_for_rbr_names,
+ DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));
+#endif //RBR_TRIGGERS
+
static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
static Sys_var_set Slave_type_conversions(
"slave_type_conversions",
@@ -4851,7 +4913,7 @@ const char *use_stat_tables_modes[] =
static Sys_var_enum Sys_optimizer_use_stat_tables(
"use_stat_tables",
"Specifies how to use system statistics tables. Possible values are "
- "NEVER, COMPLEMENTARY, PREVERABLY",
+ "NEVER, COMPLEMENTARY, PREFERABLY",
SESSION_VAR(use_stat_tables), CMD_LINE(REQUIRED_ARG),
use_stat_tables_modes, DEFAULT(0));
diff --git a/sql/table.cc b/sql/table.cc
index 0548796a424..f7fe67c8437 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3027,6 +3027,7 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
char buff[FN_REFLEN];
const myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log
DBUG_ENTER("open_table_error");
+ DBUG_PRINT("info", ("error: %d db_errno: %d", error, db_errno));
switch (error) {
case OPEN_FRM_OPEN_ERROR:
@@ -3443,10 +3444,11 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf,
SYNPOSIS
check_db_name()
- org_name Name of database and length
+ org_name Name of database
NOTES
- If lower_case_table_names is set then database is converted to lower case
+ If lower_case_table_names is set to 1 then database name is converted
+ to lower case
RETURN
0 ok
@@ -3468,9 +3470,12 @@ bool check_db_name(LEX_STRING *org_name)
if (!name_length || name_length > NAME_LEN)
return 1;
- if (lower_case_table_names && name != any_db)
- my_casedn_str(files_charset_info, name);
-
+ if (lower_case_table_names == 1 && name != any_db)
+ {
+ org_name->length= name_length= my_casedn_str(files_charset_info, name);
+ if (check_for_path_chars)
+ org_name->length+= MYSQL50_TABLE_NAME_PREFIX_LENGTH;
+ }
if (db_name_is_in_ignore_db_dirs_list(name))
return 1;
@@ -3808,13 +3813,14 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
/*
To protect all_tables list from being concurrently modified
- while we are iterating through it we acquire LOCK_open.
+ while we are iterating through it we increment tdc.all_tables_refs.
This does not introduce deadlocks in the deadlock detector
- because we won't try to acquire LOCK_open while
+ because we won't try to acquire tdc.LOCK_table_share while
holding a write-lock on MDL_lock::m_rwlock.
*/
- if (gvisitor->m_lock_open_count++ == 0)
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&tdc.LOCK_table_share);
+ tdc.all_tables_refs++;
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
All_share_tables_list::Iterator tables_it(tdc.all_tables);
@@ -3857,8 +3863,10 @@ end_leave_node:
gvisitor->leave_node(src_ctx);
end:
- if (gvisitor->m_lock_open_count-- == 1)
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_lock(&tdc.LOCK_table_share);
+ if (!--tdc.all_tables_refs)
+ mysql_cond_broadcast(&tdc.COND_release);
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
return result;
}
@@ -4003,6 +4011,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
+#ifdef HAVE_REPLICATION
+ /* used in RBR Triggers */
+ master_had_triggers= 0;
+#endif
/* Catch wrong handling of the auto_increment_field_not_null. */
DBUG_ASSERT(!auto_increment_field_not_null);
@@ -6666,6 +6678,81 @@ int TABLE::update_default_fields()
/*
+ Prepare triggers for INSERT-like statement.
+
+ SYNOPSIS
+ prepare_triggers_for_insert_stmt_or_event()
+
+ NOTE
+ Prepare triggers for INSERT-like statement by marking fields
+ used by triggers and inform handlers that batching of UPDATE/DELETE
+ cannot be done if there are BEFORE UPDATE/DELETE triggers.
+*/
+
+void TABLE::prepare_triggers_for_insert_stmt_or_event()
+{
+ if (triggers)
+ {
+ if (triggers->has_triggers(TRG_EVENT_DELETE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER DELETE triggers that might access to
+ subject table and therefore might need delete to be done
+ immediately. So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+ }
+ if (triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+ }
+ }
+}
+
+
+bool TABLE::prepare_triggers_for_delete_stmt_or_event()
+{
+ if (triggers &&
+ triggers->has_triggers(TRG_EVENT_DELETE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER DELETE triggers that might access to subject table
+ and therefore might need delete to be done immediately. So we turn-off
+ the batching.
+ */
+ (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bool TABLE::prepare_triggers_for_update_stmt_or_event()
+{
+ if (triggers &&
+ triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
@brief Reset const_table flag
@detail
diff --git a/sql/table.h b/sql/table.h
index 6f16bc55c9e..cd1b934dcdd 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -616,12 +616,14 @@ struct TABLE_SHARE
struct
{
/**
- Protects ref_count and m_flush_tickets.
+ Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
+ all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
TABLE_SHARE *next, **prev; /* Link to unused shares */
uint ref_count; /* How many TABLE objects uses this */
+ uint all_tables_refs; /* Number of refs to all_tables */
/**
List of tickets representing threads waiting for the share to be flushed.
*/
@@ -977,6 +979,9 @@ struct TABLE_SHARE
*/
bool write_frm_image(const uchar *frm_image, size_t frm_length);
+ bool write_frm_image(void)
+ { return frm_image ? write_frm_image(frm_image->str, frm_image->length) : 0; }
+
/*
returns an frm image for this table.
the memory is allocated and must be freed later
@@ -1235,6 +1240,10 @@ public:
bool get_fields_in_item_tree; /* Signal to fix_field */
bool m_needs_reopen;
bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/
+#ifdef HAVE_REPLICATION
+ /* used in RBR Triggers */
+ bool master_had_triggers;
+#endif
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
@@ -1363,6 +1372,10 @@ public:
ulong actual_key_flags(KEY *keyinfo);
int update_default_fields();
inline ha_rows stat_records() { return used_stat_records; }
+
+ void prepare_triggers_for_insert_stmt_or_event();
+ bool prepare_triggers_for_delete_stmt_or_event();
+ bool prepare_triggers_for_update_stmt_or_event();
};
@@ -2160,9 +2173,11 @@ struct TABLE_LIST
}
inline void set_merged_derived()
{
+ DBUG_ENTER("set_merged_derived");
derived_type= ((derived_type & DTYPE_MASK) |
DTYPE_TABLE | DTYPE_MERGE);
set_check_merged();
+ DBUG_VOID_RETURN;
}
inline bool is_materialized_derived()
{
@@ -2170,9 +2185,11 @@ struct TABLE_LIST
}
void set_materialized_derived()
{
+ DBUG_ENTER("set_materialized_derived");
derived_type= ((derived_type & DTYPE_MASK) |
DTYPE_TABLE | DTYPE_MATERIALIZE);
set_check_materialized();
+ DBUG_VOID_RETURN;
}
inline bool is_multitable()
{
@@ -2529,6 +2546,9 @@ int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field);
bool get_field(MEM_ROOT *mem, Field *field, class String *res);
+bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+ uint err_code, const char *name);
+
int closefrm(TABLE *table, bool free_share);
void free_blobs(TABLE *table);
void free_field_buffers_larger_than(TABLE *table, uint32 size);
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index fbafd8e0c9f..8b768240b4f 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -70,13 +70,6 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */
/**
- Protects TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables.
-*/
-
-mysql_mutex_t LOCK_open;
-
-
-/**
Protects unused shares list.
TABLE_SHARE::tdc.prev
@@ -90,11 +83,9 @@ static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */
my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */
#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_open, key_LOCK_unused_shares,
- key_TABLE_SHARE_LOCK_table_share;
+static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share;
static PSI_mutex_info all_tc_mutexes[]=
{
- { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL },
{ &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL },
{ &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
};
@@ -171,6 +162,33 @@ static void tc_remove_table(TABLE *table)
/**
+ Wait for MDL deadlock detector to complete traversing tdc.all_tables.
+
+ Must be called before updating TABLE_SHARE::tdc.all_tables.
+*/
+
+static void tc_wait_for_mdl_deadlock_detector(TABLE_SHARE *share)
+{
+ while (share->tdc.all_tables_refs)
+ mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
+}
+
+
+/**
+ Get last element of tdc.free_tables.
+*/
+
+static TABLE *tc_free_tables_back(TABLE_SHARE *share)
+{
+ TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
+ TABLE *entry, *last= 0;
+ while ((entry= it++))
+ last= entry;
+ return last;
+}
+
+
+/**
Free all unused TABLE objects.
While locked:
@@ -193,9 +211,11 @@ void tc_purge(bool mark_flushed)
TABLE_SHARE::TABLE_list purge_tables;
tdc_it.init();
- mysql_mutex_lock(&LOCK_open);
while ((share= tdc_it.next()))
{
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(share);
+
if (mark_flushed)
share->tdc.flushed= true;
while ((table= share->tdc.free_tables.pop_front()))
@@ -203,9 +223,9 @@ void tc_purge(bool mark_flushed)
tc_remove_table(table);
purge_tables.push_front(table);
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
tdc_it.deinit();
- mysql_mutex_unlock(&LOCK_open);
while ((table= purge_tables.pop_front()))
intern_close_table(table);
@@ -232,9 +252,10 @@ void tc_add_table(THD *thd, TABLE *table)
{
bool need_purge;
DBUG_ASSERT(table->in_use == thd);
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(table->s);
table->s->tdc.all_tables.push_front(table);
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
/* If we have too many TABLE instances around, try to get rid of them */
my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
@@ -243,31 +264,48 @@ void tc_add_table(THD *thd, TABLE *table)
if (need_purge)
{
- TABLE *purge_table= 0;
+ TABLE_SHARE *purge_share= 0;
TABLE_SHARE *share;
+ TABLE *entry;
+ ulonglong purge_time;
TDC_iterator tdc_it;
tdc_it.init();
- mysql_mutex_lock(&LOCK_open);
while ((share= tdc_it.next()))
{
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
- TABLE *entry;
- while ((entry= it++))
- if (!purge_table || entry->tc_time < purge_table->tc_time)
- purge_table= entry;
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if ((entry= tc_free_tables_back(share)) &&
+ (!purge_share || entry->tc_time < purge_time))
+ {
+ purge_share= share;
+ purge_time= entry->tc_time;
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
- tdc_it.deinit();
- if (purge_table)
+ if (purge_share)
{
- purge_table->s->tdc.free_tables.remove(purge_table);
- tc_remove_table(purge_table);
- mysql_mutex_unlock(&LOCK_open);
- intern_close_table(purge_table);
+ mysql_mutex_lock(&purge_share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(purge_share);
+ tdc_it.deinit();
+ /*
+ It may happen that oldest table was acquired meanwhile. In this case
+ just go ahead, number of objects in table cache will normalize
+ eventually.
+ */
+ if ((entry= tc_free_tables_back(purge_share)) &&
+ entry->tc_time == purge_time)
+ {
+ entry->s->tdc.free_tables.remove(entry);
+ tc_remove_table(entry);
+ mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
+ intern_close_table(entry);
+ }
+ else
+ mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
}
else
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
}
}
@@ -292,10 +330,8 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
{
TABLE *table;
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
table= share->tdc.free_tables.pop_front();
- mysql_mutex_unlock(&LOCK_open);
-
if (table)
{
DBUG_ASSERT(!table->in_use);
@@ -305,6 +341,7 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
/* The children must be detached from the table. */
DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
return table;
}
@@ -328,7 +365,7 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
@note Another thread may mark share for purge any moment (even
after version check). It means to-be-purged object may go to
unused lists. This other thread is expected to call tc_purge(),
- which is synchronized with us on LOCK_open.
+ which is synchronized with us on TABLE_SHARE::tdc.LOCK_table_share.
@return
@retval true object purged
@@ -342,17 +379,17 @@ bool tc_release_table(TABLE *table)
if (table->needs_reopen() || tc_records() > tc_size)
{
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
goto purge;
}
table->tc_time= my_interval_timer();
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
if (table->s->tdc.flushed)
goto purge;
/*
- in_use doesn't really need protection of LOCK_open, but must be reset after
+ in_use doesn't really need mutex protection, but must be reset after
checking tdc.flushed and before this table appears in free_tables.
Resetting in_use is needed only for print_cached_tables() and
list_open_tables().
@@ -360,12 +397,13 @@ bool tc_release_table(TABLE *table)
table->in_use= 0;
/* Add table to the list of unused TABLE objects for this share. */
table->s->tdc.free_tables.push_front(table);
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
return false;
purge:
+ tc_wait_for_mdl_deadlock_detector(table->s);
tc_remove_table(table);
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
table->in_use= 0;
intern_close_table(table);
return true;
@@ -446,13 +484,6 @@ int tdc_init(void)
init_tc_psi_keys();
#endif
tdc_inited= true;
- mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
- mysql_mutex_record_order(&LOCK_active_mi, &LOCK_open);
- /*
- We must have LOCK_open before LOCK_global_system_variables because
- LOCK_open is held while sql_plugin.cc::intern_sys_var_ptr() is called.
- */
- mysql_mutex_record_order(&LOCK_open, &LOCK_global_system_variables);
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
MY_MUTEX_INIT_FAST);
mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc);
@@ -505,7 +536,6 @@ void tdc_deinit(void)
my_atomic_rwlock_destroy(&LOCK_tdc_atomics);
mysql_rwlock_destroy(&LOCK_tdc);
mysql_mutex_destroy(&LOCK_unused_shares);
- mysql_mutex_destroy(&LOCK_open);
}
DBUG_VOID_RETURN;
}
@@ -575,6 +605,7 @@ void tdc_init_share(TABLE_SHARE *share)
tdc_assign_new_table_id(share);
share->tdc.version= tdc_refresh_version();
share->tdc.flushed= false;
+ share->tdc.all_tables_refs= 0;
DBUG_VOID_RETURN;
}
@@ -590,6 +621,7 @@ void tdc_deinit_share(TABLE_SHARE *share)
DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
DBUG_ASSERT(share->tdc.all_tables.is_empty());
DBUG_ASSERT(share->tdc.free_tables.is_empty());
+ DBUG_ASSERT(share->tdc.all_tables_refs == 0);
mysql_cond_destroy(&share->tdc.COND_release);
mysql_mutex_destroy(&share->tdc.LOCK_table_share);
DBUG_VOID_RETURN;
@@ -826,7 +858,8 @@ void tdc_release_share(TABLE_SHARE *share)
if (share->tdc.ref_count > 1)
{
share->tdc.ref_count--;
- mysql_cond_broadcast(&share->tdc.COND_release);
+ if (!share->is_view)
+ mysql_cond_broadcast(&share->tdc.COND_release);
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
DBUG_VOID_RETURN;
}
@@ -954,13 +987,14 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
I_P_List <TABLE, TABLE_share> purge_tables;
uint my_refs= 1;
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(share);
/*
- Set share's version to zero in order to ensure that it gets
+ Mark share flushed in order to ensure that it gets
automatically deleted once it is no longer referenced.
Note that code in TABLE_SHARE::wait_for_old_version() assumes that
- incrementing of refresh_version is followed by purge of unused table
+ marking share flushed is followed by purge of unused table
shares.
*/
if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
@@ -985,7 +1019,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
}
}
DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
while ((table= purge_tables.pop_front()))
intern_close_table(table);
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 6da6a667792..ea3822f9f68 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -26,7 +26,6 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
-extern mysql_mutex_t LOCK_open; /* FIXME: make private */
extern int tdc_init(void);
extern void tdc_start_shutdown(void);
diff --git a/sql/unireg.cc b/sql/unireg.cc
index ad2d711be99..aeeba6f4f85 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -40,7 +40,7 @@
#define ALLOCA_THRESHOLD 2048
static uint pack_keys(uchar *,uint, KEY *, ulong);
-static bool pack_header(uchar *, List<Create_field> &, uint, ulong, handler *);
+static bool pack_header(THD *, uchar *, List<Create_field> &, uint, ulong, handler *);
static uint get_interval_id(uint *,List<Create_field> &, Create_field *);
static bool pack_fields(uchar *, List<Create_field> &, ulong);
static size_t packed_fields_length(List<Create_field> &);
@@ -99,7 +99,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
uint keys, KEY *key_info, handler *db_file)
{
LEX_STRING str_db_type;
- uint reclength, key_info_length, tmp_len, i;
+ uint reclength, key_info_length, i;
ulong key_buff_length;
ulong filepos, data_offset;
uint options_len;
@@ -115,7 +115,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
create_info->null_bits++;
data_offset= (create_info->null_bits + 7) / 8;
- error= pack_header(forminfo, create_fields, create_info->table_options,
+ error= pack_header(thd, forminfo, create_fields, create_info->table_options,
data_offset, db_file);
if (error)
@@ -150,51 +150,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table,
keys, key_info);
DBUG_PRINT("info", ("Options length: %u", options_len));
- /*
- This gives us the byte-position of the character at
- (character-position, not byte-position) TABLE_COMMENT_MAXLEN.
- The trick here is that character-positions start at 0, so the last
- character in a maximum-allowed length string would be at char-pos
- MAXLEN-1; charpos MAXLEN will be the position of the terminator.
- Consequently, bytepos(charpos(MAXLEN)) should be equal to
- comment[length] (which should also be the terminator, or at least
- the first byte after the payload in the strict sense). If this is
- not so (bytepos(charpos(MAXLEN)) comes /before/ the end of the
- string), the string is too long.
-
- For additional credit, realise that UTF-8 has 1-3 bytes before 6.0,
- and 1-4 bytes in 6.0 (6.0 also has UTF-32).
- */
- tmp_len= system_charset_info->cset->charpos(system_charset_info,
- create_info->comment.str,
- create_info->comment.str +
- create_info->comment.length,
- TABLE_COMMENT_MAXLEN);
-
- if (tmp_len < create_info->comment.length)
- {
- char *real_table_name= (char*) table;
- List_iterator<Create_field> it(create_fields);
- Create_field *field;
- while ((field=it++))
- {
- if (field->field && field->field->table &&
- (real_table_name= field->field->table->s->table_name.str))
- break;
- }
- if (thd->is_strict_mode())
- {
- my_error(ER_TOO_LONG_TABLE_COMMENT, MYF(0),
- real_table_name, TABLE_COMMENT_MAXLEN);
- DBUG_RETURN(frm);
- }
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_TABLE_COMMENT),
- real_table_name, TABLE_COMMENT_MAXLEN);
- push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- ER_TOO_LONG_TABLE_COMMENT, warn_buff);
- create_info->comment.length= tmp_len;
- }
+ if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_COMMENT, table))
+ DBUG_RETURN(frm);
/*
If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes,
store the comment in an extra segment (up to TABLE_COMMENT_MAXLEN bytes).
@@ -505,7 +463,8 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
/* Make formheader */
-static bool pack_header(uchar *forminfo, List<Create_field> &create_fields,
+static bool pack_header(THD *thd, uchar *forminfo,
+ List<Create_field> &create_fields,
uint table_options, ulong data_offset, handler *file)
{
uint length,int_count,int_length,no_empty, int_parts;
@@ -530,32 +489,18 @@ static bool pack_header(uchar *forminfo, List<Create_field> &create_fields,
Create_field *field;
while ((field=it++))
{
- uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
- field->comment.str,
- field->comment.str +
- field->comment.length,
- COLUMN_COMMENT_MAXLEN);
- if (tmp_len < field->comment.length)
- {
- myf myf_warning= current_thd->is_strict_mode() ? 0 : ME_JUST_WARNING;
-
- my_error(ER_TOO_LONG_FIELD_COMMENT, myf_warning, field->field_name,
- COLUMN_COMMENT_MAXLEN);
+ if (validate_comment_length(thd, &field->comment, COLUMN_COMMENT_MAXLEN,
+ ER_TOO_LONG_FIELD_COMMENT, field->field_name))
+ DBUG_RETURN(1);
- if (!myf_warning)
- DBUG_RETURN(1);
-
- field->comment.length= tmp_len;
- }
if (field->vcol_info)
{
uint col_expr_maxlen= field->virtual_col_expr_maxlen();
- tmp_len=
- system_charset_info->cset->charpos(system_charset_info,
- field->vcol_info->expr_str.str,
- field->vcol_info->expr_str.str +
- field->vcol_info->expr_str.length,
- col_expr_maxlen);
+ uint tmp_len= my_charpos(system_charset_info,
+ field->vcol_info->expr_str.str,
+ field->vcol_info->expr_str.str +
+ field->vcol_info->expr_str.length,
+ col_expr_maxlen);
if (tmp_len < field->vcol_info->expr_str.length)
{