summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Petrunia <psergey@askmonty.org>2016-03-28 22:18:38 +0300
committerSergei Petrunia <psergey@askmonty.org>2016-03-28 22:18:38 +0300
commit2bd4dc38e0d5eb257e2e29413dd01239ce075d42 (patch)
treedad0557b1d927d012aebd1c36c8c3e00462adcb1 /sql
parent44fdb56c977259b2801c612116813beda403df78 (diff)
parent3df261dc31ab18ee1537f327b07320b0a07fb8f5 (diff)
downloadmariadb-git-2bd4dc38e0d5eb257e2e29413dd01239ce075d42.tar.gz
Merge branch '10.2' into bb-10.2-mdev9543
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt4
-rw-r--r--sql/authors.h4
-rw-r--r--sql/bounded_queue.h2
-rw-r--r--sql/client_settings.h4
-rw-r--r--sql/contributors.h20
-rw-r--r--sql/discover.cc5
-rw-r--r--sql/event_db_repository.cc7
-rw-r--r--sql/event_queue.cc2
-rw-r--r--sql/event_scheduler.cc17
-rw-r--r--sql/events.cc31
-rw-r--r--sql/field.cc50
-rw-r--r--sql/field.h21
-rw-r--r--sql/field_conv.cc8
-rw-r--r--sql/filesort.cc289
-rw-r--r--sql/filesort.h90
-rw-r--r--sql/filesort_utils.cc68
-rw-r--r--sql/filesort_utils.h34
-rw-r--r--sql/gcalc_slicescan.cc266
-rw-r--r--sql/gcalc_slicescan.h22
-rw-r--r--sql/gcalc_tools.cc6
-rw-r--r--sql/ha_partition.cc2
-rw-r--r--sql/handler.cc253
-rw-r--r--sql/handler.h55
-rw-r--r--sql/hostname.cc4
-rw-r--r--sql/hostname.h2
-rw-r--r--sql/item.cc29
-rw-r--r--sql/item.h62
-rw-r--r--sql/item_cmpfunc.cc434
-rw-r--r--sql/item_cmpfunc.h124
-rw-r--r--sql/item_func.cc38
-rw-r--r--sql/item_func.h20
-rw-r--r--sql/item_geofunc.cc32
-rw-r--r--sql/item_row.h11
-rw-r--r--sql/item_strfunc.cc8
-rw-r--r--sql/item_strfunc.h10
-rw-r--r--sql/item_subselect.cc9
-rw-r--r--sql/item_subselect.h3
-rw-r--r--sql/item_sum.cc57
-rw-r--r--sql/item_sum.h14
-rw-r--r--sql/item_timefunc.cc4
-rw-r--r--sql/lex.h1
-rw-r--r--sql/log.cc38
-rw-r--r--sql/log_event.cc58
-rw-r--r--sql/log_event_old.cc4
-rw-r--r--sql/mdl.cc11
-rw-r--r--sql/my_apc.cc2
-rw-r--r--sql/mysql_install_db.cc6
-rw-r--r--sql/mysqld.cc612
-rw-r--r--sql/mysqld.h46
-rw-r--r--sql/net_serv.cc42
-rw-r--r--sql/opt_range.cc45
-rw-r--r--sql/opt_range.h3
-rw-r--r--sql/opt_subselect.cc18
-rw-r--r--sql/records.cc59
-rw-r--r--sql/records.h6
-rw-r--r--sql/rpl_gtid.cc4
-rw-r--r--sql/rpl_mi.cc18
-rw-r--r--sql/rpl_mi.h2
-rw-r--r--sql/rpl_parallel.cc16
-rw-r--r--sql/rpl_parallel.h2
-rw-r--r--sql/rpl_record.cc4
-rw-r--r--sql/rpl_reporting.cc1
-rw-r--r--sql/rpl_rli.cc4
-rw-r--r--sql/rpl_utility.cc43
-rw-r--r--sql/scheduler.cc21
-rw-r--r--sql/scheduler.h2
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/set_var.h6
-rw-r--r--sql/share/errmsg-utf8.txt52
-rw-r--r--sql/signal_handler.cc5
-rw-r--r--sql/slave.cc521
-rw-r--r--sql/slave.h12
-rw-r--r--sql/sp.cc80
-rw-r--r--sql/sp.h2
-rw-r--r--sql/sp_head.cc83
-rw-r--r--sql/sp_head.h5
-rw-r--r--sql/sql_acl.cc527
-rw-r--r--sql/sql_acl.h4
-rw-r--r--sql/sql_admin.cc105
-rw-r--r--sql/sql_audit.cc147
-rw-r--r--sql/sql_audit.h346
-rw-r--r--sql/sql_base.cc249
-rw-r--r--sql/sql_base.h12
-rw-r--r--sql/sql_cache.cc20
-rw-r--r--sql/sql_class.cc166
-rw-r--r--sql/sql_class.h157
-rw-r--r--sql/sql_cmd.h2
-rw-r--r--sql/sql_connect.cc168
-rw-r--r--sql/sql_connect.h37
-rw-r--r--sql/sql_const.h2
-rw-r--r--sql/sql_cte.cc601
-rw-r--r--sql/sql_cte.h178
-rw-r--r--sql/sql_delete.cc62
-rw-r--r--sql/sql_derived.cc4
-rw-r--r--sql/sql_explain.cc6
-rw-r--r--sql/sql_explain.h3
-rw-r--r--sql/sql_help.cc17
-rw-r--r--sql/sql_insert.cc35
-rw-r--r--sql/sql_join_cache.cc2
-rw-r--r--sql/sql_lex.cc398
-rw-r--r--sql/sql_lex.h81
-rw-r--r--sql/sql_list.h4
-rw-r--r--sql/sql_parse.cc683
-rw-r--r--sql/sql_parse.h6
-rw-r--r--sql/sql_plugin.cc9
-rw-r--r--sql/sql_plugin_services.ic2
-rw-r--r--sql/sql_prepare.cc280
-rw-r--r--sql/sql_prepare.h4
-rw-r--r--sql/sql_priv.h4
-rw-r--r--sql/sql_repl.cc77
-rw-r--r--sql/sql_repl.h1
-rw-r--r--sql/sql_select.cc237
-rw-r--r--sql/sql_select.h11
-rw-r--r--sql/sql_servers.cc4
-rw-r--r--sql/sql_show.cc191
-rw-r--r--sql/sql_show.h11
-rw-r--r--sql/sql_sort.h5
-rw-r--r--sql/sql_statistics.cc1
-rw-r--r--sql/sql_string.cc114
-rw-r--r--sql/sql_table.cc131
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/sql_time.h3
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_type.cc16
-rw-r--r--sql/sql_udf.cc3
-rw-r--r--sql/sql_union.cc16
-rw-r--r--sql/sql_update.cc40
-rw-r--r--sql/sql_view.cc19
-rw-r--r--sql/sql_view.h4
-rw-r--r--sql/sql_window.cc16
-rw-r--r--sql/sql_yacc.yy445
-rw-r--r--sql/structs.h9
-rw-r--r--sql/sys_vars.cc67
-rw-r--r--sql/sys_vars.ic11
-rw-r--r--sql/table.cc31
-rw-r--r--sql/table.h130
-rw-r--r--sql/table_cache.cc65
-rw-r--r--sql/table_cache.h59
-rw-r--r--sql/threadpool.h5
-rw-r--r--sql/threadpool_common.cc81
-rw-r--r--sql/threadpool_unix.cc75
-rw-r--r--sql/threadpool_win.cc38
-rw-r--r--sql/tztime.cc8
-rw-r--r--sql/uniques.cc70
-rw-r--r--sql/uniques.h100
-rw-r--r--sql/wsrep_applier.cc31
-rw-r--r--sql/wsrep_binlog.cc16
-rw-r--r--sql/wsrep_dummy.cc2
-rw-r--r--sql/wsrep_hton.cc42
-rw-r--r--sql/wsrep_mysqld.cc146
-rw-r--r--sql/wsrep_mysqld.h4
-rw-r--r--sql/wsrep_sst.cc136
-rw-r--r--sql/wsrep_thd.cc66
153 files changed, 7118 insertions, 3702 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 19fbed85737..062e59bee5b 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -112,7 +112,8 @@ SET (SQL_SOURCE
sql_statistics.cc sql_string.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
- sql_time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
+ sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc
+ uniques.cc uniques.h
rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
@@ -138,6 +139,7 @@ SET (SQL_SOURCE
rpl_gtid.cc rpl_parallel.cc
sql_type.cc sql_type.h
item_windowfunc.cc sql_window.cc
+ sql_cte.cc sql_cte.h
${WSREP_SOURCES}
table_cache.cc encryption.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
diff --git a/sql/authors.h b/sql/authors.h
index cc9889bcdbc..3a8f5497248 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -56,7 +56,7 @@ struct show_table_authors_st show_table_authors[]= {
"Unicode and character sets" },
{ "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia",
"GIS extensions, embedded server, precision math"},
- { "Daniel Bartholomew", "Raleigh, USA", "MariaDB documentation"},
+ { "Daniel Bartholomew", "Raleigh, USA", "MariaDB documentation, Buildbot, releases"},
{ "Colin Charles", "Selangor, Malesia", "MariaDB documentation, talks at a LOT of conferences"},
{ "Sergey Vojtovich", "Izhevsk, Russia",
"initial implementation of plugin architecture, maintained native storage engines (MyISAM, MEMORY, ARCHIVE, etc), rewrite of table cache"},
@@ -73,6 +73,8 @@ struct show_table_authors_st show_table_authors[]= {
{ "Pavel Ivanov", "USA", "Some patches and bug fixes"},
{ "Konstantin Osipov", "Moscow, Russia",
"Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" },
+ { "Ian Gilfillan", "South Africa", "MariaDB documentation"},
+ { "Federico Razolli", "Italy", "MariaDB documentation Italian translation"},
/* People working on MySQL code base (not NDB) */
{ "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h
index 2d4e6cff96d..88c2bbc238d 100644
--- a/sql/bounded_queue.h
+++ b/sql/bounded_queue.h
@@ -16,11 +16,11 @@
#ifndef BOUNDED_QUEUE_INCLUDED
#define BOUNDED_QUEUE_INCLUDED
-#include <string.h>
#include "my_global.h"
#include "my_base.h"
#include "my_sys.h"
#include "queues.h"
+#include <string.h>
class Sort_param;
diff --git a/sql/client_settings.h b/sql/client_settings.h
index d6a157f71fd..486862b276d 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -28,7 +28,7 @@
When adding capabilities here, consider if they should be also added to
the libmysql version.
*/
-#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
+#define CLIENT_CAPABILITIES (CLIENT_MYSQL | \
CLIENT_LONG_FLAG | \
CLIENT_TRANSACTIONS | \
CLIENT_PROTOCOL_41 | \
@@ -37,7 +37,7 @@
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
CLIENT_CONNECT_ATTRS)
-#define read_user_name(A) {}
+#define read_user_name(A) A[0]= 0
#undef _CUSTOMCONFIG_
#define mysql_server_init(a,b,c) mysql_client_plugin_init()
diff --git a/sql/contributors.h b/sql/contributors.h
index 2479f611727..04f8b74aa65 100644
--- a/sql/contributors.h
+++ b/sql/contributors.h
@@ -37,21 +37,19 @@ struct show_table_contributors_st {
struct show_table_contributors_st show_table_contributors[]= {
/* MariaDB foundation members, in contribution, size , time order */
- {"Booking.com", "http://www.booking.com", "Founding member of the MariaDB foundation"},
- {"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"},
+ {"Booking.com", "http://www.booking.com", "Founding member of the MariaDB Foundation"},
+ {"MariaDB Corporation", "https://mariadb.com", "Founding member of the MariaDB Foundation"},
+ {"Auttomattic", "http://automattic.com", "Member of the MariaDB Foundation"},
+ {"Visma", "http://visma.com", "Member of the MariaDB Foundation"},
+ {"Nexedi", "http://www.nexedi.com", "Member of the MariaDB Foundation"},
+ {"Acronis", "http://www.acronis.com", "Member of the MariaDB Foundation"},
/* Smaller sponsors, newer per year */
- {"Verkkokauppa.com", "Finland", "Sponsor of the MariaDB foundation"},
- {"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"},
+ {"Verkkokauppa.com", "Finland", "Sponsor of the MariaDB Foundation"},
+ {"Webyog", "Bangalore", "Sponsor of the MariaDB Foundation"},
/* Sponsors of important features */
- {"Google", "USA", "Sponsoring parallel replication and GTID" },
+ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"},
{"Facebook", "USA", "Sponsoring non-blocking API, LIMIT ROWS EXAMINED etc"},
/* Individual contributors, names in historical order, newer first */
diff --git a/sql/discover.cc b/sql/discover.cc
index 82648e94bc5..d8ed718fc58 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -199,14 +199,15 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta,
end= cur + dirp->number_of_files;
while (cur < end)
{
- char *octothorp= strrchr(cur->name + 1, '#');
+ char *octothorp= strchr(cur->name + 1, '#');
char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR);
if (ext)
{
size_t len= (octothorp ? octothorp : ext) - cur->name;
if (from != cur &&
- (my_strnncoll(cs, (uchar*)from->name, len, (uchar*)cur->name, len) ||
+ (strlen(from->name) <= len ||
+ my_strnncoll(cs, (uchar*)from->name, len, (uchar*)cur->name, len) ||
(from->name[len] != FN_EXTCHAR && from->name[len] != '#')))
advance(from, to, cur, skip);
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index e39f727800a..5d0754cf766 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -499,7 +499,8 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
READ_RECORD read_record_info;
DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
- if (init_read_record(&read_record_info, thd, event_table, NULL, 1, 0, FALSE))
+ if (init_read_record(&read_record_info, thd, event_table, NULL, NULL, 1, 0,
+ FALSE))
DBUG_RETURN(TRUE);
/*
@@ -947,7 +948,7 @@ end:
@retval FALSE an event with such db/name key exists
- @retval TRUE no record found or an error occured.
+ @retval TRUE no record found or an error occurred.
*/
bool
@@ -1015,7 +1016,7 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
DBUG_VOID_RETURN;
/* only enabled events are in memory, so we go now and delete the rest */
- if (init_read_record(&read_record_info, thd, table, NULL, 1, 0, FALSE))
+ if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
goto end;
while (!ret && !(read_record_info.read_record(&read_record_info)) )
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 35187af23ac..ae8ba258717 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -191,7 +191,7 @@ Event_queue::deinit_queue()
@param[out] created set to TRUE if no error and the element is
added to the queue, FALSE otherwise
- @retval TRUE an error occured. The value of created is undefined,
+ @retval TRUE an error occurred. The value of created is undefined,
the element was not deleted.
@retval FALSE success
*/
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 95c5f6d9047..6a8bdabb948 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -135,9 +135,7 @@ post_init_event_thread(THD *thd)
}
thread_safe_increment32(&thread_count);
- mysql_mutex_lock(&LOCK_thread_count);
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
+ add_to_active_threads(thd);
inc_thread_running();
return FALSE;
}
@@ -191,9 +189,7 @@ pre_init_event_thread(THD* thd)
thd->net.read_timeout= slave_net_timeout;
thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
/*
Guarantees that we will see the thread in SHOW PROCESSLIST though its
@@ -479,7 +475,7 @@ Event_scheduler::run(THD *thd)
DBUG_ENTER("Event_scheduler::run");
sql_print_information("Event Scheduler: scheduler thread started with id %lu",
- thd->thread_id);
+ (ulong) thd->thread_id);
/*
Recalculate the values in the queue because there could have been stops
in executions of the scheduler and some times could have passed by.
@@ -669,13 +665,13 @@ Event_scheduler::stop()
state= STOPPING;
DBUG_PRINT("info", ("Scheduler thread has id %lu",
- scheduler_thd->thread_id));
+ (ulong) scheduler_thd->thread_id));
/* Lock from delete */
mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
/* This will wake up the thread if it waits on Queue's conditional */
sql_print_information("Event Scheduler: Killing the scheduler thread, "
"thread id %lu",
- scheduler_thd->thread_id);
+ (ulong) scheduler_thd->thread_id);
scheduler_thd->awake(KILL_CONNECTION);
mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
@@ -832,7 +828,8 @@ Event_scheduler::dump_internal_status()
puts("");
puts("Event scheduler status:");
printf("State : %s\n", scheduler_states_names[state].str);
- printf("Thread id : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
+ printf("Thread id : %lu\n", scheduler_thd ?
+ (ulong) scheduler_thd->thread_id : (ulong) 0);
printf("LLA : %s:%u\n", mutex_last_locked_in_func,
mutex_last_locked_at_line);
printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
diff --git a/sql/events.cc b/sql/events.cc
index b80ec993ac4..f428bc17927 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -333,6 +333,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
+
if (check_db_dir_existence(parse_data->dbname.str))
{
my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
@@ -347,10 +351,6 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
*/
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
- if (lock_object_name(thd, MDL_key::EVENT,
- parse_data->dbname.str, parse_data->name.str))
- DBUG_RETURN(TRUE);
-
if (thd->lex->create_info.or_replace() && event_queue)
event_queue->drop_event(thd, parse_data->dbname, parse_data->name);
@@ -454,6 +454,16 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
+
+ if (check_db_dir_existence(parse_data->dbname.str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
+ DBUG_RETURN(TRUE);
+ }
+
if (new_dbname) /* It's a rename */
{
@@ -476,6 +486,13 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if (check_access(thd, EVENT_ACL, new_dbname->str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
+ /*
+ Acquire mdl exclusive lock on target database name.
+ */
+ if (lock_object_name(thd, MDL_key::EVENT,
+ new_dbname->str, new_name->str))
+ DBUG_RETURN(TRUE);
+
/* Check that the target database exists */
if (check_db_dir_existence(new_dbname->str))
{
@@ -490,10 +507,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
*/
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
- if (lock_object_name(thd, MDL_key::EVENT,
- parse_data->dbname.str, parse_data->name.str))
- DBUG_RETURN(TRUE);
-
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->update_event(thd, parse_data,
new_dbname, new_name)))
@@ -1134,7 +1147,7 @@ Events::load_events_from_db(THD *thd)
DBUG_RETURN(TRUE);
}
- if (init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE))
+ if (init_read_record(&read_record_info, thd, table, NULL, NULL, 0, 1, FALSE))
{
close_thread_tables(thd);
DBUG_RETURN(TRUE);
diff --git a/sql/field.cc b/sql/field.cc
index 177f219c137..6be45005d48 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -5647,6 +5647,18 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
}
break;
case ANY_SUBST:
+ if (!is_temporal_type_with_date(const_item->field_type()))
+ {
+ MYSQL_TIME ltime;
+ if (const_item->get_date_with_conversion(&ltime,
+ TIME_FUZZY_DATES |
+ TIME_INVALID_DATES))
+ return NULL;
+ return new (thd->mem_root)
+ Item_datetime_literal_for_invalid_dates(thd, &ltime,
+ ltime.second_part ?
+ TIME_SECOND_PART_DIGITS : 0);
+ }
break;
}
return const_item;
@@ -5955,7 +5967,10 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
{
MYSQL_TIME ltime;
// Get the value of const_item with conversion from DATETIME to TIME
- if (const_item->get_time_with_conversion(thd, &ltime, TIME_TIME_ONLY))
+ if (const_item->get_time_with_conversion(thd, &ltime,
+ TIME_TIME_ONLY |
+ TIME_FUZZY_DATES |
+ TIME_INVALID_DATES))
return NULL;
/*
Replace a DATE/DATETIME constant to a TIME constant:
@@ -7868,7 +7883,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length, new_length;
String_copier copier;
- const char *tmp;
+ char *tmp;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
@@ -7878,6 +7893,29 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
return 0;
}
+ if (table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT
+ {
+ DBUG_ASSERT(!f_is_hex_escape(flags));
+ DBUG_ASSERT(field_charset == cs);
+ DBUG_ASSERT(length <= max_data_length());
+
+ new_length= length;
+ copy_length= table->in_use->variables.group_concat_max_len;
+ if (new_length > copy_length)
+ {
+ int well_formed_error;
+ new_length= cs->cset->well_formed_len(cs, from, from + copy_length,
+ new_length, &well_formed_error);
+ table->blob_storage->set_truncated_value(true);
+ }
+ if (!(tmp= table->blob_storage->store(from, new_length)))
+ goto oom_error;
+
+ Field_blob::store_length(new_length);
+ bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
+ return 0;
+ }
+
/*
If the 'from' address is in the range of the temporary 'value'-
object we need to copy the content to a different location or it will be
@@ -7904,15 +7942,14 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
new_length= MY_MIN(max_data_length(), field_charset->mbmaxlen * length);
if (value.alloc(new_length))
goto oom_error;
-
+ tmp= const_cast<char*>(value.ptr());
if (f_is_hex_escape(flags))
{
copy_length= my_copy_with_hex_escaping(field_charset,
- (char*) value.ptr(), new_length,
- from, length);
+ tmp, new_length,
+ from, length);
Field_blob::store_length(copy_length);
- tmp= value.ptr();
bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
return 0;
}
@@ -7920,7 +7957,6 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
(char*) value.ptr(), new_length,
cs, from, length);
Field_blob::store_length(copy_length);
- tmp= value.ptr();
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
return check_conversion_status(&copier, from + length, cs, true);
diff --git a/sql/field.h b/sql/field.h
index 435ea20825a..736c51c2ac3 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -609,6 +609,13 @@ public:
{
in_partitioning_expr= TRUE;
}
+ bool is_equal(Virtual_column_info* vcol)
+ {
+ return field_type == vcol->get_real_type()
+ && stored_in_db == vcol->is_stored()
+ && expr_str.length == vcol->expr_str.length
+ && memcmp(expr_str.str, vcol->expr_str.str, expr_str.length) == 0;
+ }
};
class Field: public Value_source
@@ -1700,6 +1707,16 @@ public:
return do_field_real;
}
int save_in_field(Field *to) { return to->store(val_real()); }
+ bool memcpy_field_possible(const Field *from) const
+ {
+ /*
+ Cannot do memcpy from a longer field to a shorter field,
+ e.g. a DOUBLE(53,10) into a DOUBLE(10,10).
+ But it should be OK the other way around.
+ */
+ return Field_num::memcpy_field_possible(from) &&
+ field_length >= from->field_length;
+ }
int store_decimal(const my_decimal *);
int store_time_dec(MYSQL_TIME *ltime, uint dec);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
@@ -3159,7 +3176,7 @@ public:
int store_field(Field *from)
{ // Be sure the value is stored
from->val_str(&value);
- if (!value.is_alloced() && from->is_updatable())
+ if (table->copy_blobs || (!value.is_alloced() && from->is_updatable()))
value.copy();
return store(value.ptr(), value.length(), from->charset());
}
@@ -3736,7 +3753,7 @@ public:
const char *field_name_arg) const
{
return ::make_field(share, mem_root, ptr,
- length, null_pos, null_bit,
+ (uint32)length, null_pos, null_bit,
pack_flag, sql_type, charset,
geom_type, srid, unireg_check, interval,
field_name_arg);
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index ecad05c7530..263c5bb5017 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -643,9 +642,6 @@ void Copy_field::set(uchar *to,Field *from)
Field_blob::store. Is this in order to trigger the call to
well_formed_copy_nchars, by changing the pointer copy->tmp.ptr()?
That call will take place anyway in all known cases.
-
- - The above causes a truncation to MAX_FIELD_WIDTH. Is this the intended
- effect? Truncation is handled by well_formed_copy_nchars anyway.
*/
void Copy_field::set(Field *to,Field *from,bool save)
{
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 4a1f509a54d..78b74380f7e 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -50,34 +50,36 @@ if (my_b_write((file),(uchar*) (from),param->ref_length)) \
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
uchar *buf);
static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
- Filesort_info *fs_info,
+ SORT_INFO *fs_info,
IO_CACHE *buffer_file,
IO_CACHE *tempfile,
Bounded_queue<uchar, uchar> *pq,
ha_rows *found_rows);
-static bool write_keys(Sort_param *param, Filesort_info *fs_info,
+static bool write_keys(Sort_param *param, SORT_INFO *fs_info,
uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
static void register_used_fields(Sort_param *param);
static bool save_index(Sort_param *param, uint count,
- Filesort_info *table_sort);
+ SORT_INFO *table_sort);
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data,
Field **ptabfield,
- uint sortlength, uint *plength);
+ uint sortlength,
+ LEX_STRING *addon_buf);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
uchar *buff, uchar *buff_end);
-static bool check_if_pq_applicable(Sort_param *param, Filesort_info *info,
+static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info,
TABLE *table,
ha_rows records, ulong memory_available);
-
void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
ulong max_length_for_sort_data,
ha_rows maxrows, bool sort_positions)
{
+ DBUG_ASSERT(addon_field == 0 && addon_buf.length == 0);
+
sort_length= sortlen;
ref_length= table->file->ref_length;
if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
@@ -85,13 +87,13 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
{
/*
Get the descriptors of all fields whose values are appended
- to sorted fields and get its total length in addon_length.
+ to sorted fields and get its total length in addon_buf.length
*/
addon_field= get_addon_fields(max_length_for_sort_data,
- table->field, sort_length, &addon_length);
+ table->field, sort_length, &addon_buf);
}
if (addon_field)
- res_length= addon_length;
+ res_length= addon_buf.length;
else
{
res_length= ref_length;
@@ -101,7 +103,7 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
*/
sort_length+= ref_length;
}
- rec_length= sort_length + addon_length;
+ rec_length= sort_length + addon_buf.length;
max_rows= maxrows;
}
@@ -115,35 +117,33 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
Before calling filesort, one must have done
table->file->info(HA_STATUS_VARIABLE)
- The result set is stored in table->io_cache or
- table->record_pointers.
+ The result set is stored in
+ filesort_info->io_cache or
+ filesort_info->record_pointers.
@param thd Current thread
@param table Table to sort
@param filesort How to sort the table
- @param sort_positions Set to TRUE if we want to force sorting by position
+ @param sort_positions Set to TRUE if we want to force sorting by
+ position
(Needed by UPDATE/INSERT or ALTER TABLE or
when rowids are required by executor)
- @param[out] examined_rows Store number of examined rows here
- This is the number of found rows before
applying WHERE condition.
@param[out] found_rows Store the number of found rows here.
This is the number of found rows after
applying WHERE condition.
-
@note
If we sort by position (like if sort_positions is 1) filesort() will
call table->prepare_for_position().
@retval
- HA_POS_ERROR Error
- @retval
- \# Number of rows
+ 0 Error
+ # SORT_INFO
*/
-ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
- bool sort_positions, ha_rows *examined_rows,
- ha_rows *found_rows, Filesort_tracker* tracker)
+SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
+ bool sort_positions,
+ Filesort_tracker* tracker)
{
int error;
size_t memory_available= thd->variables.sortbuff_size;
@@ -161,39 +161,43 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
DBUG_ENTER("filesort");
if (!(s_length= filesort->make_sortorder(thd)))
- DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ DBUG_RETURN(NULL); /* purecov: inspected */
DBUG_EXECUTE("info",TEST_filesort(filesort->sortorder,s_length););
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
- Filesort_info table_sort= table->sort;
+ SORT_INFO *sort;
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
-
MYSQL_FILESORT_START(table->s->db.str, table->s->table_name.str);
DEBUG_SYNC(thd, "filesort_start");
+ if (!(sort= new SORT_INFO))
+ return 0;
+
+ if (subselect && subselect->filesort_buffer.is_allocated())
+ {
+ /* Reuse cache from last call */
+ sort->filesort_buffer= subselect->filesort_buffer;
+ sort->buffpek= subselect->sortbuffer;
+ subselect->filesort_buffer.reset();
+ subselect->sortbuffer.str=0;
+ }
+
+ outfile= &sort->io_cache;
+
/*
Release InnoDB's adaptive hash index latch (if holding) before
running a sort.
*/
ha_release_temporary_latches(thd);
- /*
- Don't use table->sort in filesort as it is also used by
- QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
- when index_merge select has finished with it.
- */
- table->sort.io_cache= NULL;
- DBUG_ASSERT(table_sort.record_pointers == NULL);
-
- outfile= table_sort.io_cache;
my_b_clear(&tempfile);
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
- *found_rows= HA_POS_ERROR;
+ sort->found_rows= HA_POS_ERROR;
param.init_for_filesort(sortlength(thd, filesort->sortorder, s_length,
&multi_byte_charset),
@@ -201,14 +205,12 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
thd->variables.max_length_for_sort_data,
max_rows, sort_positions);
- table_sort.addon_buf= 0;
- table_sort.addon_length= param.addon_length;
- table_sort.addon_field= param.addon_field;
- table_sort.unpack= unpack_addon_fields;
- if (param.addon_field &&
- !(table_sort.addon_buf=
- (uchar *) my_malloc(param.addon_length, MYF(MY_WME |
- MY_THREAD_SPECIFIC))))
+ sort->addon_buf= param.addon_buf;
+ sort->addon_field= param.addon_field;
+ sort->unpack= unpack_addon_fields;
+ if (multi_byte_charset &&
+ !(param.tmp_buffer= (char*) my_malloc(param.sort_length,
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
goto err;
if (select && select->quick)
@@ -221,12 +223,7 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
// If number of rows is not known, use as much of sort buffer as possible.
num_rows= table->file->estimate_rows_upper_bound();
- if (multi_byte_charset &&
- !(param.tmp_buffer= (char*) my_malloc(param.sort_length,
- MYF(MY_WME | MY_THREAD_SPECIFIC))))
- goto err;
-
- if (check_if_pq_applicable(&param, &table_sort,
+ if (check_if_pq_applicable(&param, sort,
table, num_rows, memory_available))
{
DBUG_PRINT("info", ("filesort PQ is applicable"));
@@ -238,45 +235,31 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
true, // max_at_top
NULL, // compare_function
compare_length,
- &make_sortkey, &param, table_sort.get_sort_keys()))
+ &make_sortkey, &param, sort->get_sort_keys()))
{
/*
If we fail to init pq, we have to give up:
out of memory means my_malloc() will call my_error().
*/
DBUG_PRINT("info", ("failed to allocate PQ"));
- table_sort.free_sort_buffer();
DBUG_ASSERT(thd->is_error());
goto err;
}
// For PQ queries (with limit) we initialize all pointers.
- table_sort.init_record_pointers();
+ sort->init_record_pointers();
}
else
{
DBUG_PRINT("info", ("filesort PQ is not applicable"));
- size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
+ size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY,
+ param.sort_length*MERGEBUFF2);
set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
while (memory_available >= min_sort_memory)
{
ulonglong keys= memory_available / (param.rec_length + sizeof(char*));
param.max_keys_per_buffer= (uint) MY_MIN(num_rows, keys);
- if (table_sort.get_sort_keys())
- {
- // If we have already allocated a buffer, it better have same size!
- if (!table_sort.check_sort_buffer_properties(param.max_keys_per_buffer,
- param.rec_length))
- {
- /*
- table->sort will still have a pointer to the same buffer,
- but that will be overwritten by the assignment below.
- */
- table_sort.free_sort_buffer();
- }
- }
- table_sort.alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length);
- if (table_sort.get_sort_keys())
+ if (sort->alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length))
break;
size_t old_memory_available= memory_available;
memory_available= memory_available/4*3;
@@ -289,7 +272,7 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
goto err;
}
- tracker->report_sort_buffer_size(table_sort.sort_buffer_size());
+ tracker->report_sort_buffer_size(sort->sort_buffer_size());
}
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
@@ -299,21 +282,21 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
param.sort_form= table;
param.end=(param.local_sortorder=filesort->sortorder)+s_length;
num_rows= find_all_keys(thd, &param, select,
- &table_sort,
+ sort,
&buffpek_pointers,
&tempfile,
pq.is_initialized() ? &pq : NULL,
- found_rows);
+ &sort->found_rows);
if (num_rows == HA_POS_ERROR)
goto err;
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
tracker->report_merge_passes_at_start(thd->query_plan_fsort_passes);
- tracker->report_row_numbers(param.examined_rows, *found_rows, num_rows);
+ tracker->report_row_numbers(param.examined_rows, sort->found_rows, num_rows);
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param, (uint) num_rows, &table_sort))
+ if (save_index(&param, (uint) num_rows, sort))
goto err;
}
else
@@ -321,17 +304,17 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
/* filesort cannot handle zero-length records during merge. */
DBUG_ASSERT(param.sort_length != 0);
- if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
+ if (sort->buffpek.str && sort->buffpek.length < maxbuffer)
{
- my_free(table_sort.buffpek);
- table_sort.buffpek= 0;
+ my_free(sort->buffpek.str);
+ sort->buffpek.str= 0;
}
- if (!(table_sort.buffpek=
- (uchar *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
- table_sort.buffpek)))
+ if (!(sort->buffpek.str=
+ (char *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
+ (uchar*) sort->buffpek.str)))
goto err;
- buffpek= (BUFFPEK *) table_sort.buffpek;
- table_sort.buffpek_len= maxbuffer;
+ sort->buffpek.length= maxbuffer;
+ buffpek= (BUFFPEK *) sort->buffpek.str;
close_cached_file(&buffpek_pointers);
/* Open cached file if it isn't open */
if (! my_b_inited(outfile) &&
@@ -350,7 +333,7 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
param.rec_length - 1);
maxbuffer--; // Offset from 0
if (merge_many_buff(&param,
- (uchar*) table_sort.get_sort_keys(),
+ (uchar*) sort->get_sort_keys(),
buffpek,&maxbuffer,
&tempfile))
goto err;
@@ -358,7 +341,7 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
if (merge_index(&param,
- (uchar*) table_sort.get_sort_keys(),
+ (uchar*) sort->get_sort_keys(),
buffpek,
maxbuffer,
&tempfile,
@@ -377,11 +360,18 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- table_sort.free_sort_buffer();
- my_free(buffpek);
- table_sort.buffpek= 0;
- table_sort.buffpek_len= 0;
+ sort->free_sort_buffer();
+ my_free(sort->buffpek.str);
+ }
+ else
+ {
+ /* Remember sort buffers for next subquery call */
+ subselect->filesort_buffer= sort->filesort_buffer;
+ subselect->sortbuffer= sort->buffpek;
+ sort->filesort_buffer.reset(); // Don't free this
}
+ sort->buffpek.str= 0;
+
close_cached_file(&tempfile);
close_cached_file(&buffpek_pointers);
if (my_b_inited(outfile))
@@ -402,13 +392,6 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
int kill_errno= thd->killed_errno();
DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY);
- /*
- We replace the table->sort at the end.
- Hence calling free_io_cache to make sure table->sort.io_cache
- used for QUICK_INDEX_MERGE_SELECT is free.
- */
- free_io_cache(table);
-
my_printf_error(ER_FILSORT_ABORT,
"%s: %s",
MYF(0),
@@ -429,50 +412,26 @@ ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
}
else
thd->inc_status_sort_rows(num_rows);
- *examined_rows= param.examined_rows;
+
+ sort->examined_rows= param.examined_rows;
+ sort->return_rows= num_rows;
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
#endif
- /* table->sort.io_cache should be free by this time */
- DBUG_ASSERT(NULL == table->sort.io_cache);
-
- // Assign the copy back!
- table->sort= table_sort;
-
DBUG_PRINT("exit",
- ("num_rows: %ld examined_rows: %ld found_rows: %ld",
- (long) num_rows, (long) *examined_rows, (long) *found_rows));
+ ("num_rows: %lld examined_rows: %lld found_rows: %lld",
+ (longlong) sort->return_rows, (longlong) sort->examined_rows,
+ (longlong) sort->found_rows));
MYSQL_FILESORT_DONE(error, num_rows);
- DBUG_RETURN(error ? HA_POS_ERROR : num_rows);
-} /* filesort */
-
-
-void filesort_free_buffers(TABLE *table, bool full)
-{
- DBUG_ENTER("filesort_free_buffers");
- my_free(table->sort.record_pointers);
- table->sort.record_pointers= NULL;
-
- if (unlikely(full))
- {
- table->sort.free_sort_buffer();
- my_free(table->sort.buffpek);
- table->sort.buffpek= NULL;
- table->sort.buffpek_len= 0;
- }
-
- /* addon_buf is only allocated if addon_field is set */
- if (unlikely(table->sort.addon_field))
+ if (error)
{
- my_free(table->sort.addon_field);
- my_free(table->sort.addon_buf);
- table->sort.addon_buf= NULL;
- table->sort.addon_field= NULL;
+ delete sort;
+ sort= 0;
}
- DBUG_VOID_RETURN;
-}
+ DBUG_RETURN(sort);
+} /* filesort */
void Filesort::cleanup()
@@ -521,7 +480,7 @@ uint Filesort::make_sortorder(THD *thd)
DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
DBUG_RETURN(count);
-}
+ }
/** Read 'count' number of buffer pointers into memory. */
@@ -726,7 +685,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
*/
static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
- Filesort_info *fs_info,
+ SORT_INFO *fs_info,
IO_CACHE *buffpek_pointers,
IO_CACHE *tempfile,
Bounded_queue<uchar, uchar> *pq,
@@ -931,7 +890,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
const ha_rows retval=
my_b_inited(tempfile) ?
(ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx;
- DBUG_PRINT("info", ("find_all_keys return %u", (uint) retval));
+ DBUG_PRINT("info", ("find_all_keys return %llu", (ulonglong) retval));
DBUG_RETURN(retval);
} /* find_all_keys */
@@ -959,7 +918,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
*/
static bool
-write_keys(Sort_param *param, Filesort_info *fs_info, uint count,
+write_keys(Sort_param *param, SORT_INFO *fs_info, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
size_t rec_length;
@@ -1328,11 +1287,13 @@ static void register_used_fields(Sort_param *param)
}
-static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort)
+static bool save_index(Sort_param *param, uint count,
+ SORT_INFO *table_sort)
{
uint offset,res_length;
uchar *to;
DBUG_ENTER("save_index");
+ DBUG_ASSERT(table_sort->record_pointers == 0);
table_sort->sort_buffer(param, count);
res_length= param->res_length;
@@ -1381,7 +1342,7 @@ static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort)
*/
bool check_if_pq_applicable(Sort_param *param,
- Filesort_info *filesort_info,
+ SORT_INFO *filesort_info,
TABLE *table, ha_rows num_rows,
ulong memory_available)
{
@@ -1415,9 +1376,8 @@ bool check_if_pq_applicable(Sort_param *param,
// The whole source set fits into memory.
if (param->max_rows < num_rows/PQ_slowness )
{
- filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->rec_length);
- DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length) != NULL);
}
else
{
@@ -1429,9 +1389,8 @@ bool check_if_pq_applicable(Sort_param *param,
// Do we have space for LIMIT rows in memory?
if (param->max_keys_per_buffer < num_available_keys)
{
- filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->rec_length);
- DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length) != NULL);
}
// Try to strip off addon fields.
@@ -1467,17 +1426,14 @@ bool check_if_pq_applicable(Sort_param *param,
if (sort_merge_cost < pq_cost)
DBUG_RETURN(false);
- filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->sort_length + param->ref_length);
- if (filesort_info->get_sort_keys())
+ if (filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->sort_length +
+ param->ref_length))
{
- // Make attached data to be references instead of fields.
- my_free(filesort_info->addon_buf);
+ /* Make attached data to be references instead of fields. */
my_free(filesort_info->addon_field);
- filesort_info->addon_buf= NULL;
filesort_info->addon_field= NULL;
param->addon_field= NULL;
- param->addon_length= 0;
param->res_length= param->ref_length;
param->sort_length+= param->ref_length;
@@ -2047,7 +2003,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
@param thd Current thread
@param ptabfield Array of references to the table fields
@param sortlength Total length of sorted fields
- @param[out] plength Total length of appended fields
+ @param [out] addon_buf Buffer to us for appended fields
@note
The null bits for the appended values are supposed to be put together
@@ -2061,7 +2017,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
static SORT_ADDON_FIELD *
get_addon_fields(ulong max_length_for_sort_data,
- Field **ptabfield, uint sortlength, uint *plength)
+ Field **ptabfield, uint sortlength, LEX_STRING *addon_buf)
{
Field **pfield;
Field *field;
@@ -2070,6 +2026,7 @@ get_addon_fields(ulong max_length_for_sort_data,
uint fields= 0;
uint null_fields= 0;
MY_BITMAP *read_set= (*ptabfield)->table->read_set;
+ DBUG_ENTER("get_addon_fields");
/*
If there is a reference to a field in the query add it
@@ -2081,31 +2038,33 @@ get_addon_fields(ulong max_length_for_sort_data,
the values directly from sorted fields.
But beware the case when item->cmp_type() != item->result_type()
*/
- *plength= 0;
+ addon_buf->str= 0;
+ addon_buf->length= 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{
if (!bitmap_is_set(read_set, field->field_index))
continue;
if (field->flags & BLOB_FLAG)
- return 0;
+ DBUG_RETURN(0);
length+= field->max_packed_col_length(field->pack_length());
if (field->maybe_null())
null_fields++;
fields++;
}
if (!fields)
- return 0;
+ DBUG_RETURN(0);
length+= (null_fields+7)/8;
if (length+sortlength > max_length_for_sort_data ||
- !(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
- (fields+1),
- MYF(MY_WME |
- MY_THREAD_SPECIFIC))))
- return 0;
+ !my_multi_malloc(MYF(MY_WME | MY_THREAD_SPECIFIC),
+ &addonf, sizeof(SORT_ADDON_FIELD) * (fields+1),
+ &addon_buf->str, length,
+ NullS))
- *plength= length;
+ DBUG_RETURN(0);
+
+ addon_buf->length= length;
length= (null_fields+7)/8;
null_fields= 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
@@ -2132,7 +2091,7 @@ get_addon_fields(ulong max_length_for_sort_data,
addonf->field= 0; // Put end marker
DBUG_PRINT("info",("addon_length: %d",length));
- return (addonf-fields);
+ DBUG_RETURN(addonf-fields);
}
@@ -2218,3 +2177,13 @@ void change_double_for_sort(double nr,uchar *to)
}
}
+/**
+ Free SORT_INFO
+*/
+
+SORT_INFO::~SORT_INFO()
+{
+ DBUG_ENTER("~SORT_INFO::SORT_INFO()");
+ free_data();
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/filesort.h b/sql/filesort.h
index 16ea89ec55a..6d665dbe0aa 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -16,11 +16,9 @@
#ifndef FILESORT_INCLUDED
#define FILESORT_INCLUDED
-class SQL_SELECT;
-
-#include "my_global.h" /* uint, uchar */
#include "my_base.h" /* ha_rows */
#include "sql_list.h" /* Sql_alloc */
+#include "filesort_utils.h"
class SQL_SELECT;
class THD;
@@ -71,10 +69,88 @@ private:
void cleanup();
};
-ha_rows filesort(THD *thd, TABLE *table, Filesort *filesort,
- bool sort_positions, ha_rows *examined_rows,
- ha_rows *found_rows, Filesort_tracker* tracker);
-void filesort_free_buffers(TABLE *table, bool full);
+
+class SORT_INFO
+{
+ /// Buffer for sorting keys.
+ Filesort_buffer filesort_buffer;
+
+public:
+ SORT_INFO()
+ :addon_field(0), record_pointers(0)
+ {
+ buffpek.str= 0;
+ my_b_clear(&io_cache);
+ }
+
+ ~SORT_INFO();
+
+ void free_data()
+ {
+ close_cached_file(&io_cache);
+ my_free(record_pointers);
+ my_free(buffpek.str);
+ my_free(addon_field);
+ }
+
+ void reset()
+ {
+ free_data();
+ record_pointers= 0;
+ buffpek.str= 0;
+ addon_field= 0;
+ }
+
+
+ IO_CACHE io_cache; /* If sorted through filesort */
+ LEX_STRING buffpek; /* Buffer for buffpek structures */
+ LEX_STRING addon_buf; /* Pointer to a buffer if sorted with fields */
+ struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
+ /* To unpack back */
+ void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *);
+ uchar *record_pointers; /* If sorted in memory */
+ /*
+ How many rows in final result.
+ Also how many rows in record_pointers, if used
+ */
+ ha_rows return_rows;
+ ha_rows examined_rows; /* How many rows read */
+ ha_rows found_rows; /* How many rows was accepted */
+
+ /** Sort filesort_buffer */
+ void sort_buffer(Sort_param *param, uint count)
+ { filesort_buffer.sort_buffer(param, count); }
+
+ /**
+ Accessors for Filesort_buffer (which @c).
+ */
+ uchar *get_record_buffer(uint idx)
+ { return filesort_buffer.get_record_buffer(idx); }
+
+ uchar **get_sort_keys()
+ { return filesort_buffer.get_sort_keys(); }
+
+ uchar **alloc_sort_buffer(uint num_records, uint record_length)
+ { return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
+
+ void free_sort_buffer()
+ { filesort_buffer.free_sort_buffer(); }
+
+ void init_record_pointers()
+ { filesort_buffer.init_record_pointers(); }
+
+ size_t sort_buffer_size() const
+ { return filesort_buffer.sort_buffer_size(); }
+
+ friend SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
+ bool sort_positions,
+ Filesort_tracker* tracker);
+};
+
+SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
+ bool sort_positions,
+ Filesort_tracker* tracker);
+
void change_double_for_sort(double nr,uchar *to);
#endif /* FILESORT_INCLUDED */
diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc
index 1cef30b6a56..34110dcfc1f 100644
--- a/sql/filesort_utils.cc
+++ b/sql/filesort_utils.cc
@@ -85,31 +85,66 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
return total_cost;
}
-uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
-{
- ulong sort_buff_sz;
+/*
+ alloc_sort_buffer()
- DBUG_ENTER("alloc_sort_buffer");
+ Allocate buffer for sorting keys.
+ Try to reuse old buffer if possible.
+ @return
+ 0 Error
+ # Pointer to allocated buffer
+*/
+
+uchar **Filesort_buffer::alloc_sort_buffer(uint num_records,
+ uint record_length)
+{
+ size_t buff_size;
+ uchar **sort_keys, **start_of_data;
+ DBUG_ENTER("alloc_sort_buffer");
DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
DBUG_SET("+d,simulate_out_of_memory"););
- if (m_idx_array.is_null())
+ buff_size= num_records * (record_length + sizeof(uchar*));
+ set_if_bigger(buff_size, record_length * MERGEBUFF2);
+
+ if (!m_idx_array.is_null())
{
- sort_buff_sz= num_records * (record_length + sizeof(uchar*));
- set_if_bigger(sort_buff_sz, record_length * MERGEBUFF2);
- uchar **sort_keys=
- (uchar**) my_malloc(sort_buff_sz, MYF(MY_THREAD_SPECIFIC));
- m_idx_array= Idx_array(sort_keys, num_records);
- m_record_length= record_length;
- uchar **start_of_data= m_idx_array.array() + m_idx_array.size();
- m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
+ /*
+ Reuse old buffer if exists and is large enough
+ Note that we don't make the buffer smaller, as we want to be
+ prepared for next subquery iteration.
+ */
+
+ sort_keys= m_idx_array.array();
+ if (buff_size > allocated_size)
+ {
+ /*
+ Better to free and alloc than realloc as we don't have to remember
+ the old values
+ */
+ my_free(sort_keys);
+ if (!(sort_keys= (uchar**) my_malloc(buff_size,
+ MYF(MY_THREAD_SPECIFIC))))
+ {
+ reset();
+ DBUG_RETURN(0);
+ }
+ allocated_size= buff_size;
+ }
}
else
{
- DBUG_ASSERT(num_records == m_idx_array.size());
- DBUG_ASSERT(record_length == m_record_length);
+ if (!(sort_keys= (uchar**) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))))
+ DBUG_RETURN(0);
+ allocated_size= buff_size;
}
+
+ m_idx_array= Idx_array(sort_keys, num_records);
+ m_record_length= record_length;
+ start_of_data= m_idx_array.array() + m_idx_array.size();
+ m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
+
DBUG_RETURN(m_idx_array.array());
}
@@ -117,8 +152,7 @@ uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
void Filesort_buffer::free_sort_buffer()
{
my_free(m_idx_array.array());
- m_idx_array= Idx_array();
- m_record_length= 0;
+ m_idx_array.reset();
m_start_of_data= NULL;
}
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
index 00fa6f2566b..d537b602edf 100644
--- a/sql/filesort_utils.h
+++ b/sql/filesort_utils.h
@@ -60,9 +60,23 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
class Filesort_buffer
{
public:
- Filesort_buffer() :
- m_idx_array(), m_record_length(0), m_start_of_data(NULL)
+ Filesort_buffer()
+ : m_idx_array(), m_start_of_data(NULL), allocated_size(0)
{}
+
+ ~Filesort_buffer()
+ {
+ my_free(m_idx_array.array());
+ }
+
+ bool is_allocated()
+ {
+ return m_idx_array.array() != 0;
+ }
+ void reset()
+ {
+ m_idx_array.reset();
+ }
/** Sort me... */
void sort_buffer(const Sort_param *param, uint count);
@@ -84,20 +98,12 @@ public:
/// Returns total size: pointer array + record buffers.
size_t sort_buffer_size() const
{
- return m_idx_array.size() * (m_record_length + sizeof(uchar*));
+ return allocated_size;
}
/// Allocates the buffer, but does *not* initialize pointers.
uchar **alloc_sort_buffer(uint num_records, uint record_length);
-
- /// Check <num_records, record_length> for the buffer
- bool check_sort_buffer_properties(uint num_records, uint record_length)
- {
- return (static_cast<uint>(m_idx_array.size()) == num_records &&
- m_record_length == record_length);
- }
-
/// Frees the buffer.
void free_sort_buffer();
@@ -115,15 +121,17 @@ public:
m_idx_array= rhs.m_idx_array;
m_record_length= rhs.m_record_length;
m_start_of_data= rhs.m_start_of_data;
+ allocated_size= rhs.allocated_size;
return *this;
}
private:
typedef Bounds_checked_array<uchar*> Idx_array;
- Idx_array m_idx_array;
+ Idx_array m_idx_array; /* Pointers to key data */
uint m_record_length;
- uchar *m_start_of_data;
+ uchar *m_start_of_data; /* Start of key data */
+ size_t allocated_size;
};
#endif // FILESORT_UTILS_INCLUDED
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index ed533abdaf4..f62c413fd35 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -49,14 +49,14 @@ typedef int (*sc_compare_func)(const void*, const void*);
static Gcalc_scan_iterator::point *eq_sp(const Gcalc_heap::Info *pi)
{
GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_eq_node);
- return (Gcalc_scan_iterator::point *) pi->eq_data;
+ return (Gcalc_scan_iterator::point *) pi->node.eq.data;
}
static Gcalc_scan_iterator::intersection_info *i_data(const Gcalc_heap::Info *pi)
{
GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_intersection);
- return (Gcalc_scan_iterator::intersection_info *) pi->intersection_data;
+ return (Gcalc_scan_iterator::intersection_info *) pi->node.intersection.data;
}
@@ -103,8 +103,8 @@ const char *gcalc_ev_name(int ev)
static int gcalc_pi_str(char *str, const Gcalc_heap::Info *pi, const char *postfix)
{
return sprintf(str, "%s %d %d | %s %d %d%s",
- GCALC_SIGN(pi->ix[0]) ? "-":"", FIRST_DIGIT(pi->ix[0]),pi->ix[1],
- GCALC_SIGN(pi->iy[0]) ? "-":"", FIRST_DIGIT(pi->iy[0]),pi->iy[1],
+ GCALC_SIGN(pi->node.shape.ix[0]) ? "-":"", FIRST_DIGIT(pi->node.shape.ix[0]),pi->node.shape.ix[1],
+ GCALC_SIGN(pi->node.shape.iy[0]) ? "-":"", FIRST_DIGIT(pi->node.shape.iy[0]),pi->node.shape.iy[1],
postfix);
}
@@ -594,8 +594,8 @@ void Gcalc_scan_iterator::intersection_info::do_calc_t()
Gcalc_coord1 a2_a1x, a2_a1y;
Gcalc_coord2 x1y2, x2y1;
- gcalc_sub_coord1(a2_a1x, edge_b->pi->ix, edge_a->pi->ix);
- gcalc_sub_coord1(a2_a1y, edge_b->pi->iy, edge_a->pi->iy);
+ gcalc_sub_coord1(a2_a1x, edge_b->pi->node.shape.ix, edge_a->pi->node.shape.ix);
+ gcalc_sub_coord1(a2_a1y, edge_b->pi->node.shape.iy, edge_a->pi->node.shape.iy);
GCALC_DBUG_ASSERT(!gcalc_is_zero(edge_a->dy, GCALC_COORD_BASE) ||
!gcalc_is_zero(edge_b->dy, GCALC_COORD_BASE));
@@ -619,7 +619,7 @@ void Gcalc_scan_iterator::intersection_info::do_calc_y()
Gcalc_coord3 a_tb, b_ta;
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, edge_a->pi->iy, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, edge_a->pi->node.shape.iy, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, edge_a->dy, GCALC_COORD_BASE);
@@ -635,7 +635,7 @@ void Gcalc_scan_iterator::intersection_info::do_calc_x()
Gcalc_coord3 a_tb, b_ta;
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, edge_a->pi->ix, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, edge_a->pi->node.shape.ix, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, edge_a->dx, GCALC_COORD_BASE);
@@ -656,7 +656,7 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
inf->calc_y_exp();
gcalc_mul_coord(exp, GCALC_COORD_BASE3,
- inf->t_b, GCALC_COORD_BASE2, node->iy, GCALC_COORD_BASE);
+ inf->t_b, GCALC_COORD_BASE2, node->node.shape.iy, GCALC_COORD_BASE);
result= gcalc_cmp_coord(exp, inf->y_exp, GCALC_COORD_BASE3);
#ifdef GCALC_CHECK_WITH_FLOAT
@@ -664,18 +664,18 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
isc->calc_xy_ld(&int_x, &int_y);
if (result < 0)
{
- if (!de_check(int_y, node->y) && node->y > int_y)
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g < %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y) && node->node.shape.y > int_y)
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g < %LG", node->node.shape.y, int_y));
}
else if (result > 0)
{
- if (!de_check(int_y, node->y) && node->y < int_y)
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g > %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y) && node->node.shape.y < int_y)
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g > %LG", node->node.shape.y, int_y));
}
else
{
- if (!de_check(int_y, node->y))
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g == %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y))
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g == %LG", node->node.shape.y, int_y));
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
if (result)
@@ -684,27 +684,27 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
inf->calc_x_exp();
gcalc_mul_coord(exp, GCALC_COORD_BASE3,
- inf->t_b, GCALC_COORD_BASE2, node->ix, GCALC_COORD_BASE);
+ inf->t_b, GCALC_COORD_BASE2, node->node.shape.ix, GCALC_COORD_BASE);
result= gcalc_cmp_coord(exp, inf->x_exp, GCALC_COORD_BASE3);
#ifdef GCALC_CHECK_WITH_FLOAT
if (result < 0)
{
- if (!de_check(int_x, node->x) && node->x > int_x)
+ if (!de_check(int_x, node->node.shape.x) && node->node.shape.x > int_x)
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g < %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
else if (result > 0)
{
- if (!de_check(int_x, node->x) && node->x < int_x)
+ if (!de_check(int_x, node->node.shape.x) && node->node.shape.x < int_x)
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g > %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
else
{
- if (!de_check(int_x, node->x))
+ if (!de_check(int_x, node->node.shape.x))
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g == %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
exit:
@@ -844,13 +844,13 @@ Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y,
return NULL;
*m_hook= result;
m_hook= &result->next;
- result->x= x;
- result->y= y;
- result->shape= shape;
- result->top_node= 1;
+ result->node.shape.x= x;
+ result->node.shape.y= y;
+ result->node.shape.shape= shape;
+ result->node.shape.top_node= 1;
result->type= nt_shape_node;
- gcalc_set_double(result->ix, x, coord_extent);
- gcalc_set_double(result->iy, y, coord_extent);
+ gcalc_set_double(result->node.shape.ix, x, coord_extent);
+ gcalc_set_double(result->node.shape.iy, y, coord_extent);
m_n_points++;
return result;
@@ -864,11 +864,11 @@ static Gcalc_heap::Info *new_intersection(
if (!isc)
return 0;
isc->type= Gcalc_heap::nt_intersection;
- isc->p1= ii->edge_a->pi;
- isc->p2= ii->edge_a->next_pi;
- isc->p3= ii->edge_b->pi;
- isc->p4= ii->edge_b->next_pi;
- isc->intersection_data= ii;
+ isc->node.intersection.p1= ii->edge_a->pi;
+ isc->node.intersection.p2= ii->edge_a->next_pi;
+ isc->node.intersection.p3= ii->edge_b->pi;
+ isc->node.intersection.p4= ii->edge_b->next_pi;
+ isc->node.intersection.data= ii;
return isc;
}
@@ -881,46 +881,46 @@ static Gcalc_heap::Info *new_eq_point(
if (!eqp)
return 0;
eqp->type= Gcalc_heap::nt_eq_node;
- eqp->node= p;
- eqp->eq_data= edge;
+ eqp->node.eq.node= p;
+ eqp->node.eq.data= edge;
return eqp;
}
void Gcalc_heap::Info::calc_xy(double *x, double *y) const
{
- double b0_x= p2->x - p1->x;
- double b0_y= p2->y - p1->y;
- double b1_x= p4->x - p3->x;
- double b1_y= p4->y - p3->y;
+ double b0_x= node.intersection.p2->node.shape.x - node.intersection.p1->node.shape.x;
+ double b0_y= node.intersection.p2->node.shape.y - node.intersection.p1->node.shape.y;
+ double b1_x= node.intersection.p4->node.shape.x - node.intersection.p3->node.shape.x;
+ double b1_y= node.intersection.p4->node.shape.y - node.intersection.p3->node.shape.y;
double b0xb1= b0_x * b1_y - b0_y * b1_x;
- double t= (p3->x - p1->x) * b1_y - (p3->y - p1->y) * b1_x;
+ double t= (node.intersection.p3->node.shape.x - node.intersection.p1->node.shape.x) * b1_y - (node.intersection.p3->node.shape.y - node.intersection.p1->node.shape.y) * b1_x;
t/= b0xb1;
- *x= p1->x + b0_x * t;
- *y= p1->y + b0_y * t;
+ *x= node.intersection.p1->node.shape.x + b0_x * t;
+ *y= node.intersection.p1->node.shape.y + b0_y * t;
}
#ifdef GCALC_CHECK_WITH_FLOAT
void Gcalc_heap::Info::calc_xy_ld(long double *x, long double *y) const
{
- long double b0_x= ((long double) p2->x) - p1->x;
- long double b0_y= ((long double) p2->y) - p1->y;
- long double b1_x= ((long double) p4->x) - p3->x;
- long double b1_y= ((long double) p4->y) - p3->y;
+ long double b0_x= ((long double) p2->node.shape.x) - p1->node.shape.x;
+ long double b0_y= ((long double) p2->node.shape.y) - p1->node.shape.y;
+ long double b1_x= ((long double) p4->node.shape.x) - p3->node.shape.x;
+ long double b1_y= ((long double) p4->node.shape.y) - p3->node.shape.y;
long double b0xb1= b0_x * b1_y - b0_y * b1_x;
- long double ax= ((long double) p3->x) - p1->x;
- long double ay= ((long double) p3->y) - p1->y;
+ long double ax= ((long double) p3->node.shape.x) - p1->node.shape.x;
+ long double ay= ((long double) p3->node.shape.y) - p1->node.shape.y;
long double t_a= ax * b1_y - ay * b1_x;
- long double hx= (b0xb1 * (long double) p1->x + b0_x * t_a);
- long double hy= (b0xb1 * (long double) p1->y + b0_y * t_a);
+ long double hx= (b0xb1 * (long double) p1->node.shape.x + b0_x * t_a);
+ long double hy= (b0xb1 * (long double) p1->node.shape.y + b0_y * t_a);
if (fabs(b0xb1) < 1e-15)
{
- *x= p1->x;
- *y= p1->y;
+ *x= p1->node.shape.x;
+ *y= p1->node.shape.y;
return;
}
@@ -933,10 +933,10 @@ void Gcalc_heap::Info::calc_xy_ld(long double *x, long double *y) const
static int cmp_point_info(const Gcalc_heap::Info *i0,
const Gcalc_heap::Info *i1)
{
- int cmp_y= gcalc_cmp_coord1(i0->iy, i1->iy);
+ int cmp_y= gcalc_cmp_coord1(i0->node.shape.iy, i1->node.shape.iy);
if (cmp_y)
return cmp_y;
- return gcalc_cmp_coord1(i0->ix, i1->ix);
+ return gcalc_cmp_coord1(i0->node.shape.ix, i1->node.shape.ix);
}
@@ -944,11 +944,11 @@ static inline void trim_node(Gcalc_heap::Info *node, Gcalc_heap::Info *prev_node
{
if (!node)
return;
- node->top_node= 0;
- GCALC_DBUG_ASSERT((node->left == prev_node) || (node->right == prev_node));
- if (node->left == prev_node)
- node->left= node->right;
- node->right= NULL;
+ node->node.shape.top_node= 0;
+ GCALC_DBUG_ASSERT((node->node.shape.left == prev_node) || (node->node.shape.right == prev_node));
+ if (node->node.shape.left == prev_node)
+ node->node.shape.left= node->node.shape.right;
+ node->node.shape.right= NULL;
GCALC_DBUG_ASSERT(cmp_point_info(node, prev_node));
}
@@ -972,8 +972,8 @@ void Gcalc_heap::prepare_operation()
/* TODO - move this to the 'normal_scan' loop */
for (cur= get_first(); cur; cur= cur->get_next())
{
- trim_node(cur->left, cur);
- trim_node(cur->right, cur);
+ trim_node(cur->node.shape.left, cur);
+ trim_node(cur->node.shape.right, cur);
}
}
@@ -995,7 +995,7 @@ int Gcalc_shape_transporter::int_single_point(gcalc_shape_info Info,
Gcalc_heap::Info *point= m_heap->new_point_info(x, y, Info);
if (!point)
return 1;
- point->left= point->right= 0;
+ point->node.shape.left= point->node.shape.right= 0;
return 0;
}
@@ -1018,9 +1018,9 @@ int Gcalc_shape_transporter::int_add_point(gcalc_shape_info Info,
m_heap->free_point_info(point, hook);
return 0;
}
- GCALC_DBUG_ASSERT(!m_prev || m_prev->x != x || m_prev->y != y);
- m_prev->left= point;
- point->right= m_prev;
+ GCALC_DBUG_ASSERT(!m_prev || m_prev->node.shape.x != x || m_prev->node.shape.y != y);
+ m_prev->node.shape.left= point;
+ point->node.shape.right= m_prev;
}
else
m_first= point;
@@ -1040,16 +1040,16 @@ void Gcalc_shape_transporter::int_complete()
/* simple point */
if (m_first == m_prev)
{
- m_first->right= m_first->left= NULL;
+ m_first->node.shape.right= m_first->node.shape.left= NULL;
return;
}
/* line */
if (m_shape_started == 1)
{
- m_first->right= NULL;
- m_prev->left= m_prev->right;
- m_prev->right= NULL;
+ m_first->node.shape.right= NULL;
+ m_prev->node.shape.left= m_prev->node.shape.right;
+ m_prev->node.shape.right= NULL;
return;
}
@@ -1057,32 +1057,32 @@ void Gcalc_shape_transporter::int_complete()
if (cmp_point_info(m_first, m_prev) == 0)
{
/* Coinciding points, remove the last one from the list */
- m_prev->right->left= m_first;
- m_first->right= m_prev->right;
+ m_prev->node.shape.right->node.shape.left= m_first;
+ m_first->node.shape.right= m_prev->node.shape.right;
m_heap->free_point_info(m_prev, m_prev_hook);
}
else
{
- GCALC_DBUG_ASSERT(m_prev->x != m_first->x || m_prev->y != m_first->y);
- m_first->right= m_prev;
- m_prev->left= m_first;
+ GCALC_DBUG_ASSERT(m_prev->node.shape.x != m_first->node.shape.x || m_prev->node.shape.y != m_first->node.shape.y);
+ m_first->node.shape.right= m_prev;
+ m_prev->node.shape.left= m_first;
}
}
inline void calc_dx_dy(Gcalc_scan_iterator::point *p)
{
- gcalc_sub_coord1(p->dx, p->next_pi->ix, p->pi->ix);
- gcalc_sub_coord1(p->dy, p->next_pi->iy, p->pi->iy);
+ gcalc_sub_coord1(p->dx, p->next_pi->node.shape.ix, p->pi->node.shape.ix);
+ gcalc_sub_coord1(p->dy, p->next_pi->node.shape.iy, p->pi->node.shape.iy);
if (GCALC_SIGN(p->dx[0]))
{
- p->l_border= &p->next_pi->ix;
- p->r_border= &p->pi->ix;
+ p->l_border= &p->next_pi->node.shape.ix;
+ p->r_border= &p->pi->node.shape.ix;
}
else
{
- p->r_border= &p->next_pi->ix;
- p->l_border= &p->pi->ix;
+ p->r_border= &p->next_pi->node.shape.ix;
+ p->l_border= &p->pi->node.shape.ix;
}
}
@@ -1143,10 +1143,10 @@ int Gcalc_scan_iterator::point::cmp_dx_dy(const Gcalc_heap::Info *p1,
const Gcalc_heap::Info *p4)
{
Gcalc_coord1 dx_a, dy_a, dx_b, dy_b;
- gcalc_sub_coord1(dx_a, p2->ix, p1->ix);
- gcalc_sub_coord1(dy_a, p2->iy, p1->iy);
- gcalc_sub_coord1(dx_b, p4->ix, p3->ix);
- gcalc_sub_coord1(dy_b, p4->iy, p3->iy);
+ gcalc_sub_coord1(dx_a, p2->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(dy_a, p2->node.shape.iy, p1->node.shape.iy);
+ gcalc_sub_coord1(dx_b, p4->node.shape.ix, p3->node.shape.ix);
+ gcalc_sub_coord1(dy_b, p4->node.shape.iy, p3->node.shape.iy);
return cmp_dx_dy(dx_a, dy_a, dx_b, dy_b);
}
@@ -1168,8 +1168,8 @@ void Gcalc_scan_iterator::point::calc_x(long double *x, long double y,
*x= ix;
}
else
- *x= (ddy * (long double) pi->x + gcalc_get_double(dx, GCALC_COORD_BASE) *
- (y - pi->y)) / ddy;
+ *x= (ddy * (long double) pi->node.shape.x + gcalc_get_double(dx, GCALC_COORD_BASE) *
+ (y - pi->node.shape.y)) / ddy;
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
@@ -1280,7 +1280,7 @@ int Gcalc_scan_iterator::arrange_event(int do_sorting, int n_intersections)
int Gcalc_heap::Info::equal_pi(const Info *pi) const
{
if (type == nt_intersection)
- return equal_intersection;
+ return node.intersection.equal;
if (pi->type == nt_eq_node)
return 1;
if (type == nt_eq_node || pi->type == nt_intersection)
@@ -1322,7 +1322,7 @@ int Gcalc_scan_iterator::step()
#ifndef GCALC_DBUG_OFF
if (m_cur_pi->type == Gcalc_heap::nt_intersection &&
m_cur_pi->get_next()->type == Gcalc_heap::nt_intersection &&
- m_cur_pi->equal_intersection)
+ m_cur_pi->node.intersection.equal)
GCALC_DBUG_ASSERT(cmp_intersections(m_cur_pi, m_cur_pi->get_next()) == 0);
#endif /*GCALC_DBUG_OFF*/
GCALC_DBUG_CHECK_COUNTER();
@@ -1377,23 +1377,23 @@ static int node_on_right(const Gcalc_heap::Info *node,
Gcalc_coord2 ax_by, ay_bx;
int result;
- gcalc_sub_coord1(a_x, node->ix, edge_a->ix);
- gcalc_sub_coord1(a_y, node->iy, edge_a->iy);
- gcalc_sub_coord1(b_x, edge_b->ix, edge_a->ix);
- gcalc_sub_coord1(b_y, edge_b->iy, edge_a->iy);
+ gcalc_sub_coord1(a_x, node->node.shape.ix, edge_a->node.shape.ix);
+ gcalc_sub_coord1(a_y, node->node.shape.iy, edge_a->node.shape.iy);
+ gcalc_sub_coord1(b_x, edge_b->node.shape.ix, edge_a->node.shape.ix);
+ gcalc_sub_coord1(b_y, edge_b->node.shape.iy, edge_a->node.shape.iy);
gcalc_mul_coord1(ax_by, a_x, b_y);
gcalc_mul_coord1(ay_bx, a_y, b_x);
result= gcalc_cmp_coord(ax_by, ay_bx, GCALC_COORD_BASE2);
#ifdef GCALC_CHECK_WITH_FLOAT
{
- long double dx= gcalc_get_double(edge_b->ix, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->ix, GCALC_COORD_BASE);
- long double dy= gcalc_get_double(edge_b->iy, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->iy, GCALC_COORD_BASE);
- long double ax= gcalc_get_double(node->ix, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->ix, GCALC_COORD_BASE);
- long double ay= gcalc_get_double(node->iy, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->iy, GCALC_COORD_BASE);
+ long double dx= gcalc_get_double(edge_b->node.shape.ix, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.ix, GCALC_COORD_BASE);
+ long double dy= gcalc_get_double(edge_b->node.shape.iy, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.iy, GCALC_COORD_BASE);
+ long double ax= gcalc_get_double(node->node.shape.ix, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.ix, GCALC_COORD_BASE);
+ long double ay= gcalc_get_double(node->node.shape.iy, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.iy, GCALC_COORD_BASE);
long double d= ax * dy - ay * dx;
if (result == 0)
GCALC_DBUG_ASSERT(de_check(d, 0.0));
@@ -1412,8 +1412,8 @@ static int cmp_tops(const Gcalc_heap::Info *top_node,
{
int cmp_res_a, cmp_res_b;
- cmp_res_a= gcalc_cmp_coord1(edge_a->ix, top_node->ix);
- cmp_res_b= gcalc_cmp_coord1(edge_b->ix, top_node->ix);
+ cmp_res_a= gcalc_cmp_coord1(edge_a->node.shape.ix, top_node->node.shape.ix);
+ cmp_res_b= gcalc_cmp_coord1(edge_b->node.shape.ix, top_node->node.shape.ix);
if (cmp_res_a <= 0 && cmp_res_b > 0)
return -1;
@@ -1438,26 +1438,26 @@ int Gcalc_scan_iterator::insert_top_node()
if (!sp0)
GCALC_DBUG_RETURN(1);
sp0->pi= m_cur_pi;
- sp0->next_pi= m_cur_pi->left;
+ sp0->next_pi= m_cur_pi->node.shape.left;
#ifndef GCALC_DBUG_OFF
sp0->thread= m_cur_thread++;
#endif /*GCALC_DBUG_OFF*/
- if (m_cur_pi->left)
+ if (m_cur_pi->node.shape.left)
{
calc_dx_dy(sp0);
- if (m_cur_pi->right)
+ if (m_cur_pi->node.shape.right)
{
if (!(sp1= new_slice_point()))
GCALC_DBUG_RETURN(1);
sp1->event= sp0->event= scev_two_threads;
sp1->pi= m_cur_pi;
- sp1->next_pi= m_cur_pi->right;
+ sp1->next_pi= m_cur_pi->node.shape.right;
#ifndef GCALC_DBUG_OFF
sp1->thread= m_cur_thread++;
#endif /*GCALC_DBUG_OFF*/
calc_dx_dy(sp1);
/* We have two threads so should decide which one will be first */
- cmp_res= cmp_tops(m_cur_pi, m_cur_pi->left, m_cur_pi->right);
+ cmp_res= cmp_tops(m_cur_pi, m_cur_pi->node.shape.left, m_cur_pi->node.shape.right);
if (cmp_res > 0)
{
point *tmp= sp0;
@@ -1467,7 +1467,7 @@ int Gcalc_scan_iterator::insert_top_node()
else if (cmp_res == 0)
{
/* Exactly same direction of the edges. */
- cmp_res= gcalc_cmp_coord1(m_cur_pi->left->iy, m_cur_pi->right->iy);
+ cmp_res= gcalc_cmp_coord1(m_cur_pi->node.shape.left->node.shape.iy, m_cur_pi->node.shape.right->node.shape.iy);
if (cmp_res != 0)
{
if (cmp_res < 0)
@@ -1483,7 +1483,7 @@ int Gcalc_scan_iterator::insert_top_node()
}
else
{
- cmp_res= gcalc_cmp_coord1(m_cur_pi->left->ix, m_cur_pi->right->ix);
+ cmp_res= gcalc_cmp_coord1(m_cur_pi->node.shape.left->node.shape.ix, m_cur_pi->node.shape.right->node.shape.ix);
if (cmp_res != 0)
{
if (cmp_res < 0)
@@ -1517,7 +1517,7 @@ int Gcalc_scan_iterator::insert_top_node()
/* We need to find the place to insert. */
for (; sp; prev_hook= sp->next_ptr(), sp=sp->get_next())
{
- if (sp->event || gcalc_cmp_coord1(*sp->r_border, m_cur_pi->ix) < 0)
+ if (sp->event || gcalc_cmp_coord1(*sp->r_border, m_cur_pi->node.shape.ix) < 0)
continue;
cmp_res= node_on_right(m_cur_pi, sp->pi, sp->next_pi);
if (cmp_res == 0)
@@ -1743,7 +1743,7 @@ int Gcalc_scan_iterator::node_scan()
GCALC_DBUG_PRINT(("node for %d", sp->thread));
/* Handle the point itself. */
sp->pi= cur_pi;
- sp->next_pi= cur_pi->left;
+ sp->next_pi= cur_pi->node.shape.left;
sp->event= scev_point;
calc_dx_dy(sp);
@@ -1794,7 +1794,7 @@ void Gcalc_scan_iterator::intersection_scan()
ii->edge_a->event= ii->edge_b->event= scev_intersection;
ii->edge_a->ev_pi= ii->edge_b->ev_pi= m_cur_pi;
free_item(ii);
- m_cur_pi->intersection_data= NULL;
+ m_cur_pi->node.intersection.data= NULL;
GCALC_DBUG_VOID_RETURN;
}
@@ -1813,7 +1813,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
!(ii= new_intersection(m_heap, i_calc)))
GCALC_DBUG_RETURN(1);
- ii->equal_intersection= 0;
+ ii->node.intersection.equal= 0;
for (;
pi_from->get_next() != sp_a->next_pi &&
@@ -1824,7 +1824,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
if (skip_next)
{
if (cur->type == Gcalc_heap::nt_intersection)
- skip_next= cur->equal_intersection;
+ skip_next= cur->node.intersection.equal;
else
skip_next= 0;
continue;
@@ -1832,7 +1832,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
if (cur->type == Gcalc_heap::nt_intersection)
{
cmp_res= cmp_intersections(cur, ii);
- skip_next= cur->equal_intersection;
+ skip_next= cur->node.intersection.equal;
}
else if (cur->type == Gcalc_heap::nt_eq_node)
continue;
@@ -1840,7 +1840,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
cmp_res= cmp_node_isc(cur, ii);
if (cmp_res == 0)
{
- ii->equal_intersection= 1;
+ ii->node.intersection.equal= 1;
break;
}
else if (cmp_res > 0)
@@ -1881,13 +1881,13 @@ void calc_t(Gcalc_coord2 t_a, Gcalc_coord2 t_b,
Gcalc_coord2 x1y2, x2y1;
Gcalc_coord1 dya, dyb;
- gcalc_sub_coord1(a2_a1x, p3->ix, p1->ix);
- gcalc_sub_coord1(a2_a1y, p3->iy, p1->iy);
+ gcalc_sub_coord1(a2_a1x, p3->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(a2_a1y, p3->node.shape.iy, p1->node.shape.iy);
- gcalc_sub_coord1(dxa, p2->ix, p1->ix);
- gcalc_sub_coord1(dya, p2->iy, p1->iy);
- gcalc_sub_coord1(dxb, p4->ix, p3->ix);
- gcalc_sub_coord1(dyb, p4->iy, p3->iy);
+ gcalc_sub_coord1(dxa, p2->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(dya, p2->node.shape.iy, p1->node.shape.iy);
+ gcalc_sub_coord1(dxb, p4->node.shape.ix, p3->node.shape.ix);
+ gcalc_sub_coord1(dyb, p4->node.shape.iy, p3->node.shape.iy);
gcalc_mul_coord1(x1y2, dxa, dyb);
gcalc_mul_coord1(x2y1, dya, dxb);
@@ -1908,11 +1908,11 @@ double Gcalc_scan_iterator::get_y() const
Gcalc_coord2 t_a, t_b;
Gcalc_coord3 a_tb, b_ta, y_exp;
calc_t(t_a, t_b, dxa, dya,
- state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4);
+ state.pi->node.intersection.p1, state.pi->node.intersection.p2, state.pi->node.intersection.p3, state.pi->node.intersection.p4);
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, state.pi->p1->iy, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, state.pi->node.intersection.p1->node.shape.iy, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, dya, GCALC_COORD_BASE);
@@ -1922,7 +1922,7 @@ double Gcalc_scan_iterator::get_y() const
get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent;
}
else
- return state.pi->y;
+ return state.pi->node.shape.y;
}
@@ -1934,11 +1934,11 @@ double Gcalc_scan_iterator::get_event_x() const
Gcalc_coord2 t_a, t_b;
Gcalc_coord3 a_tb, b_ta, x_exp;
calc_t(t_a, t_b, dxa, dya,
- state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4);
+ state.pi->node.intersection.p1, state.pi->node.intersection.p2, state.pi->node.intersection.p3, state.pi->node.intersection.p4);
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, state.pi->p1->ix, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, state.pi->node.intersection.p1->node.shape.ix, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, dxa, GCALC_COORD_BASE);
@@ -1948,7 +1948,7 @@ double Gcalc_scan_iterator::get_event_x() const
get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent;
}
else
- return state.pi->x;
+ return state.pi->node.shape.x;
}
double Gcalc_scan_iterator::get_h() const
@@ -1961,7 +1961,7 @@ double Gcalc_scan_iterator::get_h() const
state.pi->calc_xy(&x, &next_y);
}
else
- next_y= state.pi->next ? state.pi->get_next()->y : 0.0;
+ next_y= state.pi->next ? state.pi->get_next()->node.shape.y : 0.0;
return next_y - cur_y;
}
@@ -1970,11 +1970,11 @@ double Gcalc_scan_iterator::get_sp_x(const point *sp) const
{
double dy;
if (sp->event & (scev_end | scev_two_ends | scev_point))
- return sp->pi->x;
- dy= sp->next_pi->y - sp->pi->y;
+ return sp->pi->node.shape.x;
+ dy= sp->next_pi->node.shape.y - sp->pi->node.shape.y;
if (fabs(dy) < 1e-12)
- return sp->pi->x;
- return sp->pi->x + (sp->next_pi->x - sp->pi->x) * dy;
+ return sp->pi->node.shape.x;
+ return sp->pi->node.shape.x + (sp->next_pi->node.shape.x - sp->pi->node.shape.x) * dy;
}
diff --git a/sql/gcalc_slicescan.h b/sql/gcalc_slicescan.h
index 55de497f1ee..4996287ca88 100644
--- a/sql/gcalc_slicescan.h
+++ b/sql/gcalc_slicescan.h
@@ -26,7 +26,7 @@
#ifndef GCALC_DBUG_OFF
#define GCALC_DBUG_PRINT(b) DBUG_PRINT("Gcalc", b)
-#define GCALC_DBUG_ENTER(a) DBUG_ENTER("Gcalc "a)
+#define GCALC_DBUG_ENTER(a) DBUG_ENTER("Gcalc " a)
#define GCALC_DBUG_RETURN(r) DBUG_RETURN(r)
#define GCALC_DBUG_VOID_RETURN DBUG_VOID_RETURN
#define GCALC_DBUG_ASSERT(r) DBUG_ASSERT(r)
@@ -188,7 +188,7 @@ public:
double x,y;
Gcalc_coord1 ix, iy;
int top_node;
- };
+ } shape;
struct
{
/* nt_intersection */
@@ -197,21 +197,21 @@ public:
const Info *p2;
const Info *p3;
const Info *p4;
- void *intersection_data;
- int equal_intersection;
- };
+ void *data;
+ int equal;
+ } intersection;
struct
{
/* nt_eq_node */
const Info *node;
- void *eq_data;
- };
- };
+ void *data;
+ } eq;
+ } node;
bool is_bottom() const
- { GCALC_DBUG_ASSERT(type == nt_shape_node); return !left; }
+ { GCALC_DBUG_ASSERT(type == nt_shape_node); return !node.shape.left; }
bool is_top() const
- { GCALC_DBUG_ASSERT(type == nt_shape_node); return top_node; }
+ { GCALC_DBUG_ASSERT(type == nt_shape_node); return node.shape.top_node; }
bool is_single_node() const
{ return is_bottom() && is_top(); }
@@ -383,7 +383,7 @@ public:
inline const point *c_get_next() const
{ return (const point *)next; }
inline bool is_bottom() const { return !next_pi; }
- gcalc_shape_info get_shape() const { return pi->shape; }
+ gcalc_shape_info get_shape() const { return pi->node.shape.shape; }
inline point *get_next() { return (point *)next; }
inline const point *get_next() const { return (const point *)next; }
/* Compare the dx_dy parameters regarding the horiz_dir */
diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc
index 5aac8a71249..da0252c6b67 100644
--- a/sql/gcalc_tools.cc
+++ b/sql/gcalc_tools.cc
@@ -1278,7 +1278,7 @@ inline int Gcalc_operation_reducer::get_single_result(res_point *res,
GCALC_DBUG_RETURN(1);
}
else
- if (storage->single_point(res->pi->x, res->pi->y))
+ if (storage->single_point(res->pi->node.shape.x, res->pi->node.shape.y))
GCALC_DBUG_RETURN(1);
free_result(res);
GCALC_DBUG_RETURN(0);
@@ -1304,8 +1304,8 @@ int Gcalc_operation_reducer::get_result_thread(res_point *cur,
}
else
{
- x= cur->pi->x;
- y= cur->pi->y;
+ x= cur->pi->node.shape.x;
+ y= cur->pi->node.shape.y;
}
if (storage->add_point(x, y))
GCALC_DBUG_RETURN(1);
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 5d789fa31e3..173a5f709c1 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -8306,7 +8306,7 @@ bool ha_partition::inplace_alter_table(TABLE *altered_table,
/*
Note that this function will try rollback failed ADD INDEX by
executing DROP INDEX for the indexes that were committed (if any)
- before the error occured. This means that the underlying storage
+ before the error occurred. This means that the underlying storage
engine must be able to drop index in-place with X-lock held.
(As X-lock will be held here if new indexes are to be committed)
*/
diff --git a/sql/handler.cc b/sql/handler.cc
index bd415122388..748a51f5c59 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -30,11 +30,10 @@
#include "sql_table.h" // build_table_filename
#include "sql_parse.h" // check_stack_overrun
#include "sql_acl.h" // SUPER_ACL
-#include "sql_base.h" // free_io_cache
+#include "sql_base.h" // TDC_element
#include "discover.h" // extension_based_table_discovery, etc
#include "log_event.h" // *_rows_log_event
#include "create_options.h"
-#include "rpl_filter.h"
#include <myisampack.h>
#include "transaction.h"
#include "myisam.h"
@@ -325,7 +324,7 @@ int ha_init_errors(void)
/* Set the dedicated error messages. */
SETMSG(HA_ERR_KEY_NOT_FOUND, ER_DEFAULT(ER_KEY_NOT_FOUND));
SETMSG(HA_ERR_FOUND_DUPP_KEY, ER_DEFAULT(ER_DUP_KEY));
- SETMSG(HA_ERR_RECORD_CHANGED, "Update wich is recoverable");
+ SETMSG(HA_ERR_RECORD_CHANGED, "Update which is recoverable");
SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function");
SETMSG(HA_ERR_CRASHED, ER_DEFAULT(ER_NOT_KEYFILE));
SETMSG(HA_ERR_WRONG_IN_RECORD, ER_DEFAULT(ER_CRASHED_ON_USAGE));
@@ -3931,9 +3930,7 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
if it is started.
*/
-inline
-void
-handler::mark_trx_read_write()
+void handler::mark_trx_read_write_internal()
{
Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0];
/*
@@ -4227,7 +4224,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
if (table->file->check_if_incompatible_data(create_info, table_changes)
== COMPATIBLE_DATA_YES)
- DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+ DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
}
@@ -5050,7 +5047,9 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name,
Table_exists_error_handler no_such_table_handler;
thd->push_internal_handler(&no_such_table_handler);
- TABLE_SHARE *share= tdc_acquire_share(thd, db, table_name, flags);
+ table.init_one_table(db, strlen(db), table_name, strlen(table_name),
+ table_name, TL_READ);
+ TABLE_SHARE *share= tdc_acquire_share(thd, &table, flags);
thd->pop_internal_handler();
if (hton && share)
@@ -5579,30 +5578,47 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
correct for the table.
A row in the given table should be replicated if:
+ - It's not called by partition engine
- Row-based replication is enabled in the current thread
- The binlog is enabled
- It is not a temporary table
- The binary log is open
- The database the table resides in shall be binlogged (binlog_*_db rules)
- table is not mysql.event
+
+ RETURN VALUE
+ 0 No binary logging in row format
+ 1 Row needs to be logged
*/
-static bool check_table_binlog_row_based(THD *thd, TABLE *table)
+inline bool handler::check_table_binlog_row_based(bool binlog_row)
{
- if (table->s->cached_row_logging_check == -1)
+ if (unlikely((table->in_use->variables.sql_log_bin_off)))
+ return 0; /* Called by partitioning engine */
+ if (unlikely((!check_table_binlog_row_based_done)))
{
- int const check(table->s->tmp_table == NO_TMP_TABLE &&
- ! table->no_replicate &&
- binlog_filter->db_ok(table->s->db.str));
- table->s->cached_row_logging_check= check;
+ check_table_binlog_row_based_done= 1;
+ check_table_binlog_row_based_result=
+ check_table_binlog_row_based_internal(binlog_row);
}
+ return check_table_binlog_row_based_result;
+}
- DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
- table->s->cached_row_logging_check == 1);
+bool handler::check_table_binlog_row_based_internal(bool binlog_row)
+{
+ THD *thd= table->in_use;
- return (thd->is_current_stmt_binlog_format_row() &&
- table->s->cached_row_logging_check &&
#ifdef WITH_WSREP
+ /* only InnoDB tables will be replicated through binlog emulation */
+ if (binlog_row &&
+ ((WSREP_EMULATE_BINLOG(thd) &&
+ table->file->partition_ht()->db_type != DB_TYPE_INNODB) ||
+ (thd->wsrep_ignore_table == true)))
+ return 0;
+#endif
+
+ return (table->s->cached_row_logging_check &&
+ thd->is_current_stmt_binlog_format_row() &&
/*
Wsrep partially enables binary logging if it have not been
explicitly turned on. As a result we return 'true' if we are in
@@ -5617,14 +5633,13 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
Otherwise, return 'true' if binary logging is on.
*/
- (thd->variables.sql_log_bin_off != 1) &&
- ((WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV)) ||
- ((WSREP(thd) || (thd->variables.option_bits & OPTION_BIN_LOG)) &&
- mysql_bin_log.is_open())));
-#else
- (thd->variables.option_bits & OPTION_BIN_LOG) &&
- mysql_bin_log.is_open());
-#endif
+ IF_WSREP(((WSREP_EMULATE_BINLOG(thd) &&
+ (thd->wsrep_exec_mode != REPL_RECV)) ||
+ ((WSREP(thd) ||
+ (thd->variables.option_bits & OPTION_BIN_LOG)) &&
+ mysql_bin_log.is_open())),
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
+ mysql_bin_log.is_open()));
}
@@ -5658,54 +5673,51 @@ static int write_locked_table_maps(THD *thd)
DBUG_PRINT("debug", ("get_binlog_table_maps(): %d", thd->get_binlog_table_maps()));
- if (thd->get_binlog_table_maps() == 0)
+ MYSQL_LOCK *locks[2];
+ locks[0]= thd->extra_lock;
+ locks[1]= thd->lock;
+ my_bool with_annotate= thd->variables.binlog_annotate_row_events &&
+ thd->query() && thd->query_length();
+
+ for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
{
- MYSQL_LOCK *locks[2];
- locks[0]= thd->extra_lock;
- locks[1]= thd->lock;
- my_bool with_annotate= thd->variables.binlog_annotate_row_events &&
- thd->query() && thd->query_length();
+ MYSQL_LOCK const *const lock= locks[i];
+ if (lock == NULL)
+ continue;
- for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
+ TABLE **const end_ptr= lock->table + lock->table_count;
+ for (TABLE **table_ptr= lock->table ;
+ table_ptr != end_ptr ;
+ ++table_ptr)
{
- MYSQL_LOCK const *const lock= locks[i];
- if (lock == NULL)
- continue;
-
- TABLE **const end_ptr= lock->table + lock->table_count;
- for (TABLE **table_ptr= lock->table ;
- table_ptr != end_ptr ;
- ++table_ptr)
+ TABLE *const table= *table_ptr;
+ DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
+ if (table->current_lock == F_WRLCK &&
+ table->file->check_table_binlog_row_based(0))
{
- TABLE *const table= *table_ptr;
- DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
- if (table->current_lock == F_WRLCK &&
- check_table_binlog_row_based(thd, table))
- {
- /*
- We need to have a transactional behavior for SQLCOM_CREATE_TABLE
- (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
- compatible behavior with the STMT based replication even when
- the table is not transactional. In other words, if the operation
- fails while executing the insert phase nothing is written to the
- binlog.
-
- Note that at this point, we check the type of a set of tables to
- create the table map events. In the function binlog_log_row(),
- which calls the current function, we check the type of the table
- of the current row.
- */
- bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- table->file->has_transactions();
- int const error= thd->binlog_write_table_map(table, has_trans,
- &with_annotate);
- /*
- If an error occurs, it is the responsibility of the caller to
- roll back the transaction.
- */
- if (unlikely(error))
- DBUG_RETURN(1);
- }
+ /*
+ We need to have a transactional behavior for SQLCOM_CREATE_TABLE
+ (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
+ compatible behavior with the STMT based replication even when
+ the table is not transactional. In other words, if the operation
+ fails while executing the insert phase nothing is written to the
+ binlog.
+
+ Note that at this point, we check the type of a set of tables to
+ create the table map events. In the function binlog_log_row(),
+ which calls the current function, we check the type of the table
+ of the current row.
+ */
+ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ table->file->has_transactions();
+ int const error= thd->binlog_write_table_map(table, has_trans,
+ &with_annotate);
+ /*
+ If an error occurs, it is the responsibility of the caller to
+ roll back the transaction.
+ */
+ if (unlikely(error))
+ DBUG_RETURN(1);
}
}
}
@@ -5715,44 +5727,50 @@ static int write_locked_table_maps(THD *thd)
typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*);
-static int binlog_log_row(TABLE* table,
- const uchar *before_record,
- const uchar *after_record,
- Log_func *log_func)
+
+
+static int binlog_log_row_internal(TABLE* table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func)
{
bool error= 0;
THD *const thd= table->in_use;
- /* only InnoDB tables will be replicated through binlog emulation */
- if (WSREP_EMULATE_BINLOG(thd) &&
- table->file->partition_ht()->db_type != DB_TYPE_INNODB)
- return 0;
-
- if (check_table_binlog_row_based(thd, table))
+ /*
+ If there are no table maps written to the binary log, this is
+ the first row handled in this statement. In that case, we need
+ to write table maps for all locked tables to the binary log.
+ */
+ if (likely(!(error= ((thd->get_binlog_table_maps() == 0 &&
+ write_locked_table_maps(thd))))))
{
/*
- If there are no table maps written to the binary log, this is
- the first row handled in this statement. In that case, we need
- to write table maps for all locked tables to the binary log.
+ We need to have a transactional behavior for SQLCOM_CREATE_TABLE
+ (i.e. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
+ compatible behavior with the STMT based replication even when
+ the table is not transactional. In other words, if the operation
+ fails while executing the insert phase nothing is written to the
+ binlog.
*/
- if (likely(!(error= write_locked_table_maps(thd))))
- {
- /*
- We need to have a transactional behavior for SQLCOM_CREATE_TABLE
- (i.e. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
- compatible behavior with the STMT based replication even when
- the table is not transactional. In other words, if the operation
- fails while executing the insert phase nothing is written to the
- binlog.
- */
- bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- table->file->has_transactions();
- error= (*log_func)(thd, table, has_trans, before_record, after_record);
- }
+ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ table->file->has_transactions();
+ error= (*log_func)(thd, table, has_trans, before_record, after_record);
}
return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
}
+static inline int binlog_log_row(TABLE* table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func)
+{
+ if (!table->file->check_table_binlog_row_based(1))
+ return 0;
+ return binlog_log_row_internal(table, before_record, after_record, log_func);
+}
+
+
int handler::ha_external_lock(THD *thd, int lock_type)
{
int error;
@@ -5845,12 +5863,12 @@ int handler::ha_reset()
DBUG_ASSERT(table->key_read == 0);
/* ensure that ha_index_end / ha_rnd_end has been called */
DBUG_ASSERT(inited == NONE);
- /* Free cache used by filesort */
- free_io_cache(table);
/* reset the bitmaps to point to defaults */
table->default_column_bitmaps();
pushed_cond= NULL;
tracker= NULL;
+ mark_trx_read_write_done= check_table_binlog_row_based_done=
+ check_table_binlog_row_based_result= 0;
/* Reset information about pushed engine conditions */
cancel_pushed_idx_cond();
/* Reset information about pushed index conditions */
@@ -5875,14 +5893,13 @@ int handler::ha_write_row(uchar *buf)
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
- if (unlikely(error))
- DBUG_RETURN(error);
- rows_changed++;
- if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
- DBUG_RETURN(error); /* purecov: inspected */
-
+ if (likely(!error))
+ {
+ rows_changed++;
+ error= binlog_log_row(table, 0, buf, log_func);
+ }
DEBUG_SYNC_C("ha_write_row_end");
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
@@ -5908,12 +5925,12 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
- if (unlikely(error))
- return error;
- rows_changed++;
- if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
- return error;
- return 0;
+ if (likely(!error))
+ {
+ rows_changed++;
+ error= binlog_log_row(table, old_data, new_data, log_func);
+ }
+ return error;
}
int handler::ha_delete_row(const uchar *buf)
@@ -5935,12 +5952,12 @@ int handler::ha_delete_row(const uchar *buf)
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
{ error= delete_row(buf);})
MYSQL_DELETE_ROW_DONE(error);
- if (unlikely(error))
- return error;
- rows_changed++;
- if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
- return error;
- return 0;
+ if (likely(!error))
+ {
+ rows_changed++;
+ error= binlog_log_row(table, buf, 0, log_func);
+ }
+ return error;
}
diff --git a/sql/handler.h b/sql/handler.h
index e0b5d922936..6054ec2db35 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1302,7 +1302,7 @@ struct handlerton
};
/*
- By default (if not implemented by the engine, but the discovery_table() is
+ By default (if not implemented by the engine, but the discover_table() is
implemented) it will perform a file-based discovery:
- if tablefile_extensions[0] is not null, this will discovers all tables
@@ -2581,11 +2581,6 @@ public:
RANGE_SEQ_IF mrr_funcs; /* Range sequence traversal functions */
HANDLER_BUFFER *multi_range_buffer; /* MRR buffer info */
uint ranges_in_seq; /* Total number of ranges in the traversed sequence */
- /* TRUE <=> source MRR ranges and the output are ordered */
- bool mrr_is_output_sorted;
-
- /** TRUE <=> we're currently traversing a range in mrr_cur_range. */
- bool mrr_have_range;
/** Current range (the one we're now returning rows from) */
KEY_MULTI_RANGE mrr_cur_range;
@@ -2593,23 +2588,32 @@ public:
key_range save_end_range, *end_range;
KEY_PART_INFO *range_key_part;
int key_compare_result_on_equal;
- bool eq_range;
- bool internal_tmp_table; /* If internal tmp table */
- uint errkey; /* Last dup key */
- uint key_used_on_scan;
- uint active_index;
+ /* TRUE <=> source MRR ranges and the output are ordered */
+ bool mrr_is_output_sorted;
+ /** TRUE <=> we're currently traversing a range in mrr_cur_range. */
+ bool mrr_have_range;
+ bool eq_range;
+ bool internal_tmp_table; /* If internal tmp table */
+ bool implicit_emptied; /* Can be !=0 only if HEAP */
+ bool mark_trx_read_write_done; /* mark_trx_read_write was called */
+ bool check_table_binlog_row_based_done; /* check_table_binlog.. was called */
+ bool check_table_binlog_row_based_result; /* cached check_table_binlog... */
/*
TRUE <=> the engine guarantees that returned records are within the range
being scanned.
*/
bool in_range_check_pushed_down;
+ uint errkey; /* Last dup key */
+ uint key_used_on_scan;
+ uint active_index;
+
/** Length of ref (1-8 or the clustered key length) */
uint ref_length;
FT_INFO *ft_handler;
enum {NONE=0, INDEX, RND} inited;
- bool implicit_emptied; /* Can be !=0 only if HEAP */
+
const COND *pushed_cond;
/**
next_insert_id is the next value which should be inserted into the
@@ -2693,11 +2697,16 @@ public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
estimation_rows_to_insert(0), ht(ht_arg),
- ref(0), end_range(NULL), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
+ ref(0), end_range(NULL),
+ implicit_emptied(0),
+ mark_trx_read_write_done(0),
+ check_table_binlog_row_based_done(0),
+ check_table_binlog_row_based_result(0),
in_range_check_pushed_down(FALSE),
+ key_used_on_scan(MAX_KEY),
+ active_index(MAX_KEY),
ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE),
- implicit_emptied(0),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
tracker(NULL),
pushed_idx_cond(NULL),
@@ -3617,7 +3626,7 @@ public:
*) a) If the previous step succeeds, handler::ha_commit_inplace_alter_table() is
called to allow the storage engine to do any final updates to its structures,
to make all earlier changes durable and visible to other connections.
- b) If we have failed to upgrade lock or any errors have occured during the
+ b) If we have failed to upgrade lock or any errors have occurred during the
handler functions calls (including commit), we call
handler::ha_commit_inplace_alter_table()
to rollback all changes which were done during previous steps.
@@ -3875,10 +3884,22 @@ protected:
*/
virtual int delete_table(const char *name);
+public:
+ inline bool check_table_binlog_row_based(bool binlog_row);
private:
+ /* Cache result to avoid extra calls */
+ inline void mark_trx_read_write()
+ {
+ if (unlikely(!mark_trx_read_write_done))
+ {
+ mark_trx_read_write_done= 1;
+ mark_trx_read_write_internal();
+ }
+ }
+ void mark_trx_read_write_internal();
+ bool check_table_binlog_row_based_internal(bool binlog_row);
+
/* Private helpers */
- inline void mark_trx_read_write();
-private:
inline void increment_statistics(ulong SSV::*offset) const;
inline void decrement_statistics(ulong SSV::*offset) const;
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 6aef84f1b26..a84aebdf3c8 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -412,7 +412,7 @@ static inline bool is_hostname_valid(const char *hostname)
int ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
- char **hostname,
+ const char **hostname,
uint *connect_errors)
{
const struct sockaddr *ip= (const sockaddr *) ip_storage;
@@ -436,7 +436,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
DBUG_PRINT("info", ("Loopback address detected."));
/* Do not count connect errors from localhost. */
- *hostname= (char *) my_localhost;
+ *hostname= my_localhost;
DBUG_RETURN(0);
}
diff --git a/sql/hostname.h b/sql/hostname.h
index 81a1d0de88d..d6137b7c260 100644
--- a/sql/hostname.h
+++ b/sql/hostname.h
@@ -168,7 +168,7 @@ extern ulong host_cache_size;
#define RC_BLOCKED_HOST 1
int ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
- char **hostname, uint *connect_errors);
+ const char **hostname, uint *connect_errors);
void inc_host_errors(const char *ip_string, Host_errors *errors);
void reset_host_connect_errors(const char *ip_string);
diff --git a/sql/item.cc b/sql/item.cc
index 68376d1a746..4d3a3a6e3e7 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+ Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1260,6 +1260,22 @@ err:
if allowed, otherwise - null.
*/
bzero((char*) ltime,sizeof(*ltime));
+ if (fuzzydate & TIME_TIME_ONLY)
+ {
+ /*
+ In the following scenario:
+ - The caller expected to get a TIME value
+ - Item returned a not NULL string or numeric value
+ - But then conversion from string or number to TIME failed
+ we need to change the default time_type from MYSQL_TIMESTAMP_DATE
+ (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore
+ return TIME'00:00:00' rather than DATE'0000-00-00'.
+ If we don't do this, methods like Item::get_time_with_conversion()
+ will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00'
+ and return TIME'-838:59:59' instead of TIME'00:00:00' as a result.
+ */
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+ }
return null_value|= !(fuzzydate & TIME_FUZZY_DATES);
}
@@ -2340,7 +2356,7 @@ bool Item_field::update_table_bitmaps_processor(uchar *arg)
static inline void set_field_to_new_field(Field **field, Field **new_field)
{
- if (*field)
+ if (*field && (*field)->table == new_field[0]->table)
{
Field *newf= new_field[(*field)->field_index];
if ((*field)->ptr == newf->ptr)
@@ -2353,6 +2369,7 @@ bool Item_field::switch_to_nullable_fields_processor(uchar *arg)
Field **new_fields= (Field **)arg;
set_field_to_new_field(&field, new_fields);
set_field_to_new_field(&result_field, new_fields);
+ maybe_null= field && field->maybe_null();
return 0;
}
@@ -4568,7 +4585,7 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select)
@retval
0 column fully fixed and fix_fields() should return FALSE
@retval
- -1 error occured
+ -1 error occurred
*/
int
@@ -5440,8 +5457,7 @@ String_copier_for_item::copy_with_warn(CHARSET_INFO *dstcs, String *dst,
if (const char *pos= cannot_convert_error_pos())
{
char buf[16];
- int mblen= srccs->cset->charlen(srccs, (const uchar *) pos,
- (const uchar *) src + src_length);
+ int mblen= my_charlen(srccs, pos, src + src_length);
DBUG_ASSERT(mblen > 0 && mblen * 2 + 1 <= (int) sizeof(buf));
octet2hex(buf, pos, mblen);
push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN,
@@ -6392,6 +6408,7 @@ bool Item::cache_const_expr_analyzer(uchar **arg)
!(basic_const_item() || item->basic_const_item() ||
item->type() == Item::FIELD_ITEM ||
item->type() == SUBSELECT_ITEM ||
+ item->type() == CACHE_ITEM ||
/*
Do not cache GET_USER_VAR() function as its const_item() may
return TRUE for the current thread but it still may change
@@ -6489,7 +6506,7 @@ void Item_field::update_null_value()
UPDATE statement.
RETURN
- 0 if error occured
+ 0 if error occurred
ref if all conditions are met
this field otherwise
*/
diff --git a/sql/item.h b/sql/item.h
index d5f0dfec033..863265a73f7 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2,7 +2,7 @@
#define SQL_ITEM_INCLUDED
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -430,7 +430,7 @@ public:
RETURN
FALSE if parameter value has been set,
- TRUE if error has occured.
+ TRUE if error has occurred.
*/
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
@@ -663,9 +663,7 @@ protected:
Field *tmp_table_field_from_field_type(TABLE *table,
bool fixed_length,
bool set_blob_packlength);
- Field *create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length,
- uint convert_int_length);
+ Field *create_tmp_field(bool group, TABLE *table, uint convert_int_length);
public:
/*
@@ -1665,16 +1663,13 @@ public:
// used in row subselects to get value of elements
virtual void bring_value() {}
- virtual Field *create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length)
+ virtual Field *create_tmp_field(bool group, TABLE *table)
{
/*
Values with MY_INT32_NUM_DECIMAL_DIGITS digits may or may not fit into
Field_long : make them Field_longlong.
*/
- return create_tmp_field(false, table,
- convert_blob_length,
- MY_INT32_NUM_DECIMAL_DIGITS - 2);
+ return create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS - 2);
}
virtual Item_field *field_for_view_update() { return 0; }
@@ -2743,7 +2738,7 @@ public:
/*
If value for parameter was not set we treat it as non-const
- so noone will use parameters value in fix_fields still
+ so no one will use parameters value in fix_fields still
parameter is constant during execution.
*/
virtual table_map used_tables() const
@@ -3688,6 +3683,11 @@ public:
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
}
+ void used_tables_and_const_cache_update_and_join(Item *item)
+ {
+ item->update_used_tables();
+ used_tables_and_const_cache_join(item);
+ }
/*
Call update_used_tables() for all "argc" items in the array "argv"
and join with the current cache.
@@ -3697,10 +3697,7 @@ public:
void used_tables_and_const_cache_update_and_join(uint argc, Item **argv)
{
for (uint i=0 ; i < argc ; i++)
- {
- argv[i]->update_used_tables();
- used_tables_and_const_cache_join(argv[i]);
- }
+ used_tables_and_const_cache_update_and_join(argv[i]);
}
/*
Call update_used_tables() for all items in the list
@@ -3713,10 +3710,7 @@ public:
List_iterator_fast<Item> li(list);
Item *item;
while ((item=li++))
- {
- item->update_used_tables();
- used_tables_and_const_cache_join(item);
- }
+ used_tables_and_const_cache_update_and_join(item);
}
};
@@ -5161,6 +5155,12 @@ public:
return (this->*processor)(arg);
}
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
+ void split_sum_func2_example(THD *thd, Ref_ptr_array ref_pointer_array,
+ List<Item> &fields, uint flags)
+ {
+ example->split_sum_func2(thd, ref_pointer_array, fields, &example, flags);
+ }
+ Item *get_example() const { return example; }
};
@@ -5265,6 +5265,30 @@ public:
bool cache_value();
};
+
+class Item_cache_str_for_nullif: public Item_cache_str
+{
+public:
+ Item_cache_str_for_nullif(THD *thd, const Item *item)
+ :Item_cache_str(thd, item)
+ { }
+ Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
+ {
+ /**
+ Item_cache_str::safe_charset_converter() returns a new Item_cache
+ with Item_func_conv_charset installed on "example". The original
+ Item_cache is not referenced (neither directly nor recursively)
+ from the result of Item_cache_str::safe_charset_converter().
+
+ For NULLIF() purposes we need a different behavior:
+ we need a new instance of Item_func_conv_charset,
+ with the original Item_cache referenced in args[0]. See MDEV-9181.
+ */
+ return Item::safe_charset_converter(thd, tocs);
+ }
+};
+
+
class Item_cache_row: public Item_cache
{
Item_cache **values;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 0f1cccf50c9..187d2820531 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2246,50 +2246,6 @@ void Item_func_between::print(String *str, enum_query_type query_type)
}
-void
-Item_func_case_abbreviation2::fix_length_and_dec2(Item **args)
-{
- uint32 char_length;
- set_handler_by_field_type(agg_field_type(args, 2, true));
- maybe_null=args[0]->maybe_null || args[1]->maybe_null;
- decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
- unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag;
-
- if (Item_func_case_abbreviation2::result_type() == DECIMAL_RESULT ||
- Item_func_case_abbreviation2::result_type() == INT_RESULT)
- {
- int len0= args[0]->max_char_length() - args[0]->decimals
- - (args[0]->unsigned_flag ? 0 : 1);
-
- int len1= args[1]->max_char_length() - args[1]->decimals
- - (args[1]->unsigned_flag ? 0 : 1);
-
- char_length= MY_MAX(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
- }
- else
- char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
-
- switch (Item_func_case_abbreviation2::result_type()) {
- case STRING_RESULT:
- if (count_string_result_length(Item_func_case_abbreviation2::field_type(),
- args, 2))
- return;
- break;
- case DECIMAL_RESULT:
- case REAL_RESULT:
- break;
- case INT_RESULT:
- decimals= 0;
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- DBUG_ASSERT(0);
- }
- fix_char_length(char_length);
-}
-
-
-
uint Item_func_case_abbreviation2::decimal_precision2(Item **args) const
{
int arg0_int_part= args[0]->decimal_int_part();
@@ -2526,19 +2482,201 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
}
+void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
+ List<Item> &fields, uint flags)
+{
+ if (m_cache)
+ {
+ flags|= SPLIT_SUM_SKIP_REGISTERED; // See Item_func::split_sum_func
+ m_cache->split_sum_func2_example(thd, ref_pointer_array, fields, flags);
+ args[1]->split_sum_func2(thd, ref_pointer_array, fields, &args[1], flags);
+ }
+ else
+ {
+ Item_func::split_sum_func(thd, ref_pointer_array, fields, flags);
+ }
+}
+
+
+void Item_func_nullif::update_used_tables()
+{
+ if (m_cache)
+ {
+ used_tables_and_const_cache_init();
+ used_tables_and_const_cache_update_and_join(m_cache->get_example());
+ used_tables_and_const_cache_update_and_join(arg_count, args);
+ }
+ else
+ {
+ Item_func::update_used_tables();
+ }
+}
+
+
+
void
Item_func_nullif::fix_length_and_dec()
{
- if (!args[2]) // Only false if EOM
- return;
+ /*
+ If this is the first invocation of fix_length_and_dec(), create the
+ third argument as a copy of the first. This cannot be done before
+ fix_fields(), because fix_fields() might replace items,
+ for exampe NOT x --> x==0, or (SELECT 1) --> 1.
+ See also class Item_func_nullif declaration.
+ */
+ if (arg_count == 2)
+ args[arg_count++]= args[0];
+
+ THD *thd= current_thd;
+ /*
+ At prepared statement EXECUTE time, args[0] can already
+ point to a different Item, created during PREPARE time fix_length_and_dec().
+ For example, if character set conversion was needed, arguments can look
+ like this:
+
+ args[0]= > Item_func_conv_charset \
+ l_expr
+ args[2]= >------------------------/
+
+ Otherwise (during PREPARE or convensional execution),
+ args[0] and args[2] should still point to the same original l_expr.
+ */
+ DBUG_ASSERT(args[0] == args[2] || thd->stmt_arena->is_stmt_execute());
+ if (args[0]->type() == SUM_FUNC_ITEM &&
+ !thd->lex->is_ps_or_view_context_analysis())
+ {
+ /*
+ NULLIF(l_expr, r_expr)
+
+ is calculated in the way to return a result equal to:
+
+ CASE WHEN l_expr = r_expr THEN NULL ELSE r_expr END.
+
+ There's nothing special with r_expr, because it's referenced
+ only by args[1] and nothing else.
+
+ l_expr needs a special treatment, as it's referenced by both
+ args[0] and args[2] initially.
+
+ args[2] is used to return the value. Afrer all transformations
+ (e.g. in fix_length_and_dec(), equal field propagation, etc)
+ args[2] points to a an Item which preserves the exact data type and
+ attributes (e.g. collation) of the original l_expr.
+ It can point:
+ - to the original l_expr
+ - to an Item_cache pointing to l_expr
+ - to a constant of the same data type with l_expr.
+
+ args[0] is used for comparison. It can be replaced:
+
+ - to Item_func_conv_charset by character set aggregation routines
+ - to a constant Item by equal field propagation routines
+ (in case of Item_field)
+
+ The data type and/or the attributes of args[0] can differ from
+ the data type and the attributes of the original l_expr, to make
+ it comparable to args[1] (which points to r_expr or its replacement).
+
+ For aggregate functions we have to wrap the original args[0]/args[2]
+ into Item_cache (see MDEV-9181). In this case the Item_cache
+ instance becomes the subject to character set conversion instead of
+ the original args[0]/args[2], while the original args[0]/args[2] get
+ hidden inside the cache.
+
+ Some examples of what NULLIF can end up with after argument
+ substitution (we don't mention args[1] in some cases for simplicity):
+ 1. l_expr is not an aggragate function:
+
+ a. No conversion happened.
+ args[0] and args[2] were not replaced to something else
+ (i.e. neither by character set conversion, nor by propagation):
+
+ args[1] > r_expr
+ args[0] \
+ l_expr
+ args[2] /
+
+ b. Conversion of args[0] happened:
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10) CHARACTER SET latin1,
+ b CHAR(10) CHARACTER SET utf8);
+ SELECT * FROM t1 WHERE NULLIF(a,b);
+
+ args[1] > r_expr (Item_field for t1.b)
+ args[0] > Item_func_conv_charset\
+ l_expr (Item_field for t1.a)
+ args[2] > ----------------------/
+
+ c. Conversion of args[1] happened:
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10) CHARACTER SET utf8,
+ b CHAR(10) CHARACTER SET latin1);
+ SELECT * FROM t1 WHERE NULLIF(a,b);
+
+ args[1] > Item_func_conv_charset -> r_expr (Item_field for t1.b)
+ args[0] \
+ l_expr (Item_field for t1.a)
+ args[2] /
+
+ d. Conversion of only args[0] happened (by equal field proparation):
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10),
+ b CHAR(10));
+ SELECT * FROM t1 WHERE NULLIF(a,b) AND a='a';
+
+ args[1] > r_expr (Item_field for t1.b)
+ args[0] > Item_string('a') (constant replacement for t1.a)
+ args[2] > l_expr (Item_field for t1.a)
+
+ e. Conversion of both args[0] and args[2] happened
+ (by equal field propagation):
+
+ CREATE OR REPLACE TABLE t1 (a INT,b INT);
+ SELECT * FROM t1 WHERE NULLIF(a,b) AND a=5;
+
+ args[1] > r_expr (Item_field for "b")
+ args[0] \
+ Item_int (5) (constant replacement for "a")
+ args[2] /
+
+ 2. In case if l_expr is an aggregate function:
+
+ a. No conversion happened:
+
+ args[0] \
+ Item_cache > l_expr
+ args[2] /
+
+ b. Conversion of args[0] happened:
+
+ args[0] > Item_func_conv_charset \
+ Item_cache > l_expr
+ args[2] >------------------------/
+
+ c. Conversion of both args[0] and args[2] happened.
+ (e.g. by equal expression propagation)
+ TODO: check if it's possible (and add an example query if so).
+ */
+ m_cache= args[0]->cmp_type() == STRING_RESULT ?
+ new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
+ Item_cache::get_cache(thd, args[0]);
+ m_cache->setup(thd, args[0]);
+ m_cache->store(args[0]);
+ m_cache->set_used_tables(args[0]->used_tables());
+ thd->change_item_tree(&args[0], m_cache);
+ thd->change_item_tree(&args[2], m_cache);
+ }
set_handler_by_field_type(args[2]->field_type());
collation.set(args[2]->collation);
decimals= args[2]->decimals;
unsigned_flag= args[2]->unsigned_flag;
fix_char_length(args[2]->max_char_length());
maybe_null=1;
- setup_args_and_comparator(current_thd, &cmp);
+ setup_args_and_comparator(thd, &cmp);
}
@@ -2557,10 +2695,10 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
Therefore, after equal field propagation args[0] and args[2] can point
to different items.
*/
- if (!(query_type & QT_ITEM_FUNC_NULLIF_TO_CASE) || args[0] == args[2])
+ if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) || args[0] == args[2])
{
/*
- If no QT_ITEM_FUNC_NULLIF_TO_CASE is requested,
+ If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested,
that means we want the original NULLIF() representation,
e.g. when we are in:
SHOW CREATE {VIEW|FUNCTION|PROCEDURE}
@@ -2568,17 +2706,14 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
The original representation is possible only if
args[0] and args[2] still point to the same Item.
- The caller must pass call print() with QT_ITEM_FUNC_NULLIF_TO_CASE
+ The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF
if an expression has undergone some optimization
(e.g. equal field propagation done in optimize_cond()) already and
NULLIF() potentially has two different representations of "a":
- one "a" for comparison
- another "a" for the returned value!
-
- Note, the EXPLAIN EXTENDED and EXPLAIN FORMAT=JSON routines
- do pass QT_ITEM_FUNC_NULLIF_TO_CASE to print().
*/
- DBUG_ASSERT(args[0] == args[2]);
+ DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only);
str->append(func_name());
str->append('(');
args[2]->print(str, query_type);
@@ -2606,6 +2741,13 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
}
+int Item_func_nullif::compare()
+{
+ if (m_cache)
+ m_cache->cache_value();
+ return cmp.compare();
+}
+
/**
@note
Note that we have to evaluate the first argument twice as the compare
@@ -2621,7 +2763,7 @@ Item_func_nullif::real_op()
{
DBUG_ASSERT(fixed == 1);
double value;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0.0;
@@ -2636,7 +2778,7 @@ Item_func_nullif::int_op()
{
DBUG_ASSERT(fixed == 1);
longlong value;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
@@ -2651,7 +2793,7 @@ Item_func_nullif::str_op(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
@@ -2667,7 +2809,7 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value)
{
DBUG_ASSERT(fixed == 1);
my_decimal *res;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
@@ -2682,7 +2824,7 @@ bool
Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- if (!cmp.compare())
+ if (!compare())
return (null_value= true);
return (null_value= args[2]->get_date(ltime, fuzzydate));
}
@@ -2691,7 +2833,7 @@ Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate)
bool
Item_func_nullif::is_null()
{
- return (null_value= (!cmp.compare() ? 1 : args[2]->null_value));
+ return (null_value= (!compare() ? 1 : args[2]->null_value));
}
@@ -2767,7 +2909,7 @@ Item *Item_func_case::find_item(String *str)
return else_expr_num != -1 ? args[else_expr_num] : 0;
value_added_map|= 1U << (uint)cmp_type;
}
- if (!cmp_items[(uint)cmp_type]->cmp(args[i]) && !args[i]->null_value)
+ if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE)
return args[i + 1];
}
}
@@ -2885,24 +3027,6 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
}
-void Item_func_case::agg_str_lengths(Item* arg)
-{
- fix_char_length(MY_MAX(max_char_length(), arg->max_char_length()));
- set_if_bigger(decimals, arg->decimals);
- unsigned_flag= unsigned_flag && arg->unsigned_flag;
-}
-
-
-void Item_func_case::agg_num_lengths(Item *arg)
-{
- uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
- arg->unsigned_flag) - arg->decimals;
- set_if_bigger(max_length, len);
- set_if_bigger(decimals, arg->decimals);
- unsigned_flag= unsigned_flag && arg->unsigned_flag;
-}
-
-
/**
Check if (*place) and new_value points to different Items and call
THD::change_item_tree() if needed.
@@ -2964,18 +3088,7 @@ void Item_func_case::fix_length_and_dec()
}
else
{
- collation.set_numeric();
- max_length=0;
- decimals=0;
- unsigned_flag= TRUE;
- for (uint i= 0; i < ncases; i+= 2)
- agg_num_lengths(args[i + 1]);
- if (else_expr_num != -1)
- agg_num_lengths(args[else_expr_num]);
- max_length= my_decimal_precision_to_length_no_truncation(max_length +
- decimals,
- decimals,
- unsigned_flag);
+ fix_attributes(agg, nagg);
}
/*
@@ -3277,23 +3390,25 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
}
-void Item_func_coalesce::fix_length_and_dec()
+void Item_hybrid_func::fix_attributes(Item **items, uint nitems)
{
- set_handler_by_field_type(agg_field_type(args, arg_count, true));
- switch (Item_func_coalesce::result_type()) {
+ switch (Item_hybrid_func::result_type()) {
case STRING_RESULT:
- if (count_string_result_length(Item_func_coalesce::field_type(),
- args, arg_count))
+ if (count_string_result_length(Item_hybrid_func::field_type(),
+ items, nitems))
return;
break;
case DECIMAL_RESULT:
- count_decimal_length();
+ collation.set_numeric();
+ count_decimal_length(items, nitems);
break;
case REAL_RESULT:
- count_real_length();
+ collation.set_numeric();
+ count_real_length(items, nitems);
break;
case INT_RESULT:
- count_only_length(args, arg_count);
+ collation.set_numeric();
+ count_only_length(items, nitems);
decimals= 0;
break;
case ROW_RESULT:
@@ -3425,11 +3540,11 @@ static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b)
}
-int in_vector::find(Item *item)
+bool in_vector::find(Item *item)
{
uchar *result=get_value(item);
if (!result || !used_count)
- return 0; // Null value
+ return false; // Null value
uint start,end;
start=0; end=used_count-1;
@@ -3438,13 +3553,13 @@ int in_vector::find(Item *item)
uint mid=(start+end+1)/2;
int res;
if ((res=(*compare)(collation, base+mid*size, result)) == 0)
- return 1;
+ return true;
if (res < 0)
start=mid;
else
end=mid-1;
}
- return (int) ((*compare)(collation, base+start*size, result) == 0);
+ return ((*compare)(collation, base+start*size, result) == 0);
}
in_string::in_string(THD *thd, uint elements, qsort2_cmp cmp_func,
@@ -3776,14 +3891,20 @@ int cmp_item_row::cmp(Item *arg)
arg->bring_value();
for (uint i=0; i < n; i++)
{
- if (comparators[i]->cmp(arg->element_index(i)))
+ const int rc= comparators[i]->cmp(arg->element_index(i));
+ switch (rc)
{
- if (!arg->element_index(i)->null_value)
- return 1;
- was_null= 1;
+ case UNKNOWN:
+ was_null= true;
+ break;
+ case TRUE:
+ return TRUE;
+ case FALSE:
+ break; // elements #i are equal
}
+ arg->null_value|= arg->element_index(i)->null_value;
}
- return (arg->null_value= was_null);
+ return was_null ? UNKNOWN : FALSE;
}
@@ -3806,15 +3927,15 @@ void cmp_item_decimal::store_value(Item *item)
/* val may be zero if item is nnull */
if (val && val != &value)
my_decimal2decimal(val, &value);
+ m_null_value= item->null_value;
}
int cmp_item_decimal::cmp(Item *arg)
{
my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf);
- if (arg->null_value)
- return 1;
- return my_decimal_cmp(&value, tmp);
+ return (m_null_value || arg->null_value) ?
+ UNKNOWN : (my_decimal_cmp(&value, tmp) != 0);
}
@@ -3838,12 +3959,14 @@ void cmp_item_datetime::store_value(Item *item)
enum_field_types f_type=
tmp_item[0]->field_type_for_temporal_comparison(warn_item);
value= get_datetime_value(thd, &tmp_item, &lval_cache, f_type, &is_null);
+ m_null_value= item->null_value;
}
int cmp_item_datetime::cmp(Item *arg)
{
- return value != arg->val_temporal_packed(warn_item);
+ const bool rc= value != arg->val_temporal_packed(warn_item);
+ return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
@@ -3867,10 +3990,10 @@ bool Item_func_in::count_sargable_conds(uchar *arg)
}
-bool Item_func_in::nulls_in_row()
+bool Item_func_in::list_contains_null()
{
Item **arg,**arg_end;
- for (arg= args+1, arg_end= args+arg_count; arg != arg_end ; arg++)
+ for (arg= args + 1, arg_end= args+arg_count; arg != arg_end ; arg++)
{
if ((*arg)->null_inside())
return 1;
@@ -3985,6 +4108,32 @@ void Item_func_in::fix_length_and_dec()
}
}
+ /*
+ First conditions for bisection to be possible:
+ 1. All types are similar, and
+ 2. All expressions in <in value list> are const
+ */
+ bool bisection_possible=
+ type_cnt == 1 && // 1
+ const_itm; // 2
+ if (bisection_possible)
+ {
+ /*
+ In the presence of NULLs, the correct result of evaluating this item
+ must be UNKNOWN or FALSE. To achieve that:
+ - If type is scalar, we can use bisection and the "have_null" boolean.
+ - If type is ROW, we will need to scan all of <in value list> when
+ searching, so bisection is impossible. Unless:
+ 3. UNKNOWN and FALSE are equivalent results
+ 4. Neither left expression nor <in value list> contain any NULL value
+ */
+
+ if (m_compare_type == ROW_RESULT &&
+ ((!is_top_level_item() || negated) && // 3
+ (list_contains_null() || args[0]->maybe_null))) // 4
+ bisection_possible= false;
+ }
+
if (type_cnt == 1)
{
if (m_compare_type == STRING_RESULT &&
@@ -3997,7 +4146,7 @@ void Item_func_in::fix_length_and_dec()
uint cols= args[0]->cols();
cmp_item_row *cmp= 0;
- if (const_itm && !nulls_in_row())
+ if (bisection_possible)
{
array= new (thd->mem_root) in_row(thd, arg_count-1, 0);
cmp= &((in_row*)array)->tmp;
@@ -4026,11 +4175,8 @@ void Item_func_in::fix_length_and_dec()
}
}
}
- /*
- Row item with NULLs inside can return NULL or FALSE =>
- they can't be processed as static
- */
- if (type_cnt == 1 && const_itm && !nulls_in_row())
+
+ if (bisection_possible)
{
/*
IN must compare INT columns and constants as int values (the same
@@ -4086,20 +4232,25 @@ void Item_func_in::fix_length_and_dec()
array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1);
break;
}
- if (array && !(thd->is_fatal_error)) // If not EOM
+ if (!array || thd->is_fatal_error) // OOM
+ return;
+ uint j=0;
+ for (uint i=1 ; i < arg_count ; i++)
{
- uint j=0;
- for (uint i=1 ; i < arg_count ; i++)
+ array->set(j,args[i]);
+ if (!args[i]->null_value)
+ j++; // include this cell in the array.
+ else
{
- array->set(j,args[i]);
- if (!args[i]->null_value) // Skip NULL values
- j++;
- else
- have_null= 1;
+ /*
+ We don't put NULL values in array, to avoid erronous matches in
+ bisection.
+ */
+ have_null= 1;
}
- if ((array->used_count= j))
- array->sort();
}
+ if ((array->used_count= j))
+ array->sort();
}
else
{
@@ -4167,7 +4318,14 @@ longlong Item_func_in::val_int()
uint value_added_map= 0;
if (array)
{
- int tmp=array->find(args[0]);
+ bool tmp=array->find(args[0]);
+ /*
+ NULL on left -> UNKNOWN.
+ Found no match, and NULL on right -> UNKNOWN.
+ NULL on right can never give a match, as it is not stored in
+ array.
+ See also the 'bisection_possible' variable in fix_length_and_dec().
+ */
null_value=args[0]->null_value || (!tmp && have_null);
return (longlong) (!null_value && tmp != negated);
}
@@ -4189,13 +4347,12 @@ longlong Item_func_in::val_int()
if (!(value_added_map & (1U << (uint)cmp_type)))
{
in_item->store_value(args[0]);
- if ((null_value= args[0]->null_value))
- return 0;
value_added_map|= 1U << (uint)cmp_type;
}
- if (!in_item->cmp(args[i]) && !args[i]->null_value)
+ const int rc= in_item->cmp(args[i]);
+ if (rc == FALSE)
return (longlong) (!negated);
- have_null|= args[i]->null_value;
+ have_null|= (rc == UNKNOWN);
}
null_value= have_null;
@@ -5605,7 +5762,7 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref)
args[0]->under_not(this);
if (args[0]->type() == FIELD_ITEM)
{
- /* replace "NOT <field>" with "<filed> == 0" */
+ /* replace "NOT <field>" with "<field> == 0" */
Query_arena backup, *arena;
Item *new_item;
bool rc= TRUE;
@@ -6317,7 +6474,8 @@ longlong Item_equal::val_int()
/* Skip fields of tables that has not been read yet */
if (!field->table->status || (field->table->status & STATUS_NULL_ROW))
{
- if (eval_item->cmp(item) || (null_value= item->null_value))
+ const int rc= eval_item->cmp(item);
+ if ((rc == TRUE) || (null_value= (rc == UNKNOWN)))
return 0;
}
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 38d07017b32..5789186dbe8 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1,7 +1,7 @@
#ifndef ITEM_CMPFUNC_INCLUDED
#define ITEM_CMPFUNC_INCLUDED
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -795,6 +795,7 @@ public:
public:
inline void negate() { negated= !negated; }
inline void top_level_item() { pred_level= 1; }
+ bool is_top_level_item() const { return pred_level; }
Item *neg_transformer(THD *thd)
{
negated= !negated;
@@ -901,7 +902,11 @@ public:
String *str_op(String *);
my_decimal *decimal_op(my_decimal *);
bool date_op(MYSQL_TIME *ltime,uint fuzzydate);
- void fix_length_and_dec();
+ void fix_length_and_dec()
+ {
+ set_handler_by_field_type(agg_field_type(args, arg_count, true));
+ fix_attributes(args, arg_count);
+ }
const char *func_name() const { return "coalesce"; }
table_map not_null_tables() const { return 0; }
};
@@ -914,13 +919,18 @@ public:
*/
class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
{
+protected:
+ void fix_length_and_dec2(Item **items)
+ {
+ set_handler_by_field_type(agg_field_type(items, 2, true));
+ fix_attributes(items, 2);
+ }
+ uint decimal_precision2(Item **args) const;
public:
Item_func_case_abbreviation2(THD *thd, Item *a, Item *b):
Item_func_hybrid_field_type(thd, a, b) { }
Item_func_case_abbreviation2(THD *thd, Item *a, Item *b, Item *c):
Item_func_hybrid_field_type(thd, a, b, c) { }
- void fix_length_and_dec2(Item **args);
- uint decimal_precision2(Item **args) const;
};
@@ -996,11 +1006,21 @@ class Item_func_nullif :public Item_func_hybrid_field_type
- Item_field::propagate_equal_fields(ANY_SUBST) for the left "a"
- Item_field::propagate_equal_fields(IDENTITY_SUBST) for the right "a"
*/
+ Item_cache *m_cache;
+ int compare();
public:
- // Put "a" to args[0] for comparison and to args[2] for the returned value.
+ /*
+ Here we pass three arguments to the parent constructor, as NULLIF
+ is a three-argument function, it needs two copies of the first argument
+ (see above). But fix_fields() will be confused if we try to prepare the
+ same Item twice (if args[0]==args[2]), so we hide the third argument
+ (decrementing arg_count) and copy args[2]=args[0] again after fix_fields().
+ See also Item_func_nullif::fix_length_and_dec().
+ */
Item_func_nullif(THD *thd, Item *a, Item *b):
- Item_func_hybrid_field_type(thd, a, b, a)
- {}
+ Item_func_hybrid_field_type(thd, a, b, a),
+ m_cache(NULL)
+ { arg_count--; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
double real_op();
longlong int_op();
@@ -1010,6 +1030,9 @@ public:
uint decimal_precision() const { return args[2]->decimal_precision(); }
const char *func_name() const { return "nullif"; }
void print(String *str, enum_query_type query_type);
+ void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
+ List<Item> &fields, uint flags);
+ void update_used_tables();
table_map not_null_tables() const { return 0; }
bool is_null();
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
@@ -1054,7 +1077,7 @@ public:
{
my_qsort2(base,used_count,size,compare,(void*)collation);
}
- int find(Item *item);
+ bool find(Item *item);
/*
Create an instance of Item_{type} (e.g. Item_decimal) constant object
@@ -1222,6 +1245,10 @@ public:
cmp_item() { cmp_charset= &my_charset_bin; }
virtual ~cmp_item() {}
virtual void store_value(Item *item)= 0;
+ /**
+ @returns result (TRUE, FALSE or UNKNOWN) of
+ "stored argument's value <> item's value"
+ */
virtual int cmp(Item *item)= 0;
// for optimized IN with row
virtual int compare(cmp_item *item)= 0;
@@ -1234,7 +1261,14 @@ public:
}
};
-class cmp_item_string :public cmp_item
+/// cmp_item which stores a scalar (i.e. non-ROW).
+class cmp_item_scalar : public cmp_item
+{
+protected:
+ bool m_null_value; ///< If stored value is NULL
+};
+
+class cmp_item_string : public cmp_item_scalar
{
protected:
String *value_res;
@@ -1260,14 +1294,20 @@ public:
void store_value(Item *item)
{
value_res= item->val_str(&value);
+ m_null_value= item->null_value;
}
int cmp(Item *arg)
{
char buff[STRING_BUFFER_USUAL_SIZE];
- String tmp(buff, sizeof(buff), cmp_charset), *res;
- res= arg->val_str(&tmp);
- return (value_res ? (res ? sortcmp(value_res, res, cmp_charset) : 1) :
- (res ? -1 : 0));
+ String tmp(buff, sizeof(buff), cmp_charset), *res= arg->val_str(&tmp);
+ if (m_null_value || arg->null_value)
+ return UNKNOWN;
+ if (value_res && res)
+ return sortcmp(value_res, res, cmp_charset) != 0;
+ else if (!value_res && !res)
+ return FALSE;
+ else
+ return TRUE;
}
int compare(cmp_item *ci)
{
@@ -1282,7 +1322,7 @@ public:
}
};
-class cmp_item_int :public cmp_item
+class cmp_item_int : public cmp_item_scalar
{
longlong value;
public:
@@ -1290,10 +1330,12 @@ public:
void store_value(Item *item)
{
value= item->val_int();
+ m_null_value= item->null_value;
}
int cmp(Item *arg)
{
- return value != arg->val_int();
+ const bool rc= value != arg->val_int();
+ return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
int compare(cmp_item *ci)
{
@@ -1309,7 +1351,7 @@ public:
If the left item is a constant one then its value is cached in the
lval_cache variable.
*/
-class cmp_item_datetime :public cmp_item
+class cmp_item_datetime : public cmp_item_scalar
{
longlong value;
public:
@@ -1327,7 +1369,7 @@ public:
cmp_item *make_same();
};
-class cmp_item_real :public cmp_item
+class cmp_item_real : public cmp_item_scalar
{
double value;
public:
@@ -1335,10 +1377,12 @@ public:
void store_value(Item *item)
{
value= item->val_real();
+ m_null_value= item->null_value;
}
int cmp(Item *arg)
{
- return value != arg->val_real();
+ const bool rc= value != arg->val_real();
+ return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
int compare(cmp_item *ci)
{
@@ -1349,7 +1393,7 @@ public:
};
-class cmp_item_decimal :public cmp_item
+class cmp_item_decimal : public cmp_item_scalar
{
my_decimal value;
public:
@@ -1376,12 +1420,13 @@ public:
void store_value(Item *item)
{
value_res= item->val_str(&value);
+ m_null_value= item->null_value;
}
int cmp(Item *item)
{
// Should never be called
- DBUG_ASSERT(0);
- return 1;
+ DBUG_ASSERT(false);
+ return TRUE;
}
int compare(cmp_item *ci)
{
@@ -1442,39 +1487,47 @@ public:
Item *find_item(String *str);
CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
void cleanup();
- void agg_str_lengths(Item *arg);
- void agg_num_lengths(Item *arg);
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond);
};
/*
- The Item_func_in class implements the in_expr IN(values_list) function.
+ The Item_func_in class implements
+ in_expr IN (<in value list>)
+ and
+ in_expr NOT IN (<in value list>)
The current implementation distinguishes 2 cases:
- 1) all items in the value_list are constants and have the same
+ 1) all items in <in value list> are constants and have the same
result type. This case is handled by in_vector class.
- 2) items in the value_list have different result types or there is some
- non-constant items.
- In this case Item_func_in employs several cmp_item objects to performs
- comparisons of in_expr and an item from the values_list. One cmp_item
+ 2) otherwise Item_func_in employs several cmp_item objects to perform
+ comparisons of in_expr and an item from <in value list>. One cmp_item
object for each result type. Different result types are collected in the
fix_length_and_dec() member function by means of collect_cmp_types()
function.
*/
class Item_func_in :public Item_func_opt_neg
{
+ /**
+ Usable if <in value list> is made only of constants. Returns true if one
+ of these constants contains a NULL. Example:
+ IN ( (-5, (12,NULL)), ... ).
+ */
+ bool list_contains_null();
protected:
SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
Field *field, Item *value);
public:
- /*
- an array of values when the right hand arguments of IN
- are all SQL constant and there are no nulls
- */
+ /// An array of values, created when the bisection lookup method is used
in_vector *array;
+ /**
+ If there is some NULL among <in value list>, during a val_int() call; for
+ example
+ IN ( (1,(3,'col')), ... ), where 'col' is a column which evaluates to
+ NULL.
+ */
bool have_null;
- /*
- true when all arguments of the IN clause are of compatible types
+ /**
+ true when all arguments of the IN list are of compatible types
and can be used safely as comparisons for key conditions
*/
bool arg_types_compatible;
@@ -1528,7 +1581,6 @@ public:
virtual void print(String *str, enum_query_type query_type);
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return " IN "; }
- bool nulls_in_row();
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
bool count_sargable_conds(uchar *arg);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9183fd8f81a..57bd004cf88 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -234,7 +234,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
}
}
fix_length_and_dec();
- if (thd->is_error()) // An error inside fix_length_and_dec occured
+ if (thd->is_error()) // An error inside fix_length_and_dec occurred
return TRUE;
fixed= 1;
return FALSE;
@@ -577,18 +577,19 @@ void Item_udf_func::fix_num_length_and_dec()
@retval False on success, true on error.
*/
-void Item_func::count_datetime_length(Item **item, uint nitems)
+void Item_func::count_datetime_length(enum_field_types field_type_arg,
+ Item **item, uint nitems)
{
unsigned_flag= 0;
decimals= 0;
- if (field_type() != MYSQL_TYPE_DATE)
+ if (field_type_arg != MYSQL_TYPE_DATE)
{
for (uint i= 0; i < nitems; i++)
set_if_bigger(decimals, item[i]->decimals);
}
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
uint len= decimals ? (decimals + 1) : 0;
- len+= mysql_temporal_int_part_length(field_type());
+ len+= mysql_temporal_int_part_length(field_type_arg);
fix_char_length(len);
}
@@ -597,16 +598,16 @@ void Item_func::count_datetime_length(Item **item, uint nitems)
result length/precision depends on argument ones.
*/
-void Item_func::count_decimal_length()
+void Item_func::count_decimal_length(Item **item, uint nitems)
{
int max_int_part= 0;
decimals= 0;
unsigned_flag= 1;
- for (uint i=0 ; i < arg_count ; i++)
+ for (uint i=0 ; i < nitems ; i++)
{
- set_if_bigger(decimals, args[i]->decimals);
- set_if_bigger(max_int_part, args[i]->decimal_int_part());
- set_if_smaller(unsigned_flag, args[i]->unsigned_flag);
+ set_if_bigger(decimals, item[i]->decimals);
+ set_if_bigger(max_int_part, item[i]->decimal_int_part());
+ set_if_smaller(unsigned_flag, item[i]->unsigned_flag);
}
int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
fix_char_length(my_decimal_precision_to_length_no_truncation(precision,
@@ -637,19 +638,20 @@ void Item_func::count_only_length(Item **item, uint nitems)
result length/precision depends on argument ones.
*/
-void Item_func::count_real_length()
+void Item_func::count_real_length(Item **items, uint nitems)
{
uint32 length= 0;
decimals= 0;
max_length= 0;
- for (uint i=0 ; i < arg_count ; i++)
+ unsigned_flag= false;
+ for (uint i=0 ; i < nitems ; i++)
{
if (decimals != NOT_FIXED_DEC)
{
- set_if_bigger(decimals, args[i]->decimals);
- set_if_bigger(length, (args[i]->max_length - args[i]->decimals));
+ set_if_bigger(decimals, items[i]->decimals);
+ set_if_bigger(length, (items[i]->max_length - items[i]->decimals));
}
- set_if_bigger(max_length, args[i]->max_length);
+ set_if_bigger(max_length, items[i]->max_length);
}
if (decimals != NOT_FIXED_DEC)
{
@@ -678,11 +680,11 @@ bool Item_func::count_string_result_length(enum_field_types field_type_arg,
if (agg_arg_charsets_for_string_result(collation, items, nitems, 1))
return true;
if (is_temporal_type(field_type_arg))
- count_datetime_length(items, nitems);
+ count_datetime_length(field_type_arg, items, nitems);
else
{
- decimals= NOT_FIXED_DEC;
count_only_length(items, nitems);
+ decimals= max_length ? NOT_FIXED_DEC : 0;
}
return false;
}
@@ -757,7 +759,7 @@ void Item_num_op::fix_length_and_dec(void)
if (r0 == REAL_RESULT || r1 == REAL_RESULT ||
r0 == STRING_RESULT || r1 ==STRING_RESULT)
{
- count_real_length();
+ count_real_length(args, arg_count);
max_length= float_length(decimals);
set_handler_by_result_type(REAL_RESULT);
}
@@ -5622,7 +5624,7 @@ void Item_func_get_user_var::fix_length_and_dec()
/*
If the variable didn't exist it has been created as a STRING-type.
- 'm_var_entry' is NULL only if there occured an error during the call to
+ 'm_var_entry' is NULL only if there occurred an error during the call to
get_var_with_binlog.
*/
if (!error && m_var_entry)
diff --git a/sql/item_func.h b/sql/item_func.h
index b11b29b6aa1..5c21535adbe 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -41,6 +41,14 @@ protected:
*/
uint allowed_arg_cols;
String *val_str_from_val_str_ascii(String *str, String *str2);
+
+ void count_only_length(Item **item, uint nitems);
+ void count_real_length(Item **item, uint nitems);
+ void count_decimal_length(Item **item, uint nitems);
+ void count_datetime_length(enum_field_types field_type,
+ Item **item, uint nitems);
+ bool count_string_result_length(enum_field_types field_type,
+ Item **item, uint nitems);
public:
table_map not_null_tables_cache;
@@ -148,16 +156,10 @@ public:
virtual void print(String *str, enum_query_type query_type);
void print_op(String *str, enum_query_type query_type);
void print_args(String *str, uint from, enum_query_type query_type);
- void count_only_length(Item **item, uint nitems);
- void count_real_length();
- void count_decimal_length();
inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
return (null_value=args[0]->get_date_with_conversion(ltime, fuzzy_date));
}
- void count_datetime_length(Item **item, uint nitems);
- bool count_string_result_length(enum_field_types field_type,
- Item **item, uint nitems);
inline bool get_arg0_time(MYSQL_TIME *ltime)
{
null_value= args[0]->get_time(ltime);
@@ -175,7 +177,7 @@ public:
{
DBUG_ASSERT(thd == table->in_use);
return result_type() != STRING_RESULT ?
- create_tmp_field(false, table, 0, MY_INT32_NUM_DECIMAL_DIGITS) :
+ create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) :
tmp_table_field_from_field_type(table, false, false);
}
Item *get_tmp_table_item(THD *thd);
@@ -387,6 +389,8 @@ public:
class Item_hybrid_func: public Item_func,
public Type_handler_hybrid_field_type
{
+protected:
+ void fix_attributes(Item **item, uint nitems);
public:
Item_hybrid_func(THD *thd): Item_func(thd) { }
Item_hybrid_func(THD *thd, Item *a): Item_func(thd, a) { }
@@ -1766,7 +1770,7 @@ public:
Field *create_field_for_create_select(THD *thd, TABLE *table)
{
return result_type() != STRING_RESULT ?
- create_tmp_field(false, table, 0, MY_INT32_NUM_DECIMAL_DIGITS) :
+ create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) :
tmp_table_field_from_field_type(table, false, true);
}
table_map used_tables() const
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 352c9fd93b9..39d06fd7a26 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -436,7 +436,7 @@ String *Item_func_convexhull::val_str(String *str_value)
if (!cur_pi->get_next())
{
/* Single point. */
- if (res_receiver.single_point(cur_pi->x, cur_pi->y))
+ if (res_receiver.single_point(cur_pi->node.shape.x, cur_pi->node.shape.y))
goto mem_error;
goto build_result;
}
@@ -461,8 +461,8 @@ String *Item_func_convexhull::val_str(String *str_value)
{
/* We only have 2 nodes in the result, so we create a polyline. */
if (res_receiver.start_shape(Gcalc_function::shape_line) ||
- res_receiver.add_point(left_first->pi->x, left_first->pi->y) ||
- res_receiver.add_point(left_cur->pi->x, left_cur->pi->y) ||
+ res_receiver.add_point(left_first->pi->node.shape.x, left_first->pi->node.shape.y) ||
+ res_receiver.add_point(left_cur->pi->node.shape.x, left_cur->pi->node.shape.y) ||
res_receiver.complete_shape())
goto mem_error;
@@ -475,7 +475,7 @@ String *Item_func_convexhull::val_str(String *str_value)
while (left_first)
{
- if (res_receiver.add_point(left_first->pi->x, left_first->pi->y))
+ if (res_receiver.add_point(left_first->pi->node.shape.x, left_first->pi->node.shape.y))
goto mem_error;
left_first= left_first->get_next();
}
@@ -485,7 +485,7 @@ String *Item_func_convexhull::val_str(String *str_value)
right_cur= right_cur->prev;
while (right_cur->prev)
{
- if (res_receiver.add_point(right_cur->pi->x, right_cur->pi->y))
+ if (res_receiver.add_point(right_cur->pi->node.shape.x, right_cur->pi->node.shape.y))
goto mem_error;
right_cur= right_cur->prev;
}
@@ -1105,10 +1105,10 @@ static double count_edge_t(const Gcalc_heap::Info *ea,
double &ex, double &ey, double &vx, double &vy,
double &e_sqrlen)
{
- ex= eb->x - ea->x;
- ey= eb->y - ea->y;
- vx= v->x - ea->x;
- vy= v->y - ea->y;
+ ex= eb->node.shape.x - ea->node.shape.x;
+ ey= eb->node.shape.y - ea->node.shape.y;
+ vx= v->node.shape.x - ea->node.shape.x;
+ vy= v->node.shape.y - ea->node.shape.y;
e_sqrlen= ex * ex + ey * ey;
return (ex * vx + ey * vy) / e_sqrlen;
}
@@ -1124,8 +1124,8 @@ static double distance_to_line(double ex, double ey, double vx, double vy,
static double distance_points(const Gcalc_heap::Info *a,
const Gcalc_heap::Info *b)
{
- double x= a->x - b->x;
- double y= a->y - b->y;
+ double x= a->node.shape.x - b->node.shape.x;
+ double y= a->node.shape.y - b->node.shape.y;
return sqrt(x * x + y * y);
}
@@ -2333,7 +2333,7 @@ double Item_func_distance::val_real()
continue;
count_distance:
- if (cur_point->shape >= obj2_si)
+ if (cur_point->node.shape.shape >= obj2_si)
continue;
cur_point_edge= !cur_point->is_bottom();
@@ -2341,13 +2341,13 @@ count_distance:
{
/* We only check vertices of object 2 */
if (dist_point->type != Gcalc_heap::nt_shape_node ||
- dist_point->shape < obj2_si)
+ dist_point->node.shape.shape < obj2_si)
continue;
/* if we have an edge to check */
- if (dist_point->left)
+ if (dist_point->node.shape.left)
{
- t= count_edge_t(dist_point, dist_point->left, cur_point,
+ t= count_edge_t(dist_point, dist_point->node.shape.left, cur_point,
ex, ey, vx, vy, e_sqrlen);
if ((t>0.0) && (t<1.0))
{
@@ -2358,7 +2358,7 @@ count_distance:
}
if (cur_point_edge)
{
- t= count_edge_t(cur_point, cur_point->left, dist_point,
+ t= count_edge_t(cur_point, cur_point->node.shape.left, dist_point,
ex, ey, vx, vy, e_sqrlen);
if ((t>0.0) && (t<1.0))
{
diff --git a/sql/item_row.h b/sql/item_row.h
index cb839b60c81..ddb6f0835f2 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -2,7 +2,7 @@
#define ITEM_ROW_INCLUDED
/*
- Copyright (c) 2002, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,11 +28,20 @@
@endverbatim
*/
+
+/**
+ Item which stores (x,y,...) and ROW(x,y,...).
+ Note that this can be recursive: ((x,y),(z,t)) is a ROW of ROWs.
+*/
class Item_row: public Item,
private Item_args,
private Used_tables_and_const_cache
{
table_map not_null_tables_cache;
+ /**
+ If elements are made only of constants, of which one or more are
+ NULL. For example, this item is (1,2,NULL), or ( (1,NULL), (2,3) ).
+ */
bool with_null;
public:
Item_row(THD *thd, List<Item> &list):
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index bc39517a914..7c1c5f7da7d 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1564,7 +1564,7 @@ String *Item_func_insert::val_str(String *str)
length= res->charpos((int) length, (uint32) start);
/* Re-testing with corrected params */
- if (start > res->length())
+ if (start + 1 > res->length()) // remember, start = args[1].val_int() - 1
return res; /* purecov: inspected */ // Wrong param; skip insert
if (length > res->length() - start)
length= res->length() - start;
@@ -3404,7 +3404,7 @@ String *Item_func_conv_charset::val_str(String *str)
String *arg= args[0]->val_str(str);
String_copier_for_item copier(current_thd);
return ((null_value= args[0]->null_value ||
- copier.copy_with_warn(conv_charset, &tmp_value,
+ copier.copy_with_warn(collation.collation, &tmp_value,
arg->charset(), arg->ptr(),
arg->length(), arg->length()))) ?
0 : &tmp_value;
@@ -3412,7 +3412,7 @@ String *Item_func_conv_charset::val_str(String *str)
void Item_func_conv_charset::fix_length_and_dec()
{
- collation.set(conv_charset, DERIVATION_IMPLICIT);
+ DBUG_ASSERT(collation.derivation == DERIVATION_IMPLICIT);
fix_char_length(args[0]->max_char_length());
}
@@ -3421,7 +3421,7 @@ void Item_func_conv_charset::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("convert("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" using "));
- str->append(conv_charset->csname);
+ str->append(collation.collation->csname);
str->append(')');
}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index dfa38d7eed6..0ff38157c25 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -960,20 +960,22 @@ class Item_func_conv_charset :public Item_str_func
String tmp_value;
public:
bool safe;
- CHARSET_INFO *conv_charset; // keep it public
Item_func_conv_charset(THD *thd, Item *a, CHARSET_INFO *cs):
Item_str_func(thd, a)
- { conv_charset= cs; use_cached_value= 0; safe= 0; }
+ {
+ collation.set(cs, DERIVATION_IMPLICIT);
+ use_cached_value= 0; safe= 0;
+ }
Item_func_conv_charset(THD *thd, Item *a, CHARSET_INFO *cs, bool cache_if_const):
Item_str_func(thd, a)
{
- conv_charset= cs;
+ collation.set(cs, DERIVATION_IMPLICIT);
if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive())
{
uint errors= 0;
String tmp, *str= args[0]->val_str(&tmp);
if (!str || str_value.copy(str->ptr(), str->length(),
- str->charset(), conv_charset, &errors))
+ str->charset(), cs, &errors))
null_value= 1;
use_cached_value= 1;
str_value.mark_as_const();
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index b0eb22d2e0e..94e7bc98618 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -58,6 +58,8 @@ Item_subselect::Item_subselect(THD *thd_arg):
{
DBUG_ENTER("Item_subselect::Item_subselect");
DBUG_PRINT("enter", ("this: 0x%lx", (ulong) this));
+ sortbuffer.str= 0;
+
#ifndef DBUG_OFF
exec_counter= 0;
#endif
@@ -153,6 +155,9 @@ void Item_subselect::cleanup()
if (engine)
engine->cleanup();
reset();
+ filesort_buffer.free_sort_buffer();
+ my_free(sortbuffer.str);
+
value_assigned= 0;
expr_cache= 0;
forced_const= FALSE;
@@ -5322,6 +5327,7 @@ int subselect_hash_sj_engine::exec()
item_in->reset();
item_in->make_const();
item_in->set_first_execution();
+ thd->lex->current_select= save_select;
DBUG_RETURN(FALSE);
}
@@ -5365,6 +5371,7 @@ int subselect_hash_sj_engine::exec()
item_in->null_value= 1;
item_in->make_const();
item_in->set_first_execution();
+ thd->lex->current_select= save_select;
DBUG_RETURN(FALSE);
}
@@ -5907,7 +5914,7 @@ int subselect_partial_match_engine::exec()
/* Search for a complete match. */
if ((lookup_res= lookup_engine->index_lookup()))
{
- /* An error occured during lookup(). */
+ /* An error occurred during lookup(). */
item_in->value= 0;
item_in->null_value= 0;
return lookup_res;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 1b450044954..58b5a948048 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -95,6 +95,9 @@ public:
subselect_engine *engine;
/* unit of subquery */
st_select_lex_unit *unit;
+ /* Cached buffers used when calling filesort in sub queries */
+ Filesort_buffer filesort_buffer;
+ LEX_STRING sortbuffer;
/* A reference from inside subquery predicate to somewhere outside of it */
class Ref_to_outside : public Sql_alloc
{
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 4b4ed0225d3..f774ee5a561 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -29,6 +29,7 @@
#include <my_global.h>
#include "sql_priv.h"
#include "sql_select.h"
+#include "uniques.h"
/**
Calculate the affordable RAM limit for structures like TREE or Unique
@@ -1187,8 +1188,7 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg)
}
-Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length)
+Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table)
{
Field *field;
MEM_ROOT *mem_root;
@@ -1196,9 +1196,9 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
if (args[0]->type() == Item::FIELD_ITEM)
{
field= ((Item_field*) args[0])->field;
-
+
if ((field= create_tmp_field_from_field(table->in_use, field, name, table,
- NULL, convert_blob_length)))
+ NULL)))
field->flags&= ~NOT_NULL_FLAG;
return field;
}
@@ -1224,7 +1224,7 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
Field::NONE, name, decimals);
break;
default:
- return Item_sum::create_tmp_field(group, table, convert_blob_length);
+ return Item_sum::create_tmp_field(group, table);
}
if (field)
field->init(table);
@@ -1636,8 +1636,7 @@ Item *Item_sum_avg::copy_or_same(THD* thd)
}
-Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
- uint convert_blob_len)
+Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table)
{
Field *field;
MEM_ROOT *mem_root= table->in_use->mem_root;
@@ -1873,8 +1872,7 @@ Item *Item_sum_variance::copy_or_same(THD* thd)
If we're grouping, then we need some space to serialize variables into, to
pass around.
*/
-Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table,
- uint convert_blob_len)
+Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table)
{
Field *field;
if (group)
@@ -3150,6 +3148,11 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
ER_THD(thd, ER_CUT_VALUE_GROUP_CONCAT),
item->row_count);
+ /**
+ To avoid duplicated warnings in Item_func_group_concat::val_str()
+ */
+ if (table && table->blob_storage)
+ table->blob_storage->set_truncated_value(false);
return 1;
}
return 0;
@@ -3287,6 +3290,8 @@ void Item_func_group_concat::cleanup()
if (table)
{
THD *thd= table->in_use;
+ if (table->blob_storage)
+ delete table->blob_storage;
free_tmp_table(thd, table);
table= 0;
if (tree)
@@ -3354,6 +3359,8 @@ void Item_func_group_concat::clear()
reset_tree(tree);
if (unique_filter)
unique_filter->reset();
+ if (table && table->blob_storage)
+ table->blob_storage->reset();
/* No need to reset the table as we never call write_row */
}
@@ -3480,6 +3487,7 @@ bool Item_func_group_concat::setup(THD *thd)
{
List<Item> list;
SELECT_LEX *select_lex= thd->lex->current_select;
+ const bool order_or_distinct= MY_TEST(arg_count_order > 0 || distinct);
DBUG_ENTER("Item_func_group_concat::setup");
/*
@@ -3492,9 +3500,6 @@ bool Item_func_group_concat::setup(THD *thd)
if (!(tmp_table_param= new TMP_TABLE_PARAM))
DBUG_RETURN(TRUE);
- /* We'll convert all blobs to varchar fields in the temporary table */
- tmp_table_param->convert_blob_length= max_length *
- collation.collation->mbmaxlen;
/* Push all not constant fields to the list and create a temp table */
always_null= 0;
for (uint i= 0; i < arg_count_field; i++)
@@ -3534,18 +3539,9 @@ bool Item_func_group_concat::setup(THD *thd)
count_field_types(select_lex, tmp_table_param, all_fields, 0);
tmp_table_param->force_copy_fields= force_copy_fields;
DBUG_ASSERT(table == 0);
- if (arg_count_order > 0 || distinct)
+ if (order_or_distinct)
{
/*
- Currently we have to force conversion of BLOB values to VARCHAR's
- if we are to store them in TREE objects used for ORDER BY and
- DISTINCT. This leads to truncation if the BLOB's size exceeds
- Field_varstring::MAX_SIZE.
- */
- set_if_smaller(tmp_table_param->convert_blob_length,
- Field_varstring::MAX_SIZE);
-
- /*
Force the create_tmp_table() to convert BIT columns to INT
as we cannot compare two table records containg BIT fields
stored in the the tree used for distinct/order by.
@@ -3578,6 +3574,13 @@ bool Item_func_group_concat::setup(THD *thd)
table->file->extra(HA_EXTRA_NO_ROWS);
table->no_rows= 1;
+ /**
+ Initialize blob_storage if GROUP_CONCAT is used
+ with ORDER BY | DISTINCT and BLOB field count > 0.
+ */
+ if (order_or_distinct && table->s->blob_fields)
+ table->blob_storage= new Blob_mem_storage();
+
/*
Need sorting or uniqueness: init tree and choose a function to sort.
Don't reserve space for NULLs: if any of gconcat arguments is NULL,
@@ -3630,6 +3633,16 @@ String* Item_func_group_concat::val_str(String* str)
if (no_appended && tree)
/* Tree is used for sorting as in ORDER BY */
tree_walk(tree, &dump_leaf_key, this, left_root_right);
+
+ if (table && table->blob_storage &&
+ table->blob_storage->is_truncated_value())
+ {
+ warning_for_row= true;
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT),
+ row_count);
+ }
+
return &result;
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index b8f41030465..cfe2d3db878 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -484,11 +484,9 @@ public:
}
virtual void make_unique() { force_copy_fields= TRUE; }
Item *get_tmp_table_item(THD *thd);
- Field *create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length)
+ Field *create_tmp_field(bool group, TABLE *table)
{
- return Item::create_tmp_field(group, table, convert_blob_length,
- MY_INT32_NUM_DECIMAL_DIGITS);
+ return Item::create_tmp_field(group, table, MY_INT32_NUM_DECIMAL_DIGITS);
}
virtual bool collect_outer_ref_processor(uchar *param);
bool init_sum_func_check(THD *thd);
@@ -872,7 +870,7 @@ public:
return has_with_distinct() ? "avg(distinct " : "avg(";
}
Item *copy_or_same(THD* thd);
- Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length);
+ Field *create_tmp_field(bool group, TABLE *table);
void cleanup()
{
count= 0;
@@ -928,7 +926,7 @@ public:
const char *func_name() const
{ return sample ? "var_samp(" : "variance("; }
Item *copy_or_same(THD* thd);
- Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length);
+ Field *create_tmp_field(bool group, TABLE *table);
enum Item_result result_type () const { return REAL_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
void cleanup()
@@ -1006,8 +1004,7 @@ protected:
bool any_value() { return was_values; }
void no_rows_in_result();
void restore_to_before_no_rows_in_result();
- Field *create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length);
+ Field *create_tmp_field(bool group, TABLE *table);
};
@@ -1160,7 +1157,6 @@ public:
fixed= true;
}
table_map used_tables() const { return (table_map) 1L; }
- Field *get_tmp_table_field() { DBUG_ASSERT(0); return NULL; }
void set_result_field(Field *) { DBUG_ASSERT(0); }
void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 4cc9f6dc5bc..16edb35c392 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2995,7 +2995,7 @@ void Item_func_timestamp_diff::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("SECOND"));
break;
case INTERVAL_MICROSECOND:
- str->append(STRING_WITH_LEN("SECOND_FRAC"));
+ str->append(STRING_WITH_LEN("MICROSECOND"));
break;
default:
break;
diff --git a/sql/lex.h b/sql/lex.h
index 05f0050e4f6..01e73f5f3d3 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -475,6 +475,7 @@ static SYMBOL symbols[] = {
{ "REAL", SYM(REAL)},
{ "REBUILD", SYM(REBUILD_SYM)},
{ "RECOVER", SYM(RECOVER_SYM)},
+ { "RECURSIVE", SYM(RECURSIVE_SYM)},
{ "REDO_BUFFER_SIZE", SYM(REDO_BUFFER_SIZE_SYM)},
{ "REDOFILE", SYM(REDOFILE_SYM)},
{ "REDUNDANT", SYM(REDUNDANT_SYM)},
diff --git a/sql/log.cc b/sql/log.cc
index 79eb9accdff..dc8c08bfd36 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -632,7 +632,7 @@ void Log_to_csv_event_handler::cleanup()
indicated in the return value.
@retval FALSE OK
- @retval TRUE error occured
+ @retval TRUE error occurred
*/
bool Log_to_csv_event_handler::
@@ -797,7 +797,7 @@ err:
RETURN
FALSE - OK
- TRUE - error occured
+ TRUE - error occurred
*/
bool Log_to_csv_event_handler::
@@ -1108,7 +1108,7 @@ void Log_to_file_event_handler::flush()
RETURN
FALSE - OK
- TRUE - error occured
+ TRUE - error occurred
*/
bool LOGGER::error_log_print(enum loglevel level, const char *format,
@@ -1266,7 +1266,7 @@ bool LOGGER::flush_general_log()
RETURN
FALSE OK
- TRUE error occured
+ TRUE error occurred
*/
bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
@@ -1674,14 +1674,14 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
uchar *buf;
size_t len=0;
wsrep_write_cache_buf(cache, &buf, &len);
- WSREP_WARN("binlog trx cache not empty (%lu bytes) @ connection close %lu",
- len, thd->thread_id);
+ WSREP_WARN("binlog trx cache not empty (%lu bytes) @ connection close %lld",
+ len, (longlong) thd->thread_id);
if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
cache = cache_mngr->get_binlog_cache_log(false);
wsrep_write_cache_buf(cache, &buf, &len);
- WSREP_WARN("binlog stmt cache not empty (%lu bytes) @ connection close %lu",
- len, thd->thread_id);
+ WSREP_WARN("binlog stmt cache not empty (%lu bytes) @ connection close %lld",
+ len, (longlong) thd->thread_id);
if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
}
#endif /* WITH_WSREP */
@@ -2839,7 +2839,7 @@ void MYSQL_QUERY_LOG::reopen_file()
RETURN
FASE - OK
- TRUE - error occured
+ TRUE - error occurred
*/
bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
@@ -2941,7 +2941,7 @@ err:
RETURN
FALSE - OK
- TRUE - error occured
+ TRUE - error occurred
*/
bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
@@ -6403,7 +6403,7 @@ binlog_checkpoint_callback(void *cookie)
/*
For every supporting engine, we increment the xid_count and issue a
commit_checkpoint_request(). Then we can count when all
- commit_checkpoint_notify() callbacks have occured, and then log a new
+ commit_checkpoint_notify() callbacks have occurred, and then log a new
binlog checkpoint event.
*/
mysql_bin_log.mark_xids_active(entry->binlog_id, 1);
@@ -6582,7 +6582,6 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
bool check_purge= false;
- //todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
mysql_mutex_lock(&LOCK_log);
prev_binlog_id= current_binlog_id;
if ((error= rotate(force_rotate, &check_purge)))
@@ -9508,9 +9507,7 @@ binlog_background_thread(void *arg __attribute__((unused)))
thd= new THD;
thd->system_thread= SYSTEM_THREAD_BINLOG_BACKGROUND;
thd->thread_stack= (char*) &thd; /* Set approximate stack start */
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thread_id++;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->thread_id= next_thread_id();
thd->store_globals();
thd->security_ctx->skip_grants();
thd->set_command(COM_DAEMON);
@@ -9597,9 +9594,8 @@ binlog_background_thread(void *arg __attribute__((unused)))
THD_STAGE_INFO(thd, stage_binlog_stopping_background_thread);
- mysql_mutex_lock(&LOCK_thread_count);
+ /* No need to use mutex as thd is not linked into other threads */
delete thd;
- mysql_mutex_unlock(&LOCK_thread_count);
my_thread_end();
@@ -10183,7 +10179,8 @@ IO_CACHE * get_trans_log(THD * thd)
if (cache_mngr)
return cache_mngr->get_binlog_cache_log(true);
- WSREP_DEBUG("binlog cache not initialized, conn :%ld", thd->thread_id);
+ WSREP_DEBUG("binlog cache not initialized, conn: %lld",
+ (longlong) thd->thread_id);
return NULL;
}
@@ -10221,7 +10218,8 @@ void thd_binlog_trx_reset(THD * thd)
void thd_binlog_rollback_stmt(THD * thd)
{
- WSREP_DEBUG("thd_binlog_rollback_stmt :%ld", thd->thread_id);
+ WSREP_DEBUG("thd_binlog_rollback_stmt connection: %lld",
+ (longlong) thd->thread_id);
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
if (cache_mngr)
diff --git a/sql/log_event.cc b/sql/log_event.cc
index ff2f9594922..b56a9e2aee3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1392,9 +1392,9 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
if (packet->append(file, data_len - LOG_EVENT_MINIMAL_HEADER_LEN))
{
/*
- Fatal error occured when appending rest of the event
+ Fatal error occurred when appending rest of the event
to packet, possible failures:
- 1. EOF occured when reading from file, it's really an error
+ 1. EOF occurred when reading from file, it's really an error
as there's supposed to be more bytes available.
file->error will have been set to number of bytes left to read
2. Read was interrupted, file->error would normally be set to -1
@@ -3525,7 +3525,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
slave_proxy_id= thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET);
exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
- db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
+ db_len = (uchar)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);
/*
@@ -4428,8 +4428,18 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
if (thd->m_digest != NULL)
thd->m_digest->reset(thd->m_token_array, max_digest_length);
+ if (thd->slave_thread)
+ {
+ /*
+ The opt_log_slow_slave_statements variable can be changed
+ dynamically, so we have to set the sql_log_slow respectively.
+ */
+ thd->variables.sql_log_slow= opt_log_slow_slave_statements;
+ }
+
thd->enable_slow_log= thd->variables.sql_log_slow;
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+ FALSE);
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
log_slow_statement(thd);
@@ -7463,6 +7473,7 @@ bool slave_execute_deferred_events(THD *thd)
return res;
res= rgi->deferred_events->execute(rgi);
+ rgi->deferred_events->rewind();
return res;
}
@@ -9895,7 +9906,18 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
}
#ifdef HAVE_QUERY_CACHE
+#ifdef WITH_WSREP
+ /*
+ Moved invalidation right before the call to rows_event_stmt_cleanup(),
+ to avoid query cache being polluted with stale entries.
+ */
+ if (! (WSREP(thd) && (thd->wsrep_exec_mode == REPL_RECV)))
+ {
+#endif /* WITH_WSREP */
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+#ifdef WITH_WSREP
+ }
+#endif /* WITH_WSREP */
#endif
}
@@ -10087,6 +10109,14 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
/* remove trigger's tables */
if (slave_run_triggers_for_rbr)
restore_empty_query_table_list(thd->lex);
+
+#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
+ if (WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV)
+ {
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+ }
+#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
+
if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
slave_rows_error_report(ERROR_LEVEL,
thd->is_error() ? 0 : error,
@@ -11128,8 +11158,8 @@ bool Table_map_log_event::write_data_body()
DBUG_ASSERT(m_dbnam != NULL);
DBUG_ASSERT(m_tblnam != NULL);
/* We use only one byte per length for storage in event: */
- DBUG_ASSERT(m_dblen < 128);
- DBUG_ASSERT(m_tbllen < 128);
+ DBUG_ASSERT(m_dblen <= MY_MIN(NAME_LEN, 255));
+ DBUG_ASSERT(m_tbllen <= MY_MIN(NAME_LEN, 255));
uchar const dbuf[]= { (uchar) m_dblen };
uchar const tbuf[]= { (uchar) m_tbllen };
@@ -11352,6 +11382,7 @@ bool Rows_log_event::process_triggers(trg_event_type event,
{
bool result;
DBUG_ENTER("Rows_log_event::process_triggers");
+ m_table->triggers->mark_fields_used(event);
if (slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_YES)
{
tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
@@ -11455,7 +11486,10 @@ Rows_log_event::write_row(rpl_group_info *rgi,
/* unpack row into table->record[0] */
if ((error= unpack_current_row(rgi)))
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
if (m_curr_row == m_rows_buf && !invoke_triggers)
{
@@ -12316,7 +12350,11 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE))
error= HA_ERR_GENERIC; // in case if error is not set yet
if (!error)
+ {
+ m_table->mark_columns_per_binlog_row_image();
error= m_table->file->ha_delete_row(m_table->record[0]);
+ m_table->default_column_bitmaps();
+ }
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
@@ -12459,8 +12497,8 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
We need to read the second image in the event of error to be
able to skip to the next pair of updates
*/
- m_curr_row= m_curr_row_end;
- unpack_current_row(rgi, &m_cols_ai);
+ if ((m_curr_row= m_curr_row_end))
+ unpack_current_row(rgi, &m_cols_ai);
thd_proc_info(thd, tmp);
return error;
}
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 387534b1bed..ff2f7b156d5 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -248,7 +248,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
}
if (error)
- { /* error has occured during the transaction */
+ { /* error has occurred during the transaction */
rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), NULL,
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
@@ -1593,7 +1593,7 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
} // if (table)
if (error)
- { /* error has occured during the transaction */
+ { /* error has occurred during the transaction */
rli->report(ERROR_LEVEL, thd->net.last_errno, NULL,
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
diff --git a/sql/mdl.cc b/sql/mdl.cc
index ab4f5288d4a..61591ec9f57 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -1064,7 +1064,16 @@ MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
wait_result != ETIMEDOUT && wait_result != ETIME)
{
#ifdef WITH_WSREP
- if (wsrep_thd_is_BF(owner->get_thd(), true))
+ // Allow tests to block the applier thread using the DBUG facilities
+ DBUG_EXECUTE_IF("sync.wsrep_before_mdl_wait",
+ {
+ const char act[]=
+ "now "
+ "wait_for signal.wsrep_before_mdl_wait";
+ DBUG_ASSERT(!debug_sync_set_action((owner->get_thd()),
+ STRING_WITH_LEN(act)));
+ };);
+ if (wsrep_thd_is_BF(owner->get_thd(), false))
{
wait_result= mysql_cond_wait(&m_COND_wait_status, &m_LOCK_wait_status);
}
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
index 91f5cd3f39c..dcb8503b880 100644
--- a/sql/my_apc.cc
+++ b/sql/my_apc.cc
@@ -119,7 +119,7 @@ void init_show_explain_psi_keys(void)
@retval FALSE - Ok, the call has been made
@retval TRUE - Call wasnt made (either the target is in disabled state or
- timeout occured)
+ timeout occurred)
*/
bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 9b4f45a9971..c39789f7c97 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -119,10 +119,10 @@ static void die(const char *fmt, ...)
if (verbose_errors)
{
fprintf(stderr,
- "http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n"
+ "https://mariadb.com/kb/en/installation-issues-on-windows contains some help\n"
"for solving the most common problems. If this doesn't help you, please\n"
- "leave a comment in the Knowledgebase or file a bug report at\n"
- "http://mariadb.org/jira");
+ "leave a comment in the Knowledge Base or file a bug report at\n"
+ "https://jira.mariadb.org");
}
fflush(stderr);
va_end(args);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index c79586a08da..e7d7f90d44e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2008, 2015, MariaDB
+ Copyright (c) 2008, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -363,6 +363,7 @@ static bool volatile select_thread_in_use, signal_thread_in_use;
static volatile bool ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0, opt_silent_startup= 0;
+
uint kill_cached_threads;
static uint wake_thread;
ulong max_used_connections;
@@ -377,7 +378,7 @@ static char *default_collation_name;
char *default_storage_engine, *default_tmp_storage_engine;
char *enforced_storage_engine=NULL;
static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
-static I_List<THD> thread_cache;
+static I_List<CONNECT> thread_cache;
static bool binlog_format_used= false;
LEX_STRING opt_init_connect, opt_init_slave;
mysql_cond_t COND_thread_cache;
@@ -389,6 +390,7 @@ static DYNAMIC_ARRAY all_options;
bool opt_bin_log, opt_bin_log_used=0, opt_ignore_builtin_innodb= 0;
my_bool opt_log, debug_assert_if_crashed_table= 0, opt_help= 0;
+my_bool debug_assert_on_not_freed_memory= 0;
my_bool disable_log_notes;
static my_bool opt_abort;
ulonglong log_output_options;
@@ -409,8 +411,7 @@ uint volatile global_disable_checkpoint;
ulong slow_start_timeout;
#endif
/*
- True if the bootstrap thread is running. Protected by LOCK_thread_count,
- just like thread_count.
+ True if the bootstrap thread is running. Protected by LOCK_start_thread.
Used in bootstrap() function to determine if the bootstrap thread
has completed. Note, that we can't use 'thread_count' instead,
since in 5.1, in presence of the Event Scheduler, there may be
@@ -422,7 +423,7 @@ ulong slow_start_timeout;
bootstrap either, since we want to be able to process event-related
SQL commands in the init file and in --bootstrap mode.
*/
-bool in_bootstrap= FALSE;
+bool volatile in_bootstrap= FALSE;
/**
@brief 'grant_option' is used to indicate if privileges needs
to be checked, in which case the lock, LOCK_grant, is used
@@ -556,7 +557,8 @@ ulong max_prepared_stmt_count;
statements.
*/
ulong prepared_stmt_count=0;
-ulong thread_id=1L,current_pid;
+my_thread_id global_thread_id= 1;
+ulong current_pid;
ulong slow_launch_threads = 0;
uint sync_binlog_period= 0, sync_relaylog_period= 0,
sync_relayloginfo_period= 0, sync_masterinfo_period= 0;
@@ -712,7 +714,33 @@ SHOW_COMP_OPTION have_openssl;
/* Thread specific variables */
pthread_key(THD*, THR_THD);
-mysql_mutex_t LOCK_thread_count, LOCK_thread_cache;
+
+/*
+ LOCK_thread_count protects the following variables:
+ thread_count Number of threads with THD that servers queries.
+ threads Linked list of active THD's.
+ The effect of this is that one can't unlink and
+ delete a THD as long as one has locked
+ LOCK_thread_count.
+ ready_to_exit
+ delayed_insert_threads
+*/
+mysql_mutex_t LOCK_thread_count;
+
+/*
+ LOCK_start_thread is used to syncronize thread start and stop with
+ other threads.
+
+ It also protects these variables:
+ handler_count
+ in_bootstrap
+ select_thread_in_use
+ slave_init_thread_running
+ check_temp_dir() call
+*/
+mysql_mutex_t LOCK_start_thread;
+
+mysql_mutex_t LOCK_thread_cache;
mysql_mutex_t
LOCK_status, LOCK_show_status, LOCK_error_log, LOCK_short_uuid_generator,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
@@ -737,7 +765,7 @@ mysql_mutex_t LOCK_des_key_file;
#endif
mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
mysql_rwlock_t LOCK_system_variables_hash;
-mysql_cond_t COND_thread_count;
+mysql_cond_t COND_thread_count, COND_start_thread;
pthread_t signal_thread;
pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
@@ -887,6 +915,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
key_LOCK_error_messages, key_LOG_INFO_lock,
+ key_LOCK_start_thread,
key_LOCK_thread_count, key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc;
PSI_mutex_key key_RELAYLOG_LOCK_index;
@@ -973,6 +1002,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
{ &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0},
{ &key_LOCK_slave_state, "LOCK_slave_state", 0},
+ { &key_LOCK_start_thread, "LOCK_start_thread", PSI_FLAG_GLOBAL},
{ &key_LOCK_binlog_state, "LOCK_binlog_state", 0},
{ &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0},
{ &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0},
@@ -1014,6 +1044,7 @@ PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache,
+ key_COND_start_thread,
key_BINLOG_COND_queue_busy;
PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
key_COND_wait_commit;
@@ -1073,6 +1104,7 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0},
{ &key_COND_prepare_ordered, "COND_prepare_ordered", 0},
{ &key_COND_slave_init, "COND_slave_init", 0},
+ { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL},
{ &key_COND_wait_gtid, "COND_wait_gtid", 0},
{ &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
};
@@ -1199,8 +1231,13 @@ void init_net_server_extension(THD *thd)
/* Activate this private extension for the mysqld server. */
thd->net.extension= & thd->m_net_server_extension;
}
+#else
+void init_net_server_extension(THD *thd)
+{
+}
#endif /* EMBEDDED_LIBRARY */
+
/**
A log message for the error log, buffered in memory.
Log messages are temporarily buffered when generated before the error log
@@ -1466,7 +1503,6 @@ static openssl_lock_t *openssl_dynlock_create(const char *, int);
static void openssl_dynlock_destroy(openssl_lock_t *, const char *, int);
static void openssl_lock_function(int, int, const char *, int);
static void openssl_lock(int, openssl_lock_t *, const char *, int);
-static unsigned long openssl_id_function();
#endif
char *des_key_file;
#ifndef EMBEDDED_LIBRARY
@@ -1545,8 +1581,8 @@ static void close_connections(void)
#if !defined(__WIN__)
DBUG_PRINT("quit", ("waiting for select thread: 0x%lx",
(ulong) select_thread));
- mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_start_thread);
while (select_thread_in_use)
{
struct timespec abstime;
@@ -1560,7 +1596,7 @@ static void close_connections(void)
set_timespec(abstime, 2);
for (uint tmp=0 ; tmp < 10 && select_thread_in_use; tmp++)
{
- error= mysql_cond_timedwait(&COND_thread_count, &LOCK_thread_count,
+ error= mysql_cond_timedwait(&COND_start_thread, &LOCK_start_thread,
&abstime);
if (error != EINTR)
break;
@@ -1571,7 +1607,7 @@ static void close_connections(void)
#endif
close_server_sock();
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
@@ -1640,7 +1676,7 @@ static void close_connections(void)
while ((tmp=it++))
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- tmp->thread_id));
+ (ulong) tmp->thread_id));
/* We skip slave threads & scheduler on this first loop through. */
if (tmp->slave_thread)
continue;
@@ -1696,6 +1732,8 @@ static void close_connections(void)
much smaller than even 2 seconds, this is only a safety fallback against
stuck threads so server shutdown is not held up forever.
*/
+ DBUG_PRINT("info", ("thread_count: %d", thread_count));
+
for (int i= 0; *(volatile int32*) &thread_count && i < 1000; i++)
my_sleep(20000);
@@ -1707,11 +1745,9 @@ static void close_connections(void)
for (;;)
{
- DBUG_PRINT("quit",("Locking LOCK_thread_count"));
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
if (!(tmp=threads.get()))
{
- DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
mysql_mutex_unlock(&LOCK_thread_count);
break;
}
@@ -1720,12 +1756,13 @@ static void close_connections(void)
{
if (global_system_variables.log_warnings)
sql_print_warning(ER_DEFAULT(ER_FORCING_CLOSE),my_progname,
- tmp->thread_id,
+ (ulong) tmp->thread_id,
(tmp->main_security_ctx.user ?
tmp->main_security_ctx.user : ""));
close_connection(tmp,ER_SERVER_SHUTDOWN);
}
#endif
+
#ifdef WITH_WSREP
/*
* WSREP_TODO:
@@ -1992,7 +2029,8 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
extern "C" sig_handler print_signal_warning(int sig)
{
if (global_system_variables.log_warnings)
- sql_print_warning("Got signal %d from thread %ld", sig,my_thread_id());
+ sql_print_warning("Got signal %d from thread %ld", sig,
+ (ulong) my_thread_id());
#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif
@@ -2207,10 +2245,14 @@ void clean_up(bool print_message)
logger.cleanup_end();
sys_var_end();
free_charsets();
+
+ /*
+ Signal mysqld_main() that it can exit
+ do the broadcast inside the lock to ensure that my_end() is not called
+ during broadcast()
+ */
mysql_mutex_lock(&LOCK_thread_count);
- DBUG_PRINT("quit", ("got thread count lock"));
ready_to_exit=1;
- /* do the broadcast inside the lock to ensure that my_end() is not called */
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
@@ -2258,6 +2300,7 @@ static void clean_up_mutexes()
mysql_rwlock_destroy(&LOCK_grant);
mysql_mutex_destroy(&LOCK_thread_count);
mysql_mutex_destroy(&LOCK_thread_cache);
+ mysql_mutex_destroy(&LOCK_start_thread);
mysql_mutex_destroy(&LOCK_status);
mysql_mutex_destroy(&LOCK_show_status);
mysql_mutex_destroy(&LOCK_delayed_insert);
@@ -2291,6 +2334,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_error_messages);
mysql_cond_destroy(&COND_thread_count);
mysql_cond_destroy(&COND_thread_cache);
+ mysql_cond_destroy(&COND_start_thread);
mysql_cond_destroy(&COND_flush_thread_cache);
mysql_mutex_destroy(&LOCK_server_started);
mysql_cond_destroy(&COND_server_started);
@@ -2312,7 +2356,9 @@ static void clean_up_mutexes()
static void set_ports()
{
}
-
+void close_connection(THD *thd, uint sql_errno)
+{
+}
#else
static void set_ports()
{
@@ -2794,6 +2840,7 @@ static void network_init(void)
@note
For the connection that is doing shutdown, this is called twice
*/
+
void close_connection(THD *thd, uint sql_errno)
{
DBUG_ENTER("close_connection");
@@ -2829,20 +2876,6 @@ extern "C" sig_handler end_mysqld_signal(int sig __attribute__((unused)))
DBUG_VOID_RETURN; /* purecov: deadcode */
}
-
-/*
- Cleanup THD object
-
- SYNOPSIS
- thd_cleanup()
- thd Thread handler
-*/
-
-void thd_cleanup(THD *thd)
-{
- thd->cleanup();
-}
-
/*
Decrease number of connections
@@ -2850,43 +2883,49 @@ void thd_cleanup(THD *thd)
dec_connection_count()
*/
-void dec_connection_count(THD *thd)
+void dec_connection_count(scheduler_functions *scheduler)
{
-#ifdef WITH_WSREP
- /*
- Do not decrement when its wsrep system thread. wsrep_applier is set for
- applier as well as rollbacker threads.
- */
- if (thd->wsrep_applier)
- return;
-#endif /* WITH_WSREP */
-
- DBUG_ASSERT(*thd->scheduler->connection_count > 0);
mysql_mutex_lock(&LOCK_connection_count);
- (*thd->scheduler->connection_count)--;
+ (*scheduler->connection_count)--;
mysql_mutex_unlock(&LOCK_connection_count);
}
/*
Delete THD and decrement thread counters, including thread_running
+
+ This is mainly used to delete event threads which are not increasing
+ global counters.
*/
void delete_running_thd(THD *thd)
{
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->add_status_to_global();
+ unlink_not_visible_thd(thd);
delete thd;
dec_thread_running();
+ dec_thread_count();
+}
+
+/*
+ Decrease number of threads. Signal when it reaches 0
+
+ SYNOPSIS
+ dec_thread_count()
+*/
+
+void dec_thread_count(void)
+{
+ DBUG_ASSERT(thread_count > 0);
thread_safe_decrement32(&thread_count);
signal_thd_deleted();
}
+
/*
- Send a signal to unblock close_conneciton() if there is no more
- threads running with a THD attached
+ Send a signal to unblock close_conneciton() / rpl_slave_init_thread()
+ if there is no more threads running with a THD attached
It's safe to check for thread_count and service_thread_count outside
of a mutex as we are only interested to see if they where decremented
@@ -2898,7 +2937,7 @@ void delete_running_thd(THD *thd)
void signal_thd_deleted()
{
- if (!thread_count && ! service_thread_count)
+ if (!thread_count && !service_thread_count)
{
/* Signal close_connections() that all THD's are freed */
mysql_mutex_lock(&LOCK_thread_count);
@@ -2914,9 +2953,6 @@ void signal_thd_deleted()
SYNOPSIS
unlink_thd()
thd Thread handler
-
- NOTES
- LOCK_thread_count is locked and left locked
*/
void unlink_thd(THD *thd)
@@ -2924,23 +2960,18 @@ void unlink_thd(THD *thd)
DBUG_ENTER("unlink_thd");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
- thd_cleanup(thd);
- dec_connection_count(thd);
-
- thd->add_status_to_global();
-
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
/*
- Used by binlog_reset_master. It would be cleaner to use
- DEBUG_SYNC here, but that's not possible because the THD's debug
- sync feature has been shut down at this point.
+ Do not decrement when its wsrep system thread. wsrep_applier is set for
+ applier as well as rollbacker threads.
*/
- DBUG_EXECUTE_IF("sleep_after_lock_thread_count_before_delete_thd", sleep(5););
- mysql_mutex_unlock(&LOCK_thread_count);
+ if (IF_WSREP(!thd->wsrep_applier, 1))
+ dec_connection_count(thd->scheduler);
+ thd->cleanup();
+ thd->add_status_to_global();
+ unlink_not_visible_thd(thd);
delete thd;
- thread_safe_decrement32(&thread_count);
+ dec_thread_count();
DBUG_VOID_RETURN;
}
@@ -2964,6 +2995,7 @@ void unlink_thd(THD *thd)
static bool cache_thread()
{
+ struct timespec abstime;
DBUG_ENTER("cache_thread");
mysql_mutex_lock(&LOCK_thread_cache);
@@ -2982,18 +3014,46 @@ static bool cache_thread()
PSI_THREAD_CALL(delete_current_thread)();
#endif
+#ifndef DBUG_OFF
+ while (_db_is_pushed_())
+ _db_pop_();
+#endif
+
+ set_timespec(abstime, THREAD_CACHE_TIMEOUT);
while (!abort_loop && ! wake_thread && ! kill_cached_threads)
- mysql_cond_wait(&COND_thread_cache, &LOCK_thread_cache);
+ {
+ int error= mysql_cond_timedwait(&COND_thread_cache, &LOCK_thread_cache,
+ &abstime);
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+ /*
+ If timeout, end thread.
+ If a new thread is requested (wake_thread is set), we will handle
+ the call, even if we got a timeout (as we are already awake and free)
+ */
+ break;
+ }
+ }
cached_thread_count--;
if (kill_cached_threads)
mysql_cond_signal(&COND_flush_thread_cache);
if (wake_thread)
{
+ CONNECT *connect;
THD *thd;
+
wake_thread--;
- thd= thread_cache.get();
+ connect= thread_cache.get();
mysql_mutex_unlock(&LOCK_thread_cache);
+ if (!(thd= connect->create_thd()))
+ {
+ /* Out of resources. Free thread to get more resources */
+ connect->close_and_delete();
+ DBUG_RETURN(0);
+ }
+ delete connect;
+
thd->thread_stack= (char*) &thd; // For store_globals
(void) thd->store_globals();
@@ -3016,10 +3076,7 @@ static bool cache_thread()
thd->thr_create_utime= microsecond_interval_timer();
thd->start_utime= thd->thr_create_utime;
- /* Link thd into list of all active threads (THD's) */
- mysql_mutex_lock(&LOCK_thread_count);
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
+ add_to_active_threads(thd);
DBUG_RETURN(1);
}
}
@@ -3033,7 +3090,7 @@ static bool cache_thread()
SYNOPSIS
one_thread_per_connection_end()
- thd Thread handler
+ thd Thread handler. This may be null if we run out of resources.
put_in_cache Store thread in cache, if there is room in it
Normally this is true in all cases except when we got
out of resources initializing the current thread
@@ -3052,12 +3109,13 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
DBUG_ENTER("one_thread_per_connection_end");
const bool wsrep_applier= IF_WSREP(thd->wsrep_applier, false);
- unlink_thd(thd);
+ if (thd)
+ unlink_thd(thd);
if (!wsrep_applier && put_in_cache && cache_thread())
DBUG_RETURN(0); // Thread is reused
- signal_thd_deleted();
+ DBUG_PRINT("info", ("killing thread"));
DBUG_LEAVE; // Must match DBUG_ENTER()
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
ERR_remove_state(0);
@@ -3425,7 +3483,7 @@ static void start_signal_handler(void)
(void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
(void) my_setstacksize(&thr_attr,my_thread_stack_size);
- mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_start_thread);
if ((error= mysql_thread_create(key_thread_signal_hand,
&signal_thread, &thr_attr, signal_hand, 0)))
{
@@ -3433,8 +3491,8 @@ static void start_signal_handler(void)
error,errno);
exit(1);
}
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_wait(&COND_start_thread, &LOCK_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
(void) pthread_attr_destroy(&thr_attr);
DBUG_VOID_RETURN;
@@ -3484,12 +3542,12 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
signal to start_signal_handler that we are ready
This works by waiting for start_signal_handler to free mutex,
after which we signal it that we are ready.
- At this pointer there is no other threads running, so there
+ At this point there is no other threads running, so there
should not be any other mysql_cond_signal() calls.
*/
- mysql_mutex_lock(&LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
- mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_lock(&LOCK_start_thread);
+ mysql_cond_broadcast(&COND_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
(void) pthread_sigmask(SIG_BLOCK,&set,NULL);
for (;;)
@@ -3790,6 +3848,7 @@ SHOW_VAR com_status_vars[]= {
{"alter_server", STMT_STATUS(SQLCOM_ALTER_SERVER)},
{"alter_table", STMT_STATUS(SQLCOM_ALTER_TABLE)},
{"alter_tablespace", STMT_STATUS(SQLCOM_ALTER_TABLESPACE)},
+ {"alter_user", STMT_STATUS(SQLCOM_ALTER_USER)},
{"analyze", STMT_STATUS(SQLCOM_ANALYZE)},
{"assign_to_keycache", STMT_STATUS(SQLCOM_ASSIGN_TO_KEYCACHE)},
{"begin", STMT_STATUS(SQLCOM_BEGIN)},
@@ -3846,6 +3905,7 @@ SHOW_VAR com_status_vars[]= {
{"kill", STMT_STATUS(SQLCOM_KILL)},
{"load", STMT_STATUS(SQLCOM_LOAD)},
{"lock_tables", STMT_STATUS(SQLCOM_LOCK_TABLES)},
+ {"multi", COM_STATUS(com_multi)},
{"optimize", STMT_STATUS(SQLCOM_OPTIMIZE)},
{"preload_keys", STMT_STATUS(SQLCOM_PRELOAD_KEYS)},
{"prepare_sql", STMT_STATUS(SQLCOM_PREPARE)},
@@ -3879,6 +3939,7 @@ SHOW_VAR com_status_vars[]= {
{"show_create_proc", STMT_STATUS(SQLCOM_SHOW_CREATE_PROC)},
{"show_create_table", STMT_STATUS(SQLCOM_SHOW_CREATE)},
{"show_create_trigger", STMT_STATUS(SQLCOM_SHOW_CREATE_TRIGGER)},
+ {"show_create_user", STMT_STATUS(SQLCOM_SHOW_CREATE_USER)},
{"show_databases", STMT_STATUS(SQLCOM_SHOW_DATABASES)},
{"show_engine_logs", STMT_STATUS(SQLCOM_SHOW_ENGINE_LOGS)},
{"show_engine_mutex", STMT_STATUS(SQLCOM_SHOW_ENGINE_MUTEX)},
@@ -4045,7 +4106,8 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
(longlong) thd->status_var.local_memory_used,
size));
thd->status_var.local_memory_used+= size;
- DBUG_ASSERT((longlong) thd->status_var.local_memory_used >= 0);
+ DBUG_ASSERT((longlong) thd->status_var.local_memory_used >= 0 ||
+ !debug_assert_on_not_freed_memory);
}
}
}
@@ -4124,7 +4186,7 @@ static int init_common_variables()
sf_malloc_dbug_id= mariadb_dbug_id;
#endif
- max_system_variables.pseudo_thread_id= (ulong)~0;
+ max_system_variables.pseudo_thread_id= ~(my_thread_id) 0;
server_start_time= flush_status_time= my_time(0);
global_rpl_filter= new Rpl_filter;
@@ -4277,7 +4339,7 @@ static int init_common_variables()
of SQLCOM_ constants.
*/
compile_time_assert(sizeof(com_status_vars)/sizeof(com_status_vars[0]) - 1 ==
- SQLCOM_END + 10);
+ SQLCOM_END + 11);
#endif
if (get_options(&remaining_argc, &remaining_argv))
@@ -4637,6 +4699,7 @@ static int init_thread_environment()
DBUG_ENTER("init_thread_environment");
mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_start_thread, &LOCK_start_thread, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_show_status, &LOCK_show_status, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_delayed_insert,
@@ -4691,7 +4754,6 @@ static int init_thread_environment()
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
CRYPTO_set_dynlock_lock_callback(openssl_lock);
CRYPTO_set_locking_callback(openssl_lock_function);
- CRYPTO_set_id_callback(openssl_id_function);
#endif
#endif
mysql_rwlock_init(key_rwlock_LOCK_sys_init_connect, &LOCK_sys_init_connect);
@@ -4699,6 +4761,7 @@ static int init_thread_environment()
mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
mysql_cond_init(key_COND_thread_count, &COND_thread_count, NULL);
mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
+ mysql_cond_init(key_COND_start_thread, &COND_start_thread, NULL);
mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
#ifdef HAVE_REPLICATION
mysql_mutex_init(key_LOCK_rpl_status, &LOCK_rpl_status, MY_MUTEX_INIT_FAST);
@@ -4727,12 +4790,6 @@ static int init_thread_environment()
#if defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)
-static unsigned long openssl_id_function()
-{
- return (unsigned long) pthread_self();
-}
-
-
static openssl_lock_t *openssl_dynlock_create(const char *file, int line)
{
openssl_lock_t *lock= new openssl_lock_t;
@@ -5235,49 +5292,6 @@ static int init_server_components()
}
plugins_are_initialized= TRUE; /* Don't separate from init function */
-#ifdef WITH_WSREP
- /* Wait for wsrep threads to get created. */
- if (wsrep_creating_startup_threads == 1) {
- mysql_mutex_lock(&LOCK_thread_count);
- while (wsrep_running_threads < 2)
- {
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- }
-
- /* Now is the time to initialize threads for queries. */
- THD *tmp;
- I_List_iterator<THD> it(threads);
- while ((tmp= it++))
- {
- if (tmp->wsrep_applier == true)
- {
- /*
- Save/restore server_status and variables.option_bits and they get
- altered during init_for_queries().
- */
- unsigned int server_status_saved= tmp->server_status;
- ulonglong option_bits_saved= tmp->variables.option_bits;
-
- /*
- Set THR_THD to temporarily point to this THD to register all the
- variables that allocates memory for this THD.
- */
- THD *current_thd_saved= current_thd;
- set_current_thd(tmp);
-
- tmp->init_for_queries();
-
- /* Restore current_thd. */
- set_current_thd(current_thd_saved);
-
- tmp->server_status= server_status_saved;
- tmp->variables.option_bits= option_bits_saved;
- }
- }
- mysql_mutex_unlock(&LOCK_thread_count);
- }
-#endif
-
/* we do want to exit if there are any other unknown options */
if (remaining_argc > 1)
{
@@ -5508,7 +5522,7 @@ static void handle_connections_methods()
unireg_abort(1); // Will not return
}
- mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_start_thread);
mysql_cond_init(key_COND_handler_count, &COND_handler_count, NULL);
handler_count=0;
if (hPipe != INVALID_HANDLE_VALUE)
@@ -5551,17 +5565,17 @@ static void handle_connections_methods()
#endif
while (handler_count > 0)
- mysql_cond_wait(&COND_handler_count, &LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_wait(&COND_handler_count, &LOCK_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
DBUG_VOID_RETURN;
}
void decrement_handler_count()
{
- mysql_mutex_lock(&LOCK_thread_count);
- handler_count--;
- mysql_cond_signal(&COND_handler_count);
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_start_thread);
+ if (--handler_count == 0)
+ mysql_cond_signal(&COND_handler_count);
+ mysql_mutex_unlock(&LOCK_start_thread);
my_thread_end();
}
#else
@@ -5925,6 +5939,9 @@ int mysqld_main(int argc, char **argv)
if (Events::init((THD*) 0, opt_noacl || opt_bootstrap))
unireg_abort(1);
+ /* It's now safe to use thread specific memory */
+ mysqld_server_initialized= 1;
+
if (WSREP_ON)
{
if (opt_bootstrap)
@@ -5965,9 +5982,6 @@ int mysqld_main(int argc, char **argv)
}
}
- /* It's now safe to use thread specific memory */
- mysqld_server_initialized= 1;
-
create_shutdown_thread();
start_handle_manager();
@@ -6028,18 +6042,10 @@ int mysqld_main(int argc, char **argv)
DBUG_PRINT("quit",("Exiting main thread"));
#ifndef __WIN__
-#ifdef EXTRA_DEBUG2
- sql_print_error("Before Lock_thread_count");
-#endif
- WSREP_DEBUG("Before Lock_thread_count");
- mysql_mutex_lock(&LOCK_thread_count);
- DBUG_PRINT("quit", ("Got thread_count mutex"));
+ mysql_mutex_lock(&LOCK_start_thread);
select_thread_in_use=0; // For close_connections
- mysql_mutex_unlock(&LOCK_thread_count);
- mysql_cond_broadcast(&COND_thread_count);
-#ifdef EXTRA_DEBUG2
- sql_print_error("After lock_thread_count");
-#endif
+ mysql_cond_broadcast(&COND_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
#ifdef HAVE_PSI_THREAD_INTERFACE
@@ -6066,6 +6072,9 @@ int mysqld_main(int argc, char **argv)
CloseHandle(hEventShutdown);
}
#endif
+#if (defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)) && !defined(EMBEDDED_LIBRARY)
+ ERR_remove_state(0);
+#endif
mysqld_exit(0);
return 0;
}
@@ -6304,7 +6313,7 @@ static void bootstrap(MYSQL_FILE *file)
my_net_init(&thd->net,(st_vio*) 0, (void*) 0, MYF(0));
thd->max_client_packet_length= thd->net.max_packet;
thd->security_ctx->master_access= ~(ulong)0;
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
thread_count++; // Safe as only one thread running
in_bootstrap= TRUE;
@@ -6324,10 +6333,7 @@ static void bootstrap(MYSQL_FILE *file)
/* Wait for thread to die */
mysql_mutex_lock(&LOCK_thread_count);
while (in_bootstrap)
- {
mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
- }
mysql_mutex_unlock(&LOCK_thread_count);
#else
thd->mysql= 0;
@@ -6357,7 +6363,7 @@ static bool read_init_file(char *file_name)
*/
void inc_thread_created(void)
{
- thread_created++;
+ statistic_increment(thread_created, &LOCK_status);
}
#ifndef EMBEDDED_LIBRARY
@@ -6368,18 +6374,12 @@ void inc_thread_created(void)
NOTES
This is only used for debugging, when starting mysqld with
--thread-handling=no-threads or --one-thread
-
- When we enter this function, LOCK_thread_count is hold!
*/
-void handle_connection_in_main_thread(THD *thd)
+void handle_connection_in_main_thread(CONNECT *connect)
{
- mysql_mutex_assert_owner(&LOCK_thread_count);
- thread_cache_size=0; // Safety
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
- thd->start_utime= microsecond_interval_timer();
- do_handle_one_connection(thd);
+ thread_cache_size= 0; // Safety
+ do_handle_one_connection(connect);
}
@@ -6387,10 +6387,11 @@ void handle_connection_in_main_thread(THD *thd)
Scheduler that uses one thread per connection
*/
-void create_thread_to_handle_connection(THD *thd)
+void create_thread_to_handle_connection(CONNECT *connect)
{
+ char error_message_buff[MYSQL_ERRMSG_SIZE];
+ int error;
DBUG_ENTER("create_thread_to_handle_connection");
- mysql_mutex_assert_owner(&LOCK_thread_count);
/* Check if we can get thread from the cache */
if (cached_thread_count > wake_thread)
@@ -6399,9 +6400,8 @@ void create_thread_to_handle_connection(THD *thd)
/* Recheck condition when we have the lock */
if (cached_thread_count > wake_thread)
{
- mysql_mutex_unlock(&LOCK_thread_count);
/* Get thread from cache */
- thread_cache.push_back(thd);
+ thread_cache.push_back(connect);
wake_thread++;
mysql_cond_signal(&COND_thread_cache);
mysql_mutex_unlock(&LOCK_thread_cache);
@@ -6411,46 +6411,33 @@ void create_thread_to_handle_connection(THD *thd)
mysql_mutex_unlock(&LOCK_thread_cache);
}
- char error_message_buff[MYSQL_ERRMSG_SIZE];
/* Create new thread to handle connection */
- int error;
- thread_created++;
- threads.append(thd);
- DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
- thd->prior_thr_create_utime= microsecond_interval_timer();
+ inc_thread_created();
+ DBUG_PRINT("info",(("creating thread %lu"), (ulong) connect->thread_id));
+ connect->prior_thr_create_utime= microsecond_interval_timer();
+
if ((error= mysql_thread_create(key_thread_one_connection,
- &thd->real_id, &connection_attrib,
+ &connect->real_id, &connection_attrib,
handle_one_connection,
- (void*) thd)))
+ (void*) connect)))
{
/* purecov: begin inspected */
DBUG_PRINT("error",
("Can't create thread to handle request (error %d)",
error));
- thd->killed= KILL_CONNECTION; // Safety
- mysql_mutex_unlock(&LOCK_thread_count);
-
- mysql_mutex_lock(&LOCK_connection_count);
- (*thd->scheduler->connection_count)--;
- mysql_mutex_unlock(&LOCK_connection_count);
-
+ dec_connection_count(connect->scheduler);
statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
- /* Can't use my_error() since store_globals has not been called. */
my_snprintf(error_message_buff, sizeof(error_message_buff),
- ER_THD(thd, ER_CANT_CREATE_THREAD), error);
- net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
- close_connection(thd, ER_OUT_OF_RESOURCES);
-
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
- mysql_mutex_unlock(&LOCK_thread_count);
- delete thd;
- thread_safe_decrement32(&thread_count);
- return;
+ ER_DEFAULT(ER_CANT_CREATE_THREAD), error);
+ connect->close_with_error(ER_CANT_CREATE_THREAD,
+ error_message_buff,
+ ER_OUT_OF_RESOURCES);
+ /* thread_count was incremented in create_new_thread() */
+ dec_thread_count();
+ DBUG_VOID_RETURN;
/* purecov: end */
}
- mysql_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("info",("Thread created"));
DBUG_VOID_RETURN;
}
@@ -6469,7 +6456,7 @@ void create_thread_to_handle_connection(THD *thd)
@param[in,out] thd Thread handle of future thread.
*/
-static void create_new_thread(THD *thd)
+static void create_new_thread(CONNECT *connect)
{
DBUG_ENTER("create_new_thread");
@@ -6480,20 +6467,19 @@ static void create_new_thread(THD *thd)
mysql_mutex_lock(&LOCK_connection_count);
- if (*thd->scheduler->connection_count >=
- *thd->scheduler->max_connections + 1|| abort_loop)
+ if (*connect->scheduler->connection_count >=
+ *connect->scheduler->max_connections + 1|| abort_loop)
{
- mysql_mutex_unlock(&LOCK_connection_count);
-
DBUG_PRINT("error",("Too many connections"));
- close_connection(thd, ER_CON_COUNT_ERROR);
+
+ mysql_mutex_unlock(&LOCK_connection_count);
statistic_increment(denied_connections, &LOCK_status);
- delete thd;
statistic_increment(connection_errors_max_connection, &LOCK_status);
+ connect->close_with_error(0, NullS, ER_CON_COUNT_ERROR);
DBUG_VOID_RETURN;
}
- ++*thd->scheduler->connection_count;
+ ++*connect->scheduler->connection_count;
if (connection_count + extra_connection_count > max_used_connections)
max_used_connections= connection_count + extra_connection_count;
@@ -6501,17 +6487,15 @@ static void create_new_thread(THD *thd)
mysql_mutex_unlock(&LOCK_connection_count);
thread_safe_increment32(&thread_count);
+ connect->thread_count_incremented= 1;
- /* Start a new thread to handle connection. */
- mysql_mutex_lock(&LOCK_thread_count);
/*
The initialization of thread_id is done in create_embedded_thd() for
the embedded library.
TODO: refactor this to avoid code duplication there
*/
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
-
- MYSQL_CALLBACK(thd->scheduler, add_connection, (thd));
+ connect->thread_id= next_thread_id();
+ connect->scheduler->add_connection(connect);
DBUG_VOID_RETURN;
}
@@ -6546,13 +6530,12 @@ void handle_connections_sockets()
MYSQL_SOCKET sock= mysql_socket_invalid();
MYSQL_SOCKET new_sock= mysql_socket_invalid();
uint error_count=0;
- THD *thd;
+ CONNECT *connect;
struct sockaddr_storage cAddr;
int ip_flags __attribute__((unused))=0;
int socket_flags __attribute__((unused))= 0;
int extra_ip_flags __attribute__((unused))=0;
int flags=0,retval;
- st_vio *vio_tmp;
bool is_unix_sock;
#ifdef HAVE_POLL
int socket_count= 0;
@@ -6751,58 +6734,43 @@ void handle_connections_sockets()
}
#endif /* HAVE_LIBWRAP */
- /*
- ** Don't allow too many connections
- */
+ DBUG_PRINT("info", ("Creating CONNECT for new connection"));
- DBUG_PRINT("info", ("Creating THD for new connection"));
- if (!(thd= new THD))
+ if ((connect= new CONNECT()))
{
- (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) mysql_socket_close(new_sock);
- statistic_increment(connection_errors_internal, &LOCK_status);
- continue;
- }
- /* Set to get io buffers to be part of THD */
- set_current_thd(thd);
-
- is_unix_sock= (mysql_socket_getfd(sock) ==
- mysql_socket_getfd(unix_sock));
+ is_unix_sock= (mysql_socket_getfd(sock) ==
+ mysql_socket_getfd(unix_sock));
- if (!(vio_tmp=
- mysql_socket_vio_new(new_sock,
- is_unix_sock ? VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
- is_unix_sock ? VIO_LOCALHOST: 0)) ||
- my_net_init(&thd->net, vio_tmp, thd, MYF(MY_THREAD_SPECIFIC)))
- {
- /*
- Only delete the temporary vio if we didn't already attach it to the
- NET object. The destructor in THD will delete any initialized net
- structure.
- */
- if (vio_tmp && thd->net.vio != vio_tmp)
- vio_delete(vio_tmp);
- else
+ if (!(connect->vio=
+ mysql_socket_vio_new(new_sock,
+ is_unix_sock ? VIO_TYPE_SOCKET :
+ VIO_TYPE_TCPIP,
+ is_unix_sock ? VIO_LOCALHOST: 0)))
{
- (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) mysql_socket_close(new_sock);
+ delete connect;
+ connect= 0; // Error handling below
}
- delete thd;
+ }
+
+ if (!connect)
+ {
+ /* Connect failure */
+ (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
+ (void) mysql_socket_close(new_sock);
+ statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
- init_net_server_extension(thd);
if (is_unix_sock)
- thd->security_ctx->host=(char*) my_localhost;
+ connect->host= my_localhost;
if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
{
- thd->extra_port= 1;
- thd->scheduler= extra_thread_scheduler;
+ connect->extra_port= 1;
+ connect->scheduler= extra_thread_scheduler;
}
- create_new_thread(thd);
- set_current_thd(0);
+ create_new_thread(connect);
}
sd_notify(0, "STOPPING=1\n"
"STATUS=Shutdown in progress");
@@ -6823,7 +6791,6 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
{
HANDLE hConnectedPipe;
OVERLAPPED connectOverlapped= {0};
- THD *thd;
my_thread_init();
DBUG_ENTER("handle_connections_namedpipes");
connectOverlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -6891,25 +6858,19 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
hPipe=hConnectedPipe;
continue; // We have to try again
}
-
- if (!(thd = new THD))
+ CONNECT *connect;
+ if (!(connect= new CONNECT) ||
+ !(connect->vio= vio_new_win32pipe(hConnectedPipe)))
{
DisconnectNamedPipe(hConnectedPipe);
CloseHandle(hConnectedPipe);
+ delete connect;
+ statistic_increment(aborted_connects,&LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
- set_current_thd(thd);
- if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
- my_net_init(&thd->net, thd->net.vio, thd, MYF(MY_THREAD_SPECIFIC)))
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- delete thd;
- continue;
- }
- /* Host is unknown */
- thd->security_ctx->host= my_strdup(my_localhost, MYF(0));
- create_new_thread(thd);
- set_current_thd(0);
+ connect->host= my_localhost;
+ create_new_thread(connect);
}
CloseHandle(connectOverlapped.hEvent);
DBUG_LEAVE;
@@ -6946,7 +6907,8 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
/*
get enough space base-name + '_' + longest suffix we might ever send
*/
- if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, MYF(MY_FAE))))
+ if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L,
+ MYF(MY_FAE))))
goto error;
if (my_security_attr_create(&sa_event, &errmsg,
@@ -7012,7 +6974,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
HANDLE event_server_wrote= 0;
HANDLE event_server_read= 0;
HANDLE event_conn_closed= 0;
- THD *thd= 0;
+ CONNECT *connect= 0;
p= int10_to_str(connect_number, connect_number_char, 10);
/*
@@ -7074,8 +7036,13 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
}
if (abort_loop)
goto errorconn;
- if (!(thd= new THD))
+
+ if (!(connect= new CONNECT))
+ {
+ errmsg= "Could not create CONNECT object";
goto errorconn;
+ }
+
/* Send number of connection to client */
int4store(handle_connect_map, connect_number);
if (!SetEvent(event_connect_answer))
@@ -7089,24 +7056,20 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
errmsg= "Could not set client to read mode";
goto errorconn;
}
- set_current_thd(thd);
- if (!(thd->net.vio= vio_new_win32shared_memory(handle_client_file_map,
+ if (!(connect->vio= vio_new_win32shared_memory(handle_client_file_map,
handle_client_map,
event_client_wrote,
event_client_read,
event_server_wrote,
event_server_read,
- event_conn_closed)) ||
- my_net_init(&thd->net, thd->net.vio, thd, MYF(MY_THREAD_SPECIFIC)))
+ event_conn_closed)))
{
- close_connection(thd, ER_OUT_OF_RESOURCES);
- errmsg= 0;
+ errmsg= "Could not create VIO object";
goto errorconn;
}
- thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); /* Host is unknown */
- create_new_thread(thd);
+ connect->host= my_localhost; /* Host is unknown */
+ create_new_thread(connect);
connect_number++;
- set_current_thd(thd);
continue;
errorconn:
@@ -7132,9 +7095,11 @@ errorconn:
CloseHandle(event_client_read);
if (event_conn_closed)
CloseHandle(event_conn_closed);
- delete thd;
+
+ delete connect;
+ statistic_increment(aborted_connects,&LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
}
- set_current_thd(0);
/* End shared memory handling */
error:
@@ -7355,6 +7320,13 @@ struct my_option my_long_options[]=
&opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
0},
#endif /* HAVE_REPLICATION */
+#ifndef DBUG_OFF
+ {"debug-assert-on-not-freed-memory", 0,
+ "Assert if we found problems with memory allocation",
+ &debug_assert_on_not_freed_memory,
+ &debug_assert_on_not_freed_memory, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
+ 0},
+#endif /* DBUG_OFF */
/* default-storage-engine should have "MyISAM" as def_value. Instead
of initializing it here it is done in init_common_variables() due
to a compiler bug in Sun Studio compiler. */
@@ -7451,14 +7423,6 @@ struct my_option my_long_options[]=
"Don't log extra information to update and slow-query logs.",
&opt_short_log_format, &opt_short_log_format,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-admin-statements", 0,
- "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to "
- "the slow log if it is open.", &opt_log_slow_admin_statements,
- &opt_log_slow_admin_statements, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-slave-statements", 0,
- "Log slow statements executed by slave thread to the slow log if it is open.",
- &opt_log_slow_slave_statements, &opt_log_slow_slave_statements,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"log-tc", 0,
"Path to transaction coordinator log (used for transactions that affect "
"more than one storage engine, when binary log is disabled).",
@@ -7694,7 +7658,6 @@ struct my_option my_long_options[]=
MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-allow-batching"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-period"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-group"), // HAVE_REPLICATION
- MYSQL_SUGGEST_ANALOG_OPTION("slave-parallel-workers", "--slave-parallel-threads"), // HAVE_REPLICATION
MYSQL_SUGGEST_ANALOG_OPTION("slave-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
MYSQL_TO_BE_IMPLEMENTED_OPTION("disconnect-on-expired-password"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
@@ -7774,8 +7737,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff,
get_master_info(&thd->variables.default_master_connection,
Sql_condition::WARN_LEVEL_NOTE);
if (mi)
- tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT &&
- mi->rli.slave_running);
+ tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING &&
+ mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN);
}
mysql_mutex_unlock(&LOCK_active_mi);
if (mi)
@@ -7786,6 +7749,38 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff,
}
+/* How many slaves are connected to this master */
+
+static int show_slaves_connected(THD *thd, SHOW_VAR *var, char *buff)
+{
+
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_slave_list);
+
+ *((longlong *)buff)= slave_list.records;
+
+ mysql_mutex_unlock(&LOCK_slave_list);
+ return 0;
+}
+
+
+/* How many masters this slave is connected to */
+
+
+static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_active_mi);
+
+ *((longlong *)buff)= master_info_index->any_slave_sql_running();
+
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return 0;
+}
+
+
static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope)
{
@@ -8395,7 +8390,7 @@ SHOW_VAR status_vars[]= {
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
{"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
- {"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
+ {"Connections", (char*) &global_thread_id, SHOW_LONG_NOFLUSH},
{"Connection_errors_accept", (char*) &connection_errors_accept, SHOW_LONG},
{"Connection_errors_internal", (char*) &connection_errors_internal, SHOW_LONG},
{"Connection_errors_max_connections", (char*) &connection_errors_max_connection, SHOW_LONG},
@@ -8494,6 +8489,9 @@ SHOW_VAR status_vars[]= {
{"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS},
{"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_INT},
#ifdef HAVE_REPLICATION
+ {"Slaves_connected", (char*) &show_slaves_connected, SHOW_SIMPLE_FUNC },
+ {"Slaves_running", (char*) &show_slaves_running, SHOW_SIMPLE_FUNC },
+ {"Slave_connections", (char*) offsetof(STATUS_VAR, com_register_slave), SHOW_LONG_STATUS},
{"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC},
{"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_SIMPLE_FUNC},
{"Slave_retried_transactions",(char*)&slave_retried_transactions, SHOW_LONG},
@@ -8772,8 +8770,9 @@ static int mysql_init_variables(void)
what_to_log= ~ (1L << (uint) COM_TIME);
denied_connections= 0;
executed_events= 0;
- global_query_id= thread_id= 1L;
- strmov(server_version, MYSQL_SERVER_VERSION);
+ global_query_id= 1;
+ global_thread_id= 1;
+ strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
threads.empty();
thread_cache.empty();
key_caches.empty();
@@ -9693,17 +9692,20 @@ void set_server_version(void)
{
if (!IS_SYSVAR_AUTOSIZE(&server_version_ptr))
return;
- char *end= strxmov(server_version, MYSQL_SERVER_VERSION,
- MYSQL_SERVER_SUFFIX_STR, NullS);
+ char *version_end= server_version+sizeof(server_version)-1;
+ char *end= strxnmov(server_version, sizeof(server_version)-1,
+ MYSQL_SERVER_VERSION,
+ MYSQL_SERVER_SUFFIX_STR, NullS);
#ifdef EMBEDDED_LIBRARY
- end= strmov(end, "-embedded");
+ end= strnmov(end, "-embedded", (version_end-end));
#endif
#ifndef DBUG_OFF
if (!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug"))
- end= strmov(end, "-debug");
+ end= strnmov(end, "-debug", (version_end-end));
#endif
if (opt_log || global_system_variables.sql_log_slow || opt_bin_log)
- strmov(end, "-log"); // This may slow down system
+ strnmov(end, "-log", (version_end-end)); // This may slow down system
+ *end= 0;
}
@@ -10009,7 +10011,7 @@ void refresh_status(THD *thd)
Set max_used_connections to the number of currently open
connections. This is not perfect, but status data is not exact anyway.
*/
- max_used_connections= thread_count-delayed_insert_threads;
+ max_used_connections= connection_count + extra_connection_count;
}
#ifdef HAVE_PSI_INTERFACE
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 4e6f2fcd29d..ef4a0d6a47a 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -30,6 +30,7 @@
#include "my_rdtsc.h"
class THD;
+class CONNECT;
struct handlerton;
class Time_zone;
@@ -81,8 +82,8 @@ enum enum_slave_parallel_mode {
/* Function prototypes */
void kill_mysql(THD *thd= 0);
void close_connection(THD *thd, uint sql_errno= 0);
-void handle_connection_in_main_thread(THD *thd);
-void create_thread_to_handle_connection(THD *thd);
+void handle_connection_in_main_thread(CONNECT *thd);
+void create_thread_to_handle_connection(CONNECT *connect);
void delete_running_thd(THD *thd);
void signal_thd_deleted();
void unlink_thd(THD *thd);
@@ -90,6 +91,8 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
void flush_thread_cache();
void refresh_status(THD *thd);
bool is_secure_file_path(char *path);
+void dec_connection_count(scheduler_functions *scheduler);
+extern void init_net_server_extension(THD *thd);
extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info;
extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ;
@@ -116,8 +119,9 @@ extern bool opt_disable_networking, opt_skip_show_db;
extern bool opt_skip_name_resolve;
extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
+extern my_bool debug_assert_on_not_freed_memory;
extern bool volatile abort_loop;
-extern bool in_bootstrap;
+extern bool volatile in_bootstrap;
extern uint connection_count;
extern my_bool opt_safe_user_create;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
@@ -177,7 +181,7 @@ extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
extern const double log_10[309];
extern ulonglong keybuff_size;
extern ulonglong thd_startup_options;
-extern ulong thread_id;
+extern my_thread_id global_thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong binlog_stmt_cache_use, binlog_stmt_cache_disk_use;
extern ulong aborted_threads,aborted_connects;
@@ -292,6 +296,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
key_rpl_group_info_sleep_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
+ key_LOCK_start_thread,
key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
extern PSI_mutex_key key_RELAYLOG_LOCK_index;
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
@@ -323,6 +328,7 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
+ key_COND_start_thread,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
key_COND_wait_commit;
@@ -558,6 +564,7 @@ extern mysql_mutex_t
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
LOCK_slave_init;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
+extern mysql_mutex_t LOCK_start_thread;
#ifdef HAVE_OPENSSL
extern char* des_key_file;
extern mysql_mutex_t LOCK_des_key_file;
@@ -566,7 +573,7 @@ extern mysql_mutex_t LOCK_server_started;
extern mysql_cond_t COND_server_started;
extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_system_variables_hash;
-extern mysql_cond_t COND_thread_count;
+extern mysql_cond_t COND_thread_count, COND_start_thread;
extern mysql_cond_t COND_manager;
extern mysql_cond_t COND_slave_init;
extern int32 thread_running;
@@ -660,15 +667,16 @@ enum enum_query_type
/// If NULLIF(a,b) should print itself as
/// CASE WHEN a_for_comparison=b THEN NULL ELSE a_for_return_value END
/// when "a" was replaced to two different items
- /// (e.g. by equal fields propagation in optimize_cond()).
- /// The default behaviour is to print as NULLIF(a_for_return, b)
- /// which should be Ok for SHOW CREATE {VIEW|PROCEDURE|FUNCTION}
- /// as they are not affected by WHERE optimization.
- QT_ITEM_FUNC_NULLIF_TO_CASE= (1 <<6),
+ /// (e.g. by equal fields propagation in optimize_cond())
+ /// or always as NULLIF(a, b).
+ /// The default behaviour is to use CASE syntax when
+ /// a_for_return_value is not the same as a_for_comparison.
+ /// SHOW CREATE {VIEW|PROCEDURE|FUNCTION} and other cases where the
+ /// original representation is required, should set this flag.
+ QT_ITEM_ORIGINAL_FUNC_NULLIF= (1 <<6),
/// This value means focus on readability, not on ability to parse back, etc.
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
- QT_ITEM_FUNC_NULLIF_TO_CASE |
QT_ITEM_IDENT_SKIP_CURRENT_DATABASE |
QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS |
QT_ITEM_SUBSELECT_ID_ONLY,
@@ -677,7 +685,7 @@ enum enum_query_type
/// Be more detailed than QT_EXPLAIN.
/// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE
/// here, as it would give better readable results
- QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET | QT_ITEM_FUNC_NULLIF_TO_CASE
+ QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET
};
@@ -698,6 +706,16 @@ inline query_id_t get_query_id()
return my_atomic_load64_explicit(&global_query_id, MY_MEMORY_ORDER_RELAXED);
}
+/* increment global_thread_id and return it. */
+inline __attribute__((warn_unused_result)) my_thread_id next_thread_id()
+{
+ return my_atomic_add64_explicit(&global_thread_id, 1, MY_MEMORY_ORDER_RELAXED);
+}
+
+#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
+extern "C" my_thread_id next_thread_id_noinline();
+#define next_thread_id() next_thread_id_noinline()
+#endif
/*
TODO: Replace this with an inline function.
@@ -746,7 +764,8 @@ inline void dec_thread_running()
thread_safe_decrement32(&thread_running);
}
-void set_server_version(void);
+extern void set_server_version(void);
+extern void dec_thread_count(void);
#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
extern "C" THD *_current_thd_noinline();
@@ -768,6 +787,7 @@ inline int set_current_thd(THD *thd)
return my_pthread_setspecific_ptr(THR_THD, thd);
}
+
/*
@todo remove, make it static in ha_maria.cc
currently it's needed for sql_select.cc
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index b6da7933bb1..f0284462206 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -34,6 +34,7 @@
HFTODO this must be hidden if we don't want client capabilities in
embedded library
*/
+
#include <my_global.h>
#include <mysql.h>
#include <mysql_com.h>
@@ -107,13 +108,12 @@ extern void query_cache_insert(void *thd, const char *packet, ulong length,
unsigned pkt_nr);
#endif // HAVE_QUERY_CACHE
#define update_statistics(A) A
-#else
-#define update_statistics(A)
-#endif
-
-#ifdef MYSQL_SERVER
+extern my_bool thd_net_is_killed();
/* Additional instrumentation hooks for the server */
#include "mysql_com_server.h"
+#else
+#define update_statistics(A)
+#define thd_net_is_killed() 0
#endif
#define TEST_BLOCKING 8
@@ -121,6 +121,8 @@ extern void query_cache_insert(void *thd, const char *packet, ulong length,
static my_bool net_write_buff(NET *, const uchar *, ulong);
+my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags);
+
/** Init with packet info. */
my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
@@ -129,14 +131,12 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
DBUG_PRINT("enter", ("my_flags: %u", my_flags));
net->vio = vio;
my_net_local_init(net); /* Set some limits */
- if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
- NET_HEADER_SIZE + COMP_HEADER_SIZE +1,
- MYF(MY_WME | my_flags))))
+
+ if (net_allocate_new_packet(net, thd, my_flags))
DBUG_RETURN(1);
- net->buff_end=net->buff+net->max_packet;
+
net->error=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
- net->write_pos=net->read_pos = net->buff;
net->last_error[0]=0;
net->compress=0; net->reading_or_writing=0;
net->where_b = net->remain_in_buf=0;
@@ -165,6 +165,18 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
DBUG_RETURN(0);
}
+my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags)
+{
+ DBUG_ENTER("net_allocate_new_packet");
+ if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
+ NET_HEADER_SIZE + COMP_HEADER_SIZE +1,
+ MYF(MY_WME | my_flags))))
+ DBUG_RETURN(1);
+ net->buff_end=net->buff+net->max_packet;
+ net->write_pos=net->read_pos = net->buff;
+ DBUG_RETURN(0);
+}
+
void net_end(NET *net)
{
@@ -876,6 +888,16 @@ my_real_read(NET *net, size_t *complen,
DBUG_PRINT("info",("vio_read returned %ld errno: %d",
(long) length, vio_errno(net->vio)));
+
+ if (i== 0 && thd_net_is_killed())
+ {
+ len= packet_error;
+ net->error= 0;
+ net->last_errno= ER_CONNECTION_KILLED;
+ MYSQL_SERVER_my_error(net->last_errno, MYF(0));
+ goto end;
+ }
+
#if !defined(__WIN__) && defined(MYSQL_SERVER)
/*
We got an error that there was no data on the socket. We now set up
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 79ac2fe38a7..af027ad6fdc 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -114,12 +114,11 @@
#include "sql_parse.h" // check_stack_overrun
#include "sql_partition.h" // get_part_id_func, PARTITION_ITERATOR,
// struct partition_info, NOT_A_PARTITION_ID
-#include "sql_base.h" // free_io_cache
#include "records.h" // init_read_record, end_read_record
#include <m_ctype.h>
#include "sql_select.h"
#include "sql_statistics.h"
-#include "filesort.h" // filesort_free_buffers
+#include "uniques.h"
#ifndef EXTRA_DEBUG
#define test_rb_tree(A,B) {}
@@ -1154,6 +1153,7 @@ int imerge_list_and_tree(RANGE_OPT_PARAM *param,
SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds,
+ SORT_INFO *filesort,
bool allow_null_cond,
int *error)
{
@@ -1174,13 +1174,16 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
select->head=head;
select->cond= conds;
- if (head->sort.io_cache)
+ if (filesort && my_b_inited(&filesort->io_cache))
{
- select->file= *head->sort.io_cache;
+ /*
+ Hijack the filesort io_cache for make_select
+ SQL_SELECT will be responsible for ensuring that it's properly freed.
+ */
+ select->file= filesort->io_cache;
select->records=(ha_rows) (select->file.end_of_file/
head->file->ref_length);
- my_free(head->sort.io_cache);
- head->sort.io_cache=0;
+ my_b_clear(&filesort->io_cache);
}
DBUG_RETURN(select);
}
@@ -1393,7 +1396,6 @@ QUICK_INDEX_SORT_SELECT::~QUICK_INDEX_SORT_SELECT()
delete pk_quick_select;
/* It's ok to call the next two even if they are already deinitialized */
end_read_record(&read_record);
- free_io_cache(head);
free_root(&alloc,MYF(0));
DBUG_VOID_RETURN;
}
@@ -4336,15 +4338,14 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
Field **field= (ppar->part_fields)? part_info->part_field_array :
part_info->subpart_field_array;
bool in_subpart_fields= FALSE;
- uint max_key_len= 0;
- uint cur_key_len= 0;
+ uint total_key_len= 0;
for (uint part= 0; part < total_parts; part++, key_part++)
{
key_part->key= 0;
key_part->part= part;
key_part->length= (uint16)(*field)->key_length();
key_part->store_length= (uint16)get_partition_field_store_length(*field);
- cur_key_len += key_part->store_length;
+ total_key_len += key_part->store_length;
DBUG_PRINT("info", ("part %u length %u store_length %u", part,
key_part->length, key_part->store_length));
@@ -4370,18 +4371,13 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
{
field= part_info->subpart_field_array;
in_subpart_fields= TRUE;
- max_key_len= cur_key_len;
- cur_key_len= 0;
}
}
range_par->key_parts_end= key_part;
- if (cur_key_len > max_key_len)
- max_key_len= cur_key_len;
-
- max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */
- if (!(range_par->min_key= (uchar*)alloc_root(alloc,max_key_len)) ||
- !(range_par->max_key= (uchar*)alloc_root(alloc,max_key_len)))
+ total_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */
+ if (!(range_par->min_key= (uchar*)alloc_root(alloc,total_key_len)) ||
+ !(range_par->max_key= (uchar*)alloc_root(alloc,total_key_len)))
{
return true;
}
@@ -10680,7 +10676,6 @@ int read_keys_and_merge_scans(THD *thd,
else
{
unique->reset();
- filesort_free_buffers(head, false);
}
DBUG_ASSERT(file->ref_length == unique->get_size());
@@ -10733,7 +10728,7 @@ int read_keys_and_merge_scans(THD *thd,
/*
Ok all rowids are in the Unique now. The next call will initialize
- head->sort structure so it can be used to iterate through the rowids
+ the unique structure so it can be used to iterate through the rowids
sequence.
*/
result= unique->get(head);
@@ -10742,7 +10737,8 @@ int read_keys_and_merge_scans(THD *thd,
*/
if (enabled_keyread)
head->set_keyread(false);
- if (init_read_record(read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE))
+ if (init_read_record(read_record, thd, head, (SQL_SELECT*) 0,
+ &unique->sort, 1 , 1, TRUE))
result= 1;
DBUG_RETURN(result);
@@ -10785,7 +10781,8 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
{
result= HA_ERR_END_OF_FILE;
end_read_record(&read_record);
- free_io_cache(head);
+ // Free things used by sort early. Shouldn't be strictly necessary
+ unique->sort.reset();
/* All rows from Unique have been retrieved, do a clustered PK scan */
if (pk_quick_select)
{
@@ -10820,7 +10817,7 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next()
{
result= HA_ERR_END_OF_FILE;
end_read_record(&read_record);
- free_io_cache(head);
+ unique->sort.reset(); // Free things early
}
DBUG_RETURN(result);
@@ -14621,6 +14618,4 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
}
}
-
#endif /* !DBUG_OFF */
-
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 80f4064a529..6970b87f6d8 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -26,6 +26,8 @@
#include "records.h" /* READ_RECORD */
#include "queues.h" /* QUEUE */
+#include "filesort.h" /* SORT_INFO */
+
/*
It is necessary to include set_var.h instead of item.h because there
are dependencies on include order for set_var.h and item.h. This
@@ -1658,6 +1660,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
ha_rows records);
SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds,
+ SORT_INFO* filesort,
bool allow_null_cond, int *error);
bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index a1c4187f5ca..9140ed11828 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -833,12 +833,14 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
in_subs->sjm_scan_allowed= FALSE;
bool all_are_fields= TRUE;
+ uint32 total_key_length = 0;
for (uint i= 0; i < elements; i++)
{
Item *outer= in_subs->left_expr->element_index(i);
Item *inner= it++;
all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM &&
inner->real_item()->type() == Item::FIELD_ITEM);
+ total_key_length += inner->max_length;
if (outer->cmp_type() != inner->cmp_type())
DBUG_RETURN(FALSE);
switch (outer->cmp_type()) {
@@ -869,6 +871,15 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
}
}
+ /*
+ Make sure that create_tmp_table will not fail due to too long keys.
+ See MDEV-7122. This check is performed inside create_tmp_table also and
+ we must do it so that we know the table has keys created.
+ */
+ if (total_key_length > tmp_table_max_key_length() ||
+ elements > tmp_table_max_key_parts())
+ DBUG_RETURN(FALSE);
+
in_subs->types_allow_materialization= TRUE;
in_subs->sjm_scan_allowed= all_are_fields;
DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed"));
@@ -3936,7 +3947,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
{
/* if we run out of slots or we are not using tempool */
sprintf(path,"%s%lx_%lx_%x", tmp_file_prefix,current_pid,
- thd->thread_id, thd->tmp_table++);
+ (ulong) thd->thread_id, thd->tmp_table++);
}
fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
@@ -4720,8 +4731,6 @@ int clear_sj_tmp_tables(JOIN *join)
{
if ((res= table->file->ha_delete_all_rows()))
return res; /* purecov: inspected */
- free_io_cache(table);
- filesort_free_buffers(table,0);
}
SJ_MATERIALIZATION_INFO *sjm;
@@ -5525,7 +5534,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
outer join has not been optimized yet).
*/
if (outer_join && outer_join->table_count > 0 && // (1)
- outer_join->join_tab) // (2)
+ outer_join->join_tab && // (2)
+ !in_subs->const_item())
{
/*
TODO:
diff --git a/sql/records.cc b/sql/records.cc
index f208c507fbe..e7a4ab836c0 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -29,10 +29,10 @@
#include "records.h"
#include "sql_priv.h"
#include "records.h"
-#include "filesort.h" // filesort_free_buffers
#include "opt_range.h" // SQL_SELECT
#include "sql_class.h" // THD
#include "sql_base.h"
+#include "sql_sort.h" // SORT_ADDON_FIELD
static int rr_quick(READ_RECORD *info);
int rr_sequential(READ_RECORD *info);
@@ -182,26 +182,30 @@ bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
SQL_SELECT *select,
+ SORT_INFO *filesort,
int use_record_cache, bool print_error,
bool disable_rr_cache)
{
IO_CACHE *tempfile;
+ SORT_ADDON_FIELD *addon_field= filesort ? filesort->addon_field : 0;
DBUG_ENTER("init_read_record");
bzero((char*) info,sizeof(*info));
info->thd=thd;
info->table=table;
info->forms= &info->table; /* Only one table */
+ info->addon_field= addon_field;
if ((table->s->tmp_table == INTERNAL_TMP_TABLE ||
table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) &&
- !table->sort.addon_field)
+ !addon_field)
(void) table->file->extra(HA_EXTRA_MMAP);
- if (table->sort.addon_field)
+ if (addon_field)
{
- info->rec_buf= table->sort.addon_buf;
- info->ref_length= table->sort.addon_length;
+ info->rec_buf= (uchar*) filesort->addon_buf.str;
+ info->ref_length= filesort->addon_buf.length;
+ info->unpack= filesort->unpack;
}
else
{
@@ -213,19 +217,20 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
info->print_error=print_error;
info->unlock_row= rr_unlock_row;
info->ignore_not_found_rows= 0;
- table->status=0; /* And it's always found */
+ table->status= 0; /* Rows are always found */
+ tempfile= 0;
if (select && my_b_inited(&select->file))
tempfile= &select->file;
- else
- tempfile= table->sort.io_cache;
- if (tempfile && my_b_inited(tempfile) &&
- !(select && select->quick))
+ else if (filesort && my_b_inited(&filesort->io_cache))
+ tempfile= &filesort->io_cache;
+
+ if (tempfile && !(select && select->quick))
{
DBUG_PRINT("info",("using rr_from_tempfile"));
- info->read_record= (table->sort.addon_field ?
+ info->read_record= (addon_field ?
rr_unpack_from_tempfile : rr_from_tempfile);
- info->io_cache=tempfile;
+ info->io_cache= tempfile;
reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
info->ref_pos=table->file->ref;
if (!table->file->inited)
@@ -233,12 +238,12 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
DBUG_RETURN(1);
/*
- table->sort.addon_field is checked because if we use addon fields,
+ addon_field is checked because if we use addon fields,
it doesn't make sense to use cache - we don't read from the table
- and table->sort.io_cache is read sequentially
+ and filesort->io_cache is read sequentially
*/
if (!disable_rr_cache &&
- !table->sort.addon_field &&
+ !addon_field &&
thd->variables.read_rnd_buff_size &&
!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
(table->db_stat & HA_READ_ONLY ||
@@ -263,15 +268,15 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
DBUG_PRINT("info",("using rr_quick"));
info->read_record=rr_quick;
}
- else if (table->sort.record_pointers)
+ else if (filesort && filesort->record_pointers)
{
DBUG_PRINT("info",("using record_pointers"));
if (table->file->ha_rnd_init_with_error(0))
DBUG_RETURN(1);
- info->cache_pos=table->sort.record_pointers;
- info->cache_end=info->cache_pos+
- table->sort.found_records*info->ref_length;
- info->read_record= (table->sort.addon_field ?
+ info->cache_pos= filesort->record_pointers;
+ info->cache_end= (info->cache_pos+
+ filesort->return_rows * info->ref_length);
+ info->read_record= (addon_field ?
rr_unpack_from_buffer : rr_from_pointers);
}
else
@@ -288,7 +293,7 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
(use_record_cache < 0 &&
!(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE))))
(void) table->file->extra_opt(HA_EXTRA_CACHE,
- thd->variables.read_buff_size);
+ thd->variables.read_buff_size);
}
/* Condition pushdown to storage engine */
if ((table->file->ha_table_flags() & HA_CAN_TABLE_CONDITION_PUSHDOWN) &&
@@ -311,7 +316,6 @@ void end_read_record(READ_RECORD *info)
}
if (info->table)
{
- filesort_free_buffers(info->table,0);
if (info->table->is_created())
(void) info->table->file->extra(HA_EXTRA_NO_CACHE);
if (info->read_record != rr_quick) // otherwise quick_range does it
@@ -525,9 +529,8 @@ static int rr_unpack_from_tempfile(READ_RECORD *info)
{
if (my_b_read(info->io_cache, info->rec_buf, info->ref_length))
return -1;
- TABLE *table= info->table;
- (*table->sort.unpack)(table->sort.addon_field, info->rec_buf,
- info->rec_buf + info->ref_length);
+ (*info->unpack)(info->addon_field, info->rec_buf,
+ info->rec_buf + info->ref_length);
return 0;
}
@@ -577,11 +580,9 @@ static int rr_unpack_from_buffer(READ_RECORD *info)
{
if (info->cache_pos == info->cache_end)
return -1; /* End of buffer */
- TABLE *table= info->table;
- (*table->sort.unpack)(table->sort.addon_field, info->cache_pos,
- info->cache_end);
+ (*info->unpack)(info->addon_field, info->cache_pos,
+ info->cache_end);
info->cache_pos+= info->ref_length;
-
return 0;
}
/* cacheing of records from a database */
diff --git a/sql/records.h b/sql/records.h
index a3f0b5eb084..1928acfd4f4 100644
--- a/sql/records.h
+++ b/sql/records.h
@@ -25,6 +25,7 @@ struct TABLE;
class THD;
class SQL_SELECT;
class Copy_field;
+class SORT_INFO;
/**
A context for reading through a single table using a chosen access method:
@@ -60,8 +61,10 @@ struct READ_RECORD
uchar *record;
uchar *rec_buf; /* to read field values after filesort */
uchar *cache,*cache_pos,*cache_end,*read_positions;
+ struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
struct st_io_cache *io_cache;
bool print_error, ignore_not_found_rows;
+ void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *);
/*
SJ-Materialization runtime may need to read fields from the materialized
@@ -74,7 +77,8 @@ public:
};
bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
- SQL_SELECT *select, int use_record_cache,
+ SQL_SELECT *select, SORT_INFO *sort,
+ int use_record_cache,
bool print_errors, bool disable_rr_cache);
bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx, bool reverse);
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 47c0f4e3fb7..f54ef2b0081 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -567,7 +567,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
Updates in slave state table should not be appended to galera transaction
writeset.
*/
- thd->wsrep_skip_append_keys= true;
+ thd->wsrep_ignore_table= true;
#endif
if (!in_transaction)
@@ -685,7 +685,7 @@ IF_DBUG(dbug_break:, )
end:
#ifdef WITH_WSREP
- thd->wsrep_skip_append_keys= false;
+ thd->wsrep_ignore_table= false;
#endif
if (table_opened)
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 216fbde0177..df721342d1d 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -35,7 +35,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
rli(is_slave_recovery), port(MYSQL_PORT),
checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
- slave_running(0), slave_run_id(0), clock_diff_with_master(0),
+ slave_running(MYSQL_SLAVE_NOT_RUN), slave_run_id(0),
+ clock_diff_with_master(0),
sync_counter(0), heartbeat_period(0), received_heartbeats(0),
master_id(0), prev_master_id(0),
using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
@@ -1396,23 +1397,24 @@ bool Master_info_index::give_error_if_slave_running()
The LOCK_active_mi must be held while calling this function.
@return
- TRUE If some slave SQL thread is running.
- FALSE No slave SQL thread is running
+ 0 No Slave SQL thread is running
+ # Number of slave SQL thread running
*/
-bool Master_info_index::any_slave_sql_running()
+uint Master_info_index::any_slave_sql_running()
{
+ uint count= 0;
DBUG_ENTER("any_slave_sql_running");
if (!this) // master_info_index is set to NULL on server shutdown
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(count);
for (uint i= 0; i< master_info_hash.records; ++i)
{
Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i);
if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
- DBUG_RETURN(TRUE);
+ count++;
}
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(count);
}
@@ -1442,7 +1444,7 @@ bool Master_info_index::start_all_slaves(THD *thd)
Try to start all slaves that are configured (host is defined)
and are not already running
*/
- if ((mi->slave_running != MYSQL_SLAVE_RUN_CONNECT ||
+ if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN ||
!mi->rli.slave_running) && *mi->host)
{
if ((error= start_slave(thd, mi, 1)))
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 69d602c1dcb..9365c065ea9 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -345,7 +345,7 @@ public:
Master_info *get_master_info(const LEX_STRING *connection_name,
Sql_condition::enum_warning_level warning);
bool give_error_if_slave_running();
- bool any_slave_sql_running();
+ uint any_slave_sql_running();
bool start_all_slaves(THD *thd);
bool stop_all_slaves(THD *thd);
};
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index b4e7b1a89dd..acdedd6e0a0 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -705,7 +705,7 @@ do_retry:
thd->clear_error();
/*
- If we retry due to a deadlock kill that occured during the commit step, we
+ If we retry due to a deadlock kill that occurred during the commit step, we
might have already updated (but not committed) an update of table
mysql.gtid_slave_pos, and cleared the gtid_pending flag. Now we have
rolled back any such update, so we must set the gtid_pending flag back to
@@ -969,10 +969,8 @@ handle_rpl_parallel_thread(void *arg)
my_thread_init();
thd = new THD;
thd->thread_stack = (char*)&thd;
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
+ add_to_active_threads(thd);
set_current_thd(thd);
pthread_detach_this_thread();
thd->init_for_queries();
@@ -1128,7 +1126,7 @@ handle_rpl_parallel_thread(void *arg)
/*
Register ourself to wait for the previous commit, if we need to do
such registration _and_ that previous commit has not already
- occured.
+ occurred.
*/
register_wait_for_prior_event_group_commit(rgi, entry);
@@ -1189,7 +1187,7 @@ handle_rpl_parallel_thread(void *arg)
{
/*
Do an extra check for (deadlock) kill here. This helps prevent a
- lingering deadlock kill that occured during normal DML processing to
+ lingering deadlock kill that occurred during normal DML processing to
propagate past the mark_start_commit(). If we detect a deadlock only
after mark_start_commit(), we have to unmark, which has at least a
theoretical possibility of leaving a window where it looks like all
@@ -1372,10 +1370,10 @@ handle_rpl_parallel_thread(void *arg)
thd->reset_db(NULL, 0);
thd_proc_info(thd, "Slave worker thread exiting");
thd->temporary_tables= 0;
- mysql_mutex_lock(&LOCK_thread_count);
+
THD_CHECK_SENTRY(thd);
+ unlink_not_visible_thd(thd);
delete thd;
- mysql_mutex_unlock(&LOCK_thread_count);
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
rpt->running= false;
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 9bb37f77dbd..c6f77b0144c 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -319,7 +319,7 @@ struct rpl_parallel_entry {
group here. Then later event groups (with higher sub_id) can know not to
try to start (event groups that already started will be rolled back when
wait_for_prior_commit() returns error).
- The value is ULONGLONG_MAX when no error occured.
+ The value is ULONGLONG_MAX when no error occurred.
*/
uint64 stop_on_error_sub_id;
/*
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index 208b2b61704..f82c5a3982a 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -185,7 +185,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
@retval HA_ERR_GENERIC
A generic, internal, error caused the unpacking to fail.
- @retval ER_SLAVE_CORRUPT_EVENT
+ @retval HA_ERR_CORRUPT_EVENT
Found error when trying to unpack fields.
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -349,7 +349,7 @@ unpack_row(rpl_group_info *rgi,
"Could not read field '%s' of table '%s.%s'",
f->field_name, table->s->db.str,
table->s->table_name.str);
- DBUG_RETURN(ER_SLAVE_CORRUPT_EVENT);
+ DBUG_RETURN(HA_ERR_CORRUPT_EVENT);
}
}
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
index 49708df40f7..ad949402511 100644
--- a/sql/rpl_reporting.cc
+++ b/sql/rpl_reporting.cc
@@ -59,6 +59,7 @@ Slave_reporting_capability::report(loglevel level, int err_code,
report_function= sql_print_information;
break;
default:
+ va_end(args);
DBUG_ASSERT(0); // should not come here
return; // don't crash production builds, just do nothing
}
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 987e011d5eb..2dd6f7d7afc 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -63,7 +63,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0),
abort_pos_wait(0), slave_run_id(0), sql_driver_thd(),
gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0),
- slave_running(0), until_condition(UNTIL_NONE),
+ slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0), executed_entries(0),
m_flags(0)
{
@@ -389,6 +389,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
if (rli->is_relay_log_recovery && init_recovery(rli->mi, &msg))
goto err;
+ rli->relay_log_state.load(rpl_global_gtid_slave_state);
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
@@ -1148,6 +1149,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
error=1;
goto err;
}
+ rli->relay_log_state.load(rpl_global_gtid_slave_state);
if (!just_reset)
{
/* Save name of used relay log file */
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 244363e43ac..bdf5b7dea80 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -15,6 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <my_global.h>
+#include <my_bit.h>
#include "rpl_utility.h"
#include "log_event.h"
@@ -23,30 +24,6 @@
#include "sql_select.h"
/**
- Function to compare two size_t integers for their relative
- order. Used below.
- */
-int compare(size_t a, size_t b)
-{
- if (a < b)
- return -1;
- if (b < a)
- return 1;
- return 0;
-}
-
-
-/**
- Max value for an unsigned integer of 'bits' bits.
-
- The somewhat contorted expression is to avoid overflow.
- */
-uint32 uint_max(int bits) {
- return (((1UL << (bits - 1)) - 1) << 1) | 1;
-}
-
-
-/**
Calculate display length for MySQL56 temporal data types from their metadata.
It contains fractional precision in the low 16-bit word.
*/
@@ -121,20 +98,22 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
return 3;
case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TIME:
return 3;
+ case MYSQL_TYPE_TIME:
+ return MIN_TIME_WIDTH;
+
case MYSQL_TYPE_TIME2:
return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, metadata);
case MYSQL_TYPE_TIMESTAMP:
- return 4;
+ return MAX_DATETIME_WIDTH;
case MYSQL_TYPE_TIMESTAMP2:
return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
case MYSQL_TYPE_DATETIME:
- return 8;
+ return MAX_DATETIME_WIDTH;
case MYSQL_TYPE_DATETIME2:
return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
@@ -160,10 +139,10 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
*/
case MYSQL_TYPE_TINY_BLOB:
- return uint_max(1 * 8);
+ return my_set_bits(1 * 8);
case MYSQL_TYPE_MEDIUM_BLOB:
- return uint_max(3 * 8);
+ return my_set_bits(3 * 8);
case MYSQL_TYPE_BLOB:
/*
@@ -171,11 +150,11 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look
at the length instead to decide what the max display size is.
*/
- return uint_max(metadata * 8);
+ return my_set_bits(metadata * 8);
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_GEOMETRY:
- return uint_max(4 * 8);
+ return my_set_bits(4 * 8);
default:
return ~(uint32) 0;
@@ -205,7 +184,7 @@ int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata)
" target_length: %lu, target_type: %u",
(unsigned long) source_length, source_type,
(unsigned long) target_length, field->real_type()));
- int result= compare(source_length, target_length);
+ int result= source_length < target_length ? -1 : source_length > target_length;
DBUG_PRINT("result", ("%d", result));
DBUG_RETURN(result);
}
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index bc3166210b5..2a0138d06a8 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -22,9 +22,9 @@
#pragma implementation
#endif
+#include "mysqld.h"
#include "sql_connect.h" // init_new_connection_handler_thread
#include "scheduler.h"
-#include "mysqld.h"
#include "sql_class.h"
#include "sql_callback.h"
#include <violite.h>
@@ -35,7 +35,8 @@
static bool no_threads_end(THD *thd, bool put_in_cache)
{
- unlink_thd(thd);
+ if (thd)
+ unlink_thd(thd);
return 1; // Abort handle_one_connection
}
@@ -81,7 +82,9 @@ static void scheduler_wait_net_end(void) {
one_thread_scheduler() or one_thread_per_connection_scheduler() in
mysqld.cc, so this init function will always be called.
*/
-void scheduler_init() {
+
+void scheduler_init()
+{
thr_set_lock_wait_callback(scheduler_wait_lock_begin,
scheduler_wait_lock_end);
thr_set_sync_wait_callback(scheduler_wait_sync_begin,
@@ -118,7 +121,6 @@ void post_kill_notification(THD *thd)
#ifndef EMBEDDED_LIBRARY
-
void one_thread_per_connection_scheduler(scheduler_functions *func,
ulong *arg_max_connections,
uint *arg_connection_count)
@@ -132,6 +134,14 @@ void one_thread_per_connection_scheduler(scheduler_functions *func,
func->end_thread= one_thread_per_connection_end;
func->post_kill_notification= post_kill_notification;
}
+#else
+bool init_new_connection_handler_thread()
+{
+ return 0;
+}
+void handle_connection_in_main_thread(CONNECT *connect)
+{
+}
#endif
/*
@@ -144,10 +154,7 @@ void one_thread_scheduler(scheduler_functions *func)
func->max_threads= 1;
func->max_connections= &max_connections;
func->connection_count= &connection_count;
-#ifndef EMBEDDED_LIBRARY
func->init_new_connection_thread= init_new_connection_handler_thread;
func->add_connection= handle_connection_in_main_thread;
-#endif
func->end_thread= no_threads_end;
}
-
diff --git a/sql/scheduler.h b/sql/scheduler.h
index f7aff377eac..71553372999 100644
--- a/sql/scheduler.h
+++ b/sql/scheduler.h
@@ -37,7 +37,7 @@ struct scheduler_functions
ulong *max_connections;
bool (*init)(void);
bool (*init_new_connection_thread)(void);
- void (*add_connection)(THD *thd);
+ void (*add_connection)(CONNECT *connect);
void (*thd_wait_begin)(THD *thd, int wait_type);
void (*thd_wait_end)(THD *thd);
void (*post_kill_notification)(THD *thd);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 18f6cbc41fd..b5430c56865 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -739,7 +739,7 @@ int set_var::check(THD *thd)
if ((!value->fixed &&
value->fix_fields(thd, &value)) || value->check_cols(1))
return -1;
- if (var->check_update_type(value->result_type()))
+ if (var->check_update_type(value))
{
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
return -1;
diff --git a/sql/set_var.h b/sql/set_var.h
index b8192e67ca9..cf86ecf18fa 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -137,8 +137,9 @@ public:
bool is_set_stmt_ok() const { return !(flags & NO_SET_STATEMENT); }
bool is_written_to_binlog(enum_var_type type)
{ return type != OPT_GLOBAL && binlog_status == SESSION_VARIABLE_IN_BINLOG; }
- bool check_update_type(Item_result type)
+ bool check_update_type(const Item *item)
{
+ Item_result type= item->result_type();
switch (option.var_type & GET_TYPE_MASK) {
case GET_INT:
case GET_UINT:
@@ -146,7 +147,8 @@ public:
case GET_ULONG:
case GET_LL:
case GET_ULL:
- return type != INT_RESULT;
+ return type != INT_RESULT &&
+ (type != DECIMAL_RESULT || item->decimals != 0);
case GET_STR:
case GET_STR_ALLOC:
return type != STRING_RESULT;
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 062854d8cea..dfd12ec20ac 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -3947,21 +3947,21 @@ ER_ERROR_DURING_CHECKPOINT
swe "Fick fel %M vid CHECKPOINT"
ukr "Отримано помилку %M під час CHECKPOINT"
ER_NEW_ABORTING_CONNECTION 08S01
- cze "Spojení %ld do databáze: '%-.192s' uživatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo přerušeno"
- dan "Afbrød forbindelsen %ld til databasen '%-.192s' bruger: '%-.48s' vært: '%-.64s' (%-.64s)"
- nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' host: '%-.64s' (%-.64s)"
- eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)"
- est "Ühendus katkestatud %ld andmebaas: '%-.192s' kasutaja: '%-.48s' masin: '%-.64s' (%-.64s)"
- fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' hôte: '%-.64s' (%-.64s)"
- ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s', Host: '%-.64s' (%-.64s)"
- ita "Interrotta la connessione %ld al db: ''%-.192s' utente: '%-.48s' host: '%-.64s' (%-.64s)"
- jpn "接続 %ld が中断されました。データベース: '%-.192s' ユーザー: '%-.48s' ホスト: '%-.64s' (%-.64s)"
- por "Conexão %ld abortada para banco de dados '%-.192s' - usuário '%-.48s' - 'host' '%-.64s' ('%-.64s')"
- rus "Прервано соединение %ld к базе данных '%-.192s' пользователя '%-.48s' с хоста '%-.64s' (%-.64s)"
- serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' a host: '%-.64s' (%-.64s)"
- spa "Abortada conexión %ld para db: '%-.192s' usuario: '%-.48s' servidor: '%-.64s' (%-.64s)"
- swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s', host '%-.64s' (%-.64s)"
- ukr "Перервано з'єднання %ld до бази данних: '%-.192s' користувач: '%-.48s' хост: '%-.64s' (%-.64s)"
+ cze "Spojení %lld do databáze: '%-.192s' uživatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo přerušeno"
+ dan "Afbrød forbindelsen %lld til databasen '%-.192s' bruger: '%-.48s' vært: '%-.64s' (%-.64s)"
+ nla "Afgebroken verbinding %lld naar db: '%-.192s' gebruiker: '%-.48s' host: '%-.64s' (%-.64s)"
+ eng "Aborted connection %lld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)"
+ est "Ühendus katkestatud %lld andmebaas: '%-.192s' kasutaja: '%-.48s' masin: '%-.64s' (%-.64s)"
+ fre "Connection %lld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' hôte: '%-.64s' (%-.64s)"
+ ger "Abbruch der Verbindung %lld zur Datenbank '%-.192s'. Benutzer: '%-.48s', Host: '%-.64s' (%-.64s)"
+ ita "Interrotta la connessione %lld al db: ''%-.192s' utente: '%-.48s' host: '%-.64s' (%-.64s)"
+ jpn "接続 %lld が中断されました。データベース: '%-.192s' ユーザー: '%-.48s' ホスト: '%-.64s' (%-.64s)"
+ por "Conexão %lld abortada para banco de dados '%-.192s' - usuário '%-.48s' - 'host' '%-.64s' ('%-.64s')"
+ rus "Прервано соединение %lld к базе данных '%-.192s' пользователя '%-.48s' с хоста '%-.64s' (%-.64s)"
+ serbian "Prekinuta konekcija broj %lld ka bazi: '%-.192s' korisnik je bio: '%-.48s' a host: '%-.64s' (%-.64s)"
+ spa "Abortada conexión %lld para db: '%-.192s' usuario: '%-.48s' servidor: '%-.64s' (%-.64s)"
+ swe "Avbröt länken för tråd %lld till db '%-.192s', användare '%-.48s', host '%-.64s' (%-.64s)"
+ ukr "Перервано з'єднання %lld до бази данних: '%-.192s' користувач: '%-.48s' хост: '%-.64s' (%-.64s)"
ER_UNUSED_10
eng "You should never see it"
ER_FLUSH_MASTER_BINLOG_CLOSED
@@ -6034,7 +6034,7 @@ ER_EVENT_CANNOT_ALTER_IN_THE_PAST
eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was not changed. Specify a time in the future."
ger "Execution Zeitpunkt des Ereignisses in der Vergangenheit liegt, und es war NACH ABSCHLUSS Set nicht erhalten. Die Veranstaltung wurde nicht verändert. Geben Sie einen Zeitpunkt in der Zukunft."
ER_SLAVE_INCIDENT
- eng "The incident %s occured on the master. Message: %-.64s"
+ eng "The incident %s occurred on the master. Message: %-.64s"
ger "Der Vorfall %s passierte auf dem Master. Meldung: %-.64s"
ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT
eng "Table has no partition for some existing values"
@@ -7106,7 +7106,7 @@ ER_PRIOR_COMMIT_FAILED
ER_IT_IS_A_VIEW 42S02
eng "'%-.192s' is a view"
ER_SLAVE_SKIP_NOT_IN_GTID
- eng "When using GTID, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position."
+ eng "When using parallel replication and GTID with multiple replication domains, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position."
ER_TABLE_DEFINITION_TOO_BIG
eng "The definition for table %`s is too big"
ER_PLUGIN_INSTALLED
@@ -7136,6 +7136,24 @@ ER_KILL_QUERY_DENIED_ERROR
eng "You are not owner of query %lu"
ger "Sie sind nicht Eigentümer von Abfrage %lu"
rus "Вы не являетесь владельцем запроса %lu"
+ER_NO_EIS_FOR_FIELD
+ eng "Engine-independent statistics are not collected for column '%s'"
+ ukr "Незалежна від типу таблиці статистика не збирається для стовбця '%s'"
+ER_COMMULTI_BADCONTEXT 0A000
+ eng "COM_MULTI can't return a result set in the given context"
+ ger "COM_MULTI kann im gegebenen Kontext keine Ergebnismenge zurückgeben"
+ ukr "COM_MULTI не може повернути результати у цьому контексті"
+ER_BAD_COMMAND_IN_MULTI
+ eng "Command '%s' is not allowed for COM_MULTI"
+ ukr "Команда '%s' не дозволена для COM_MULTI"
+ER_WITH_COL_WRONG_LIST
+ eng "WITH column list and SELECT field list have different column counts"
+ER_DUP_QUERY_NAME
+ eng "Duplicate query name in WITH clause"
+ER_WRONG_ORDER_IN_WITH_CLAUSE
+ eng "The definition of the table '%s' refers to the table '%s' defined later in a non-recursive WITH clause"
+ER_RECURSIVE_QUERY_IN_WITH_CLAUSE
+ eng "Recursive queries in WITH clause are not supported yet"
ER_WRONG_WINDOW_SPEC_NAME
eng "Window specification with name '%s' is not defined"
ER_DUP_WINDOW_NAME
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index 4490aae6b62..bbe714fc5b4 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -100,14 +100,13 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"or misconfigured. This error can also be caused by malfunctioning hardware.\n\n");
my_safe_printf_stderr("%s",
- "To report this bug, see http://kb.askmonty.org/en/reporting-bugs\n\n");
+ "To report this bug, see https://mariadb.com/kb/en/reporting-bugs\n\n");
my_safe_printf_stderr("%s",
"We will try our best to scrape up some info that will hopefully help\n"
"diagnose the problem, but since we have already crashed, \n"
"something is definitely wrong and this may fail.\n\n");
- set_server_version();
my_safe_printf_stderr("Server version: %s\n", server_version);
if (dflt_key_cache)
@@ -227,7 +226,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
if (calling_initgroups)
{
my_safe_printf_stderr("%s", "\n"
- "This crash occured while the server was calling initgroups(). This is\n"
+ "This crash occurred while the server was calling initgroups(). This is\n"
"often due to the use of a mysqld that is statically linked against \n"
"glibc and configured to use LDAP in /etc/nsswitch.conf.\n"
"You will need to either upgrade to a version of glibc that does not\n"
diff --git a/sql/slave.cc b/sql/slave.cc
index 8512fc229c1..93506bc2ccd 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -117,7 +117,7 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]=
{
{
"Waiting to reconnect after a failed registration on master",
- "Slave I/O thread killed while waitnig to reconnect after a failed \
+ "Slave I/O thread killed while waiting to reconnect after a failed \
registration on master",
"Reconnecting after a failed registration on master",
"failed registering on master, reconnecting to try again, \
@@ -163,7 +163,6 @@ static int queue_event(Master_info* mi,const char* buf,ulong event_len);
static int terminate_slave_thread(THD *, mysql_mutex_t *, mysql_cond_t *,
volatile uint *, bool);
static bool check_io_slave_killed(Master_info *mi, const char *info);
-static bool send_show_master_info_header(THD *, bool, size_t);
static bool send_show_master_info_data(THD *, Master_info *, bool, String *);
/*
Function to set the slave's max_allowed_packet based on the value
@@ -295,9 +294,7 @@ handle_slave_init(void *arg __attribute__((unused)))
my_thread_init();
thd= new THD;
thd->thread_stack= (char*) &thd; /* Set approximate stack start */
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thread_id++;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->thread_id= next_thread_id();
thd->system_thread = SYSTEM_THREAD_SLAVE_INIT;
thread_safe_increment32(&service_thread_count);
thd->store_globals();
@@ -311,19 +308,17 @@ handle_slave_init(void *arg __attribute__((unused)))
rpl_gtid_slave_state_table_name.str,
thd->get_stmt_da()->sql_errno(),
thd->get_stmt_da()->message());
-
- mysql_mutex_lock(&LOCK_thread_count);
delete thd;
- mysql_mutex_unlock(&LOCK_thread_count);
thread_safe_decrement32(&service_thread_count);
- signal_thd_deleted();
- my_thread_end();
- mysql_mutex_lock(&LOCK_slave_init);
+ /* Signal run_slave_init_thread() that we are done */
+
+ mysql_mutex_lock(&LOCK_start_thread);
slave_init_thread_running= false;
- mysql_cond_broadcast(&COND_slave_init);
- mysql_mutex_unlock(&LOCK_slave_init);
+ mysql_cond_broadcast(&COND_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
+ my_thread_end();
return 0;
}
@@ -348,11 +343,10 @@ run_slave_init_thread()
return 1;
}
- mysql_mutex_lock(&LOCK_slave_init);
+ mysql_mutex_lock(&LOCK_start_thread);
while (slave_init_thread_running)
- mysql_cond_wait(&COND_slave_init, &LOCK_slave_init);
- mysql_mutex_unlock(&LOCK_slave_init);
-
+ mysql_cond_wait(&COND_start_thread, &LOCK_start_thread);
+ mysql_mutex_unlock(&LOCK_start_thread);
return 0;
}
@@ -2497,10 +2491,13 @@ bool show_master_info(THD *thd, Master_info *mi, bool full)
{
DBUG_ENTER("show_master_info");
String gtid_pos;
+ List<Item> field_list;
if (full && rpl_global_gtid_slave_state->tostring(&gtid_pos, NULL, 0))
DBUG_RETURN(TRUE);
- if (send_show_master_info_header(thd, full, gtid_pos.length()))
+ show_master_info_get_fields(thd, &field_list, full, gtid_pos.length());
+ if (thd->protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
if (send_show_master_info_data(thd, mi, full, &gtid_pos))
DBUG_RETURN(TRUE);
@@ -2508,226 +2505,222 @@ bool show_master_info(THD *thd, Master_info *mi, bool full)
DBUG_RETURN(FALSE);
}
-static bool send_show_master_info_header(THD *thd, bool full,
- size_t gtid_pos_length)
+void show_master_info_get_fields(THD *thd, List<Item> *field_list,
+ bool full, size_t gtid_pos_length)
{
- List<Item> field_list;
- Protocol *protocol= thd->protocol;
Master_info *mi;
MEM_ROOT *mem_root= thd->mem_root;
- DBUG_ENTER("show_master_info_header");
+ DBUG_ENTER("show_master_info_get_fields");
if (full)
{
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Connection_name",
- MAX_CONNECTION_NAME),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Slave_SQL_State", 30),
- thd->mem_root);
- }
-
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Slave_IO_State", 30),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_Host", sizeof(mi->host)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_User", sizeof(mi->user)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Master_Port", 7, MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Connect_Retry", 10,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_Log_File", FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Read_Master_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Relay_Log_File", FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Relay_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Relay_Master_Log_File",
- FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Slave_IO_Running", 3),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Slave_SQL_Running", 3),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Do_DB", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Ignore_DB", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Do_Table", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Ignore_Table", 23),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Wild_Do_Table", 24),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Wild_Ignore_Table",
- 28),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Last_Errno", 4, MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Last_Error", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Skip_Counter", 10,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Exec_Master_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Relay_Log_Space", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Until_Condition", 6),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Until_Log_File", FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Until_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Allowed", 7),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_CA_File",
- sizeof(mi->ssl_ca)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_CA_Path",
- sizeof(mi->ssl_capath)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Cert",
- sizeof(mi->ssl_cert)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Cipher",
- sizeof(mi->ssl_cipher)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Key",
- sizeof(mi->ssl_key)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Seconds_Behind_Master", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Verify_Server_Cert",
- 3),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Last_IO_Errno", 4,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Last_IO_Error", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Last_SQL_Errno", 4,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Last_SQL_Error", 20),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Ignore_Server_Ids",
- FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Master_Server_Id", sizeof(ulong),
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Crl",
- sizeof(mi->ssl_crl)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Master_SSL_Crlpath",
- sizeof(mi->ssl_crlpath)),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Using_Gtid",
- sizeof("Current_Pos")-1),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Gtid_IO_Pos", 30),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Do_Domain_Ids",
- FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Replicate_Ignore_Domain_Ids",
- FN_REFLEN),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Parallel_Mode",
- sizeof("conservative")-1),
- thd->mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Connection_name",
+ MAX_CONNECTION_NAME),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Slave_SQL_State", 30),
+ mem_root);
+ }
+
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Slave_IO_State", 30),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_Host", sizeof(mi->host)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_User", sizeof(mi->user)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Master_Port", 7, MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Connect_Retry", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_Log_File", FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Read_Master_Log_Pos", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Relay_Log_File", FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Relay_Log_Pos", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Relay_Master_Log_File",
+ FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Slave_IO_Running", 3),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Slave_SQL_Running", 3),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Do_DB", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Ignore_DB", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Do_Table", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Ignore_Table", 23),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Wild_Do_Table", 24),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Wild_Ignore_Table",
+ 28),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Last_Errno", 4, MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Last_Error", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Skip_Counter", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Exec_Master_Log_Pos", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Relay_Log_Space", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Until_Condition", 6),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Until_Log_File", FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Until_Log_Pos", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Allowed", 7),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_CA_File",
+ sizeof(mi->ssl_ca)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_CA_Path",
+ sizeof(mi->ssl_capath)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Cert",
+ sizeof(mi->ssl_cert)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Cipher",
+ sizeof(mi->ssl_cipher)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Key",
+ sizeof(mi->ssl_key)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Seconds_Behind_Master", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Verify_Server_Cert",
+ 3),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Last_IO_Errno", 4,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Last_IO_Error", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Last_SQL_Errno", 4,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Last_SQL_Error", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Ignore_Server_Ids",
+ FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Master_Server_Id", sizeof(ulong),
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Crl",
+ sizeof(mi->ssl_crl)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Master_SSL_Crlpath",
+ sizeof(mi->ssl_crlpath)),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Using_Gtid",
+ sizeof("Current_Pos")-1),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Gtid_IO_Pos", 30),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Do_Domain_Ids",
+ FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Replicate_Ignore_Domain_Ids",
+ FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Parallel_Mode",
+ sizeof("conservative")-1),
+ mem_root);
if (full)
{
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Retried_transactions", 10,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Max_relay_log_size", 10,
- MYSQL_TYPE_LONGLONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Executed_log_entries", 10,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Slave_received_heartbeats", 10,
- MYSQL_TYPE_LONG),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_float(thd, "Slave_heartbeat_period", 0.0, 3, 10),
- thd->mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Gtid_Slave_Pos",
- gtid_pos_length),
- thd->mem_root);
- }
-
- if (protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
- DBUG_RETURN(FALSE);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Retried_transactions", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Max_relay_log_size", 10,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Executed_log_entries", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Slave_received_heartbeats", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_float(thd, "Slave_heartbeat_period", 0.0, 3, 10),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Gtid_Slave_Pos",
+ gtid_pos_length),
+ mem_root);
+ }
+ DBUG_VOID_RETURN;
}
+/* Text for Slave_IO_Running */
+static const char *slave_running[]= { "No", "Connecting", "Preparing", "Yes" };
static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
String *gtid_pos)
@@ -2780,9 +2773,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
&my_charset_bin);
protocol->store((ulonglong) mi->rli.group_relay_log_pos);
protocol->store(mi->rli.group_master_log_name, &my_charset_bin);
- protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ?
- "Yes" : (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT ?
- "Connecting" : "No"), &my_charset_bin);
+ protocol->store(slave_running[mi->slave_running], &my_charset_bin);
protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin);
protocol->store(rpl_filter->get_do_db());
protocol->store(rpl_filter->get_ignore_db());
@@ -2825,7 +2816,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
Seconds_Behind_Master: if SQL thread is running and I/O thread is
connected, we can compute it otherwise show NULL (i.e. unknown).
*/
- if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
+ if ((mi->slave_running == MYSQL_SLAVE_RUN_READING) &&
mi->rli.slave_running)
{
long time_diff;
@@ -2962,6 +2953,7 @@ bool show_all_master_info(THD* thd)
uint i, elements;
String gtid_pos;
Master_info **tmp;
+ List<Item> field_list;
DBUG_ENTER("show_master_info");
mysql_mutex_assert_owner(&LOCK_active_mi);
@@ -2972,7 +2964,9 @@ bool show_all_master_info(THD* thd)
DBUG_RETURN(TRUE);
}
- if (send_show_master_info_header(thd, 1, gtid_pos.length()))
+ show_master_info_get_fields(thd, &field_list, 1, gtid_pos.length());
+ if (thd->protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
if (!master_info_index ||
@@ -3077,9 +3071,7 @@ static int init_slave_thread(THD* thd, Master_info *mi,
thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
set_slave_thread_options(thd);
thd->client_capabilities = CLIENT_LOCAL_FILES;
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
if (thd_type == SLAVE_THD_SQL)
THD_STAGE_INFO(thd, stage_waiting_for_the_next_event_in_relay_log);
@@ -3971,9 +3963,7 @@ pthread_handler_t handle_slave_io(void *arg)
goto err_during_init;
}
thd->system_thread_info.rpl_io_info= &io_info;
- mysql_mutex_lock(&LOCK_thread_count);
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
+ add_to_active_threads(thd);
mi->slave_running = MYSQL_SLAVE_RUN_NOT_CONNECT;
mi->abort_slave = 0;
mysql_mutex_unlock(&mi->run_lock);
@@ -4128,16 +4118,16 @@ connected:
if (request_dump(thd, mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
- if (check_io_slave_killed(mi, "Slave I/O thread killed while \
-requesting master dump") ||
- try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
- reconnect_messages[SLAVE_RECON_ACT_DUMP]))
+ if (check_io_slave_killed(mi, NullS) ||
+ try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
goto err;
goto connected;
}
const char *event_buf;
+ mi->slave_running= MYSQL_SLAVE_RUN_READING;
DBUG_ASSERT(mi->last_error().number == 0);
while (!io_slave_killed(mi))
{
@@ -4150,8 +4140,7 @@ requesting master dump") ||
*/
THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event);
event_len= read_event(mysql, mi, &suppress_warnings);
- if (check_io_slave_killed(mi, "Slave I/O thread killed while \
-reading event"))
+ if (check_io_slave_killed(mi, NullS))
goto err;
if (event_len == packet_error)
@@ -4297,6 +4286,7 @@ err:
flush_master_info(mi, TRUE, TRUE);
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
+ unlink_not_visible_thd(thd);
mysql_mutex_lock(&mi->run_lock);
err_during_init:
@@ -4356,7 +4346,8 @@ int check_temp_dir(char* tmp_file)
size_t tmp_dir_size;
DBUG_ENTER("check_temp_dir");
- mysql_mutex_lock(&LOCK_thread_count);
+ /* This look is safe to use as this function is only called once */
+ mysql_mutex_lock(&LOCK_start_thread);
if (check_temp_dir_run)
{
result= check_temp_dir_result;
@@ -4395,7 +4386,7 @@ int check_temp_dir(char* tmp_file)
end:
check_temp_dir_result= result;
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_start_thread);
DBUG_RETURN(result);
}
@@ -4573,9 +4564,7 @@ pthread_handler_t handle_slave_sql(void *arg)
applied. In all other cases it must be FALSE.
*/
thd->variables.binlog_annotate_row_events= 0;
- mysql_mutex_lock(&LOCK_thread_count);
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
+ add_to_active_threads(thd);
/*
We are going to set slave_running to 1. Assuming slave I/O thread is
alive and connected, this is going to make Seconds_Behind_Master be 0
@@ -4607,15 +4596,6 @@ pthread_handler_t handle_slave_sql(void *arg)
serial_rgi->gtid_sub_id= 0;
serial_rgi->gtid_pending= false;
- if (mi->using_gtid != Master_info::USE_GTID_NO)
- {
- /*
- We initialize the relay log state from the know starting position.
- It will then be updated as required by GTID and GTID_LIST events found
- while applying events read from relay logs.
- */
- rli->relay_log_state.load(rpl_global_gtid_slave_state);
- }
rli->gtid_skip_flag = GTID_SKIP_NOT;
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
@@ -4886,13 +4866,16 @@ pthread_handler_t handle_slave_sql(void *arg)
}
strmake_buf(rli->group_relay_log_name, ir->name);
rli->group_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ rli->relay_log_state.load(ir->relay_log_state, ir->relay_log_state_count);
}
}
}
}
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
+ unlink_not_visible_thd(thd);
mysql_mutex_lock(&rli->run_lock);
+
err_during_init:
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
mysql_mutex_lock(&rli->data_lock);
@@ -4916,12 +4899,8 @@ err_during_init:
to avoid unneeded position re-init
*/
thd->temporary_tables = 0; // remove tempation from destructor to close them
- THD_CHECK_SENTRY(thd);
rli->sql_driver_thd= 0;
- mysql_mutex_lock(&LOCK_thread_count);
thd->rgi_fake= thd->rgi_slave= NULL;
- delete serial_rgi;
- mysql_mutex_unlock(&LOCK_thread_count);
#ifdef WITH_WSREP
/*
@@ -4950,10 +4929,11 @@ err_during_init:
#endif /* WITH_WSREP */
/*
- Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
- is important. Otherwise a killer_thread can execute between the calls and
- delete the mi structure leading to a crash! (see BUG#25306 for details)
- */
+ Note: the order of the broadcast and unlock calls below (first
+ broadcast, then unlock) is important. Otherwise a killer_thread can
+ execute between the calls and delete the mi structure leading to a
+ crash! (see BUG#25306 for details)
+ */
mysql_cond_broadcast(&rli->stop_cond);
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
mysql_mutex_unlock(&rli->run_lock); // tell the world we are done
@@ -4965,13 +4945,18 @@ err_during_init:
*/
mysql_mutex_lock(&LOCK_active_mi);
if (opt_slave_parallel_threads > 0 &&
+ master_info_index &&// master_info_index is set to NULL on server shutdown
!master_info_index->any_slave_sql_running())
rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
mysql_mutex_unlock(&LOCK_active_mi);
+ /* TODO: Check if this lock is needed */
mysql_mutex_lock(&LOCK_thread_count);
- delete thd;
+ delete serial_rgi;
mysql_mutex_unlock(&LOCK_thread_count);
+
+ THD_CHECK_SENTRY(thd);
+ delete thd;
thread_safe_decrement32(&service_thread_count);
signal_thd_deleted();
diff --git a/sql/slave.h b/sql/slave.h
index a519229fac1..ca89064d773 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -135,11 +135,11 @@ extern const char *relay_log_index;
extern const char *relay_log_basename;
/*
- 3 possible values for Master_info::slave_running and
+ 4 possible values for Master_info::slave_running and
Relay_log_info::slave_running.
- The values 0,1,2 are very important: to keep the diff small, I didn't
- substitute places where we use 0/1 with the newly defined symbols. So don't change
- these values.
+ The values 0,1,2,3 are very important: to keep the diff small, I didn't
+ substitute places where we use 0/1 with the newly defined symbols.
+ So don't change these values.
The same way, code is assuming that in Relay_log_info we use only values
0/1.
I started with using an enum, but
@@ -148,6 +148,7 @@ extern const char *relay_log_basename;
#define MYSQL_SLAVE_NOT_RUN 0
#define MYSQL_SLAVE_RUN_NOT_CONNECT 1
#define MYSQL_SLAVE_RUN_CONNECT 2
+#define MYSQL_SLAVE_RUN_READING 3
#define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\
"FIRST")
@@ -206,8 +207,11 @@ int mysql_table_dump(THD* thd, const char* db,
int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
Master_info* mi, MYSQL* mysql, bool overwrite);
+void show_master_info_get_fields(THD *thd, List<Item> *field_list,
+ bool full, size_t gtid_pos_length);
bool show_master_info(THD* thd, Master_info* mi, bool full);
bool show_all_master_info(THD* thd);
+void show_binlog_info_get_fields(THD *thd, List<Item> *field_list);
bool show_binlog_info(THD* thd);
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
bool (*pred)(const void *), const void *param);
diff --git a/sql/sp.cc b/sql/sp.cc
index 6ec59143720..a518b520786 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -34,6 +34,10 @@
#include <my_user.h>
+/* Used in error handling only */
+#define SP_TYPE_STRING(type) \
+ (type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+
static int
db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_head **sphp,
@@ -1007,15 +1011,16 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
followed by an implicit grant (sp_grant_privileges())
and this subsequent call opens and closes mysql.procs_priv.
- @return Error code. SP_OK is returned on success. Other
- SP_ constants are used to indicate about errors.
+ @return Error status.
+ @retval FALSE on success
+ @retval TRUE on error
*/
-int
+bool
sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
{
LEX *lex= thd->lex;
- int ret;
+ bool ret= TRUE;
TABLE *table;
char definer_buf[USER_HOST_BUFF_SIZE];
LEX_STRING definer;
@@ -1040,7 +1045,22 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
- DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), sp->m_db.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Check that a database directory with this name
+ exists. Design note: This won't work on virtual databases
+ like information_schema.
+ */
+ if (check_db_dir_existence(sp->m_db.str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), sp->m_db.str);
+ DBUG_RETURN(TRUE);
+ }
+
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
@@ -1049,7 +1069,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
thd->count_cuted_fields= CHECK_FIELD_WARN;
if (!(table= open_proc_table_for_update(thd)))
- ret= SP_OPEN_TABLE_FAILED;
+ {
+ my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str);
+ goto done;
+ }
else
{
/* Checking if the routine already exists */
@@ -1065,11 +1088,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_ALREADY_EXISTS,
ER_THD(thd, ER_SP_ALREADY_EXISTS),
- type == TYPE_ENUM_FUNCTION ?
- "FUNCTION" : "PROCEDURE",
+ SP_TYPE_STRING(type),
lex->spname->m_name.str);
- ret= SP_OK;
+ ret= FALSE;
// Setting retstr as it is used for logging.
if (sp->m_type == TYPE_ENUM_FUNCTION)
@@ -1078,7 +1100,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
}
else
{
- ret= SP_WRITE_ROW_FAILED;
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0),
+ SP_TYPE_STRING(type), sp->m_name.str);
goto done;
}
}
@@ -1090,7 +1113,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
{
- ret= SP_GET_FIELD_FAILED;
+ my_error(ER_SP_STORE_FAILED, MYF(0),
+ SP_TYPE_STRING(type), sp->m_name.str);
goto done;
}
@@ -1099,12 +1123,12 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
sp->m_name.str+sp->m_name.length) >
table->field[MYSQL_PROC_FIELD_NAME]->char_length())
{
- ret= SP_BAD_IDENTIFIER;
+ my_error(ER_TOO_LONG_IDENT, MYF(0), sp->m_name.str);
goto done;
}
if (sp->m_body.length > table->field[MYSQL_PROC_FIELD_BODY]->field_length)
{
- ret= SP_BODY_TOO_LONG;
+ my_error(ER_TOO_LONG_BODY, MYF(0), sp->m_name.str);
goto done;
}
@@ -1193,17 +1217,13 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (access == SP_CONTAINS_SQL ||
access == SP_MODIFIES_SQL_DATA)
{
- my_message(ER_BINLOG_UNSAFE_ROUTINE,
- ER_THD(thd, ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
- ret= SP_INTERNAL_ERROR;
+ my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0));
goto done;
}
}
if (!(thd->security_ctx->master_access & SUPER_ACL))
{
- my_message(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER,
- ER_THD(thd, ER_BINLOG_CREATE_ROUTINE_NEED_SUPER), MYF(0));
- ret= SP_INTERNAL_ERROR;
+ my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER,MYF(0));
goto done;
}
}
@@ -1234,22 +1254,24 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (store_failed)
{
- ret= SP_FLD_STORE_FAILED;
+ my_error(ER_CANT_CREATE_SROUTINE, MYF(0), sp->m_name.str);
goto done;
}
- ret= SP_OK;
if (table->file->ha_write_row(table->record[0]))
- ret= SP_WRITE_ROW_FAILED;
+ {
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0),
+ SP_TYPE_STRING(type), sp->m_name.str);
+ goto done;
+ }
/* Make change permanent and avoid 'table is marked as crashed' errors */
table->file->extra(HA_EXTRA_FLUSH);
- if (ret == SP_OK)
- sp_cache_invalidate();
+ sp_cache_invalidate();
}
log:
- if (ret == SP_OK && mysql_bin_log.is_open())
+ if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -1268,7 +1290,7 @@ log:
&(thd->lex->definer->host),
saved_mode))
{
- ret= SP_INTERNAL_ERROR;
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto done;
}
/* restore sql_mode when binloging */
@@ -1277,9 +1299,13 @@ log:
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
log_query.ptr(), log_query.length(),
FALSE, FALSE, FALSE, 0))
- ret= SP_INTERNAL_ERROR;
+ {
+ my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), "binary log", -1);
+ goto done;
+ }
thd->variables.sql_mode= 0;
}
+ ret= FALSE;
done:
thd->count_cuted_fields= saved_count_cuted_fields;
diff --git a/sql/sp.h b/sql/sp.h
index 4bfb0577fcc..df60482f8fd 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -126,7 +126,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc);
bool
sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name);
-int
+bool
sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp);
int
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ce4ec09bede..d58b51afc5e 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -216,6 +216,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_PROC:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
+ case SQLCOM_SHOW_CREATE_USER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_EXPLAIN:
@@ -283,6 +284,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_USER:
case SQLCOM_CREATE_ROLE:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_ALTER_USER:
case SQLCOM_GRANT:
case SQLCOM_GRANT_ROLE:
case SQLCOM_REVOKE:
@@ -2534,6 +2536,69 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
/**
+ Collect metadata for SHOW CREATE statement for stored routines.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+
+ @return Error status.
+ @retval FALSE on success
+ @retval TRUE on error
+*/
+
+void
+sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
+{
+ const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Procedure" : "Function";
+
+ const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Create Procedure" : "Create Function";
+
+ MEM_ROOT *mem_root= thd->mem_root;
+
+ /* Send header. */
+
+ fields->push_back(new (mem_root)
+ Item_empty_string(thd, col1_caption, NAME_CHAR_LEN),
+ mem_root);
+ fields->push_back(new (mem_root)
+ Item_empty_string(thd, "sql_mode", 256),
+ mem_root);
+
+ {
+ /*
+ NOTE: SQL statement field must be not less than 1024 in order not to
+ confuse old clients.
+ */
+
+ Item_empty_string *stmt_fld=
+ new (mem_root) Item_empty_string(thd, col3_caption, 1024);
+ stmt_fld->maybe_null= TRUE;
+
+ fields->push_back(stmt_fld, mem_root);
+ }
+
+ fields->push_back(new (mem_root)
+ Item_empty_string(thd, "character_set_client",
+ MY_CS_NAME_SIZE),
+ mem_root);
+
+ fields->push_back(new (mem_root)
+ Item_empty_string(thd, "collation_connection",
+ MY_CS_NAME_SIZE),
+ mem_root);
+
+ fields->push_back(new (mem_root)
+ Item_empty_string(thd, "Database Collation",
+ MY_CS_NAME_SIZE),
+ mem_root);
+}
+
+
+/**
Implement SHOW CREATE statement for stored routines.
@param thd Thread context.
@@ -3211,7 +3276,8 @@ sp_instr_set::print(String *str)
}
str->qs_append(m_offset);
str->qs_append(' ');
- m_value->print(str, QT_ORDINARY);
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
@@ -3243,9 +3309,11 @@ void
sp_instr_set_trigger_field::print(String *str)
{
str->append(STRING_WITH_LEN("set_trigger_field "));
- trigger_field->print(str, QT_ORDINARY);
+ trigger_field->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
str->append(STRING_WITH_LEN(":="));
- value->print(str, QT_ORDINARY);
+ value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
/*
@@ -3371,7 +3439,8 @@ sp_instr_jump_if_not::print(String *str)
str->qs_append('(');
str->qs_append(m_cont_dest);
str->qs_append(STRING_WITH_LEN(") "));
- m_expr->print(str, QT_ORDINARY);
+ m_expr->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
@@ -3467,7 +3536,8 @@ sp_instr_freturn::print(String *str)
str->qs_append(STRING_WITH_LEN("freturn "));
str->qs_append((uint)m_type);
str->qs_append(' ');
- m_value->print(str, QT_ORDINARY);
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
/*
@@ -3939,7 +4009,8 @@ sp_instr_set_case_expr::print(String *str)
str->qs_append(STRING_WITH_LEN(") "));
str->qs_append(m_case_expr_id);
str->qs_append(' ');
- m_case_expr->print(str, QT_ORDINARY);
+ m_case_expr->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
uint
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 4e761c31d5b..65aad1dd5a1 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -337,6 +337,9 @@ public:
bool
execute_procedure(THD *thd, List<Item> *args);
+ static void
+ show_create_routine_get_fields(THD *thd, int type, List<Item> *fields);
+
bool
show_create_routine(THD *thd, int type);
@@ -604,7 +607,7 @@ public:
instruction for CONTINUE error handlers.
@retval 0 on success,
- @retval other if some error occured
+ @retval other if some error occurred
*/
virtual int execute(THD *thd, uint *nextp) = 0;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 2bcd351ae32..241f19c75a3 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -634,9 +634,6 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
char *hostname, char *rolename,
bool with_admin_option)
{
- if (!this)
- return true;
-
size_t uname_l = safe_strlen(username);
size_t hname_l = safe_strlen(hostname);
size_t rname_l = safe_strlen(rolename);
@@ -874,6 +871,17 @@ static void free_acl_role(ACL_ROLE *role)
delete_dynamic(&(role->parent_grantee));
}
+static my_bool check_if_exists(THD *, plugin_ref, void *)
+{
+ return TRUE;
+}
+
+static bool has_validation_plugins()
+{
+ return plugin_foreach(NULL, check_if_exists,
+ MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
+}
+
struct validation_data { LEX_STRING *user, *password; };
static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
@@ -885,22 +893,27 @@ static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
}
-static bool validate_password(LEX_STRING *user, LEX_STRING *password)
+static bool validate_password(LEX_USER *user)
{
- struct validation_data data= { user, password };
- return plugin_foreach(NULL, do_validate,
- MariaDB_PASSWORD_VALIDATION_PLUGIN, &data);
-}
-
-static my_bool check_if_exists(THD *, plugin_ref, void *)
-{
- return TRUE;
-}
-
-static bool has_validation_plugins()
-{
- return plugin_foreach(NULL, check_if_exists,
- MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
+ if (user->pwtext.length || !user->pwhash.length)
+ {
+ struct validation_data data= { &user->user, &user->pwtext };
+ if (plugin_foreach(NULL, do_validate,
+ MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
+ {
+ my_error(ER_NOT_VALID_PASSWORD, MYF(0));
+ return true;
+ }
+ }
+ else
+ {
+ if (strict_password_validation && has_validation_plugins())
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -984,16 +997,17 @@ static bool fix_user_plugin_ptr(ACL_USER *user)
- if user->plugin is specified, user->auth is the plugin auth data.
- if user->plugin is mysql_native_password or mysql_old_password,
- user->auth if the password hash, and LEX_USER is transformed
+ user->auth is the password hash, and LEX_USER is transformed
to match the next case (that is, user->plugin is cleared).
- if user->plugin is NOT specified, built-in auth is assumed, that is
mysql_native_password or mysql_old_password. In that case,
- user->auth is the password hash. And user->password is the original
- plain-text password. Either one can be set or even both.
+ user->pwhash is the password hash. And user->pwtext is the original
+ plain-text password. Either one can be set or both.
Upon exiting this function:
- - user->password is the password hash, as the mysql.user.password column
+ - user->pwtext is left untouched
+ - user->pwhash is the password hash, as the mysql.user.password column
- user->plugin is the plugin name, as the mysql.user.plugin column
- user->auth is the plugin auth data, as the mysql.user.authentication_string column
*/
@@ -1001,6 +1015,9 @@ static bool fix_lex_user(THD *thd, LEX_USER *user)
{
size_t check_length;
+ DBUG_ASSERT(user->plugin.length || !user->auth.length);
+ DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
+
if (my_strcasecmp(system_charset_info, user->plugin.str,
native_password_plugin_name.str) == 0)
check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
@@ -1011,36 +1028,26 @@ static bool fix_lex_user(THD *thd, LEX_USER *user)
else
if (user->plugin.length)
return false; // nothing else to do
- else
- if (user->auth.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- check_length= 0; // length is valid, no need to re-check
+ else if (thd->variables.old_passwords == 1 ||
+ user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
else
check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- if (check_length && user->auth.length && user->auth.length != check_length)
+ if (user->plugin.length)
{
- my_error(ER_PASSWD_LENGTH, MYF(0), check_length);
- return true;
+ user->pwhash= user->auth;
+ user->plugin= empty_lex_str;
+ user->auth= empty_lex_str;
}
- if (user->password.length || !user->auth.length)
- {
- if (validate_password(&user->user, &user->password))
- {
- my_error(ER_NOT_VALID_PASSWORD, MYF(0));
- return true;
- }
- }
- else
+ if (user->pwhash.length && user->pwhash.length != check_length)
{
- if (strict_password_validation && has_validation_plugins())
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
- return true;
- }
+ my_error(ER_PASSWD_LENGTH, MYF(0), check_length);
+ return true;
}
- if (user->password.length && !user->auth.length)
+ if (user->pwtext.length && !user->pwhash.length)
{
size_t scramble_length;
void (*make_scramble)(char *, const char *, size_t);
@@ -1059,14 +1066,11 @@ static bool fix_lex_user(THD *thd, LEX_USER *user)
char *buff= (char *) thd->alloc(scramble_length + 1);
if (buff == NULL)
return true;
- make_scramble(buff, user->password.str, user->password.length);
- user->auth.str= buff;
- user->auth.length= scramble_length;
+ make_scramble(buff, user->pwtext.str, user->pwtext.length);
+ user->pwhash.str= buff;
+ user->pwhash.length= scramble_length;
}
- user->password= user->auth.length ? user->auth : null_lex_str;
- user->plugin= empty_lex_str;
- user->auth= empty_lex_str;
return false;
}
@@ -1202,7 +1206,8 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
(void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0));
if ((table= tables[HOST_TABLE].table)) // "host" table may not exist (e.g. in MySQL 5.6.7+)
{
- if (init_read_record(&read_record_info, thd, table, NULL, 1, 1, FALSE))
+ if (init_read_record(&read_record_info, thd, table, NULL, NULL,
+ 1, 1, FALSE))
goto end;
table->use_all_columns();
while (!(read_record_info.read_record(&read_record_info)))
@@ -1257,7 +1262,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
freeze_size(&acl_hosts);
if (init_read_record(&read_record_info, thd, table=tables[USER_TABLE].table,
- NULL, 1, 1, FALSE))
+ NULL, NULL, 1, 1, FALSE))
goto end;
table->use_all_columns();
(void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0));
@@ -1519,7 +1524,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
freeze_size(&acl_users);
if (init_read_record(&read_record_info, thd, table=tables[DB_TABLE].table,
- NULL, 1, 1, FALSE))
+ NULL, NULL, 1, 1, FALSE))
goto end;
table->use_all_columns();
(void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB), 50, 100, MYF(0));
@@ -1589,7 +1594,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
if ((table= tables[PROXIES_PRIV_TABLE].table))
{
if (init_read_record(&read_record_info, thd, table,
- NULL, 1, 1, FALSE))
+ NULL, NULL, 1, 1, FALSE))
goto end;
table->use_all_columns();
while (!(read_record_info.read_record(&read_record_info)))
@@ -1618,7 +1623,8 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
if ((table= tables[ROLES_MAPPING_TABLE].table))
{
- if (init_read_record(&read_record_info, thd, table, NULL, 1, 1, FALSE))
+ if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 1,
+ FALSE))
goto end;
table->use_all_columns();
/* account for every role mapping */
@@ -2737,7 +2743,8 @@ bool check_change_password(THD *thd, LEX_USER *user)
{
LEX_USER *real_user= get_current_user(thd, user);
- if (fix_and_copy_user(real_user, user, thd))
+ if (fix_and_copy_user(real_user, user, thd) ||
+ validate_password(real_user))
return true;
*user= *real_user;
@@ -2767,7 +2774,7 @@ bool change_password(THD *thd, LEX_USER *user)
const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
- user->host.str, user->user.str, user->password.str));
+ user->host.str, user->user.str, user->pwhash.str));
DBUG_ASSERT(user->host.str != 0); // Ensured by parent
/*
@@ -2784,7 +2791,7 @@ bool change_password(THD *thd, LEX_USER *user)
{
query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
safe_str(user->user.str), safe_str(user->host.str),
- safe_str(user->password.str));
+ safe_str(user->pwhash.str));
}
if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
@@ -2812,10 +2819,10 @@ bool change_password(THD *thd, LEX_USER *user)
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
- acl_user->auth_string.str= strmake_root(&acl_memroot, user->password.str, user->password.length);
- acl_user->auth_string.length= user->password.length;
- set_user_salt(acl_user, user->password.str, user->password.length);
- set_user_plugin(acl_user, user->password.length);
+ acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
+ acl_user->auth_string.length= user->pwhash.length;
+ set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
+ set_user_plugin(acl_user, user->pwhash.length);
}
else
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
@@ -2825,7 +2832,7 @@ bool change_password(THD *thd, LEX_USER *user)
if (update_user_table(thd, tables[USER_TABLE].table,
safe_str(acl_user->host.hostname),
safe_str(acl_user->user.str),
- user->password.str, user->password.length))
+ user->pwhash.str, user->pwhash.length))
{
mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
goto end;
@@ -3366,17 +3373,18 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
mysql_mutex_assert_owner(&acl_cache->lock);
- if (combo.password.str && combo.password.str[0])
+ if (combo.pwhash.str && combo.pwhash.str[0])
{
- if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
- combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
+ combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
+ DBUG_ASSERT(0);
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
}
}
else
- combo.password= empty_lex_str;
+ combo.pwhash= empty_lex_str;
/* if the user table is not up to date, we can't handle role updates */
if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role)
@@ -3418,7 +3426,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
see also test_if_create_new_users()
*/
- else if (!combo.password.length && !combo.plugin.length && no_auto_create)
+ else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create)
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
goto end;
@@ -3450,6 +3458,10 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
store_record(table,record[1]); // Save copy for update
}
+ if (!old_row_exists || combo.pwtext.length || combo.pwhash.length)
+ if (validate_password(&combo))
+ goto end;
+
/* Update table columns with new privileges */
Field **tmp_field;
@@ -3465,8 +3477,8 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
}
rights= get_access(table, 3, &next_field);
DBUG_PRINT("info",("table fields: %d",table->s->fields));
- if (combo.password.str[0])
- table->field[2]->store(combo.password.str, combo.password.length, system_charset_info);
+ if (combo.pwhash.str[0])
+ table->field[2]->store(combo.pwhash.str, combo.pwhash.length, system_charset_info);
if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
@@ -3529,14 +3541,14 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
table->field[next_field + 1]->set_notnull();
if (combo.plugin.str[0])
{
- DBUG_ASSERT(combo.password.str[0] == 0);
+ DBUG_ASSERT(combo.pwhash.str[0] == 0);
table->field[2]->reset();
table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
system_charset_info);
table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
system_charset_info);
}
- if (combo.password.str[0])
+ if (combo.pwhash.str[0])
{
DBUG_ASSERT(combo.plugin.str[0] == 0);
table->field[next_field]->reset();
@@ -3605,7 +3617,7 @@ end:
acl_update_role(combo.user.str, rights);
else
acl_update_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
+ combo.pwhash.str, combo.pwhash.length,
lex->ssl_type,
lex->ssl_cipher,
lex->x509_issuer,
@@ -3621,7 +3633,7 @@ end:
acl_insert_role(combo.user.str, rights);
else
acl_insert_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
+ combo.pwhash.str, combo.pwhash.length,
lex->ssl_type,
lex->ssl_cipher,
lex->x509_issuer,
@@ -5679,7 +5691,7 @@ static bool merge_one_role_privileges(ACL_ROLE *grantee)
static bool has_auth(LEX_USER *user, LEX *lex)
{
- return user->password.str || user->plugin.length || user->auth.length ||
+ return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length ||
lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
lex->x509_issuer || lex->x509_subject ||
lex->mqh.specified_limits;
@@ -5690,7 +5702,8 @@ static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd)
if (to != from)
{
/* preserve authentication information, if LEX_USER was reallocated */
- to->password= from->password;
+ to->pwtext= from->pwtext;
+ to->pwhash= from->pwhash;
to->plugin= from->plugin;
to->auth= from->auth;
}
@@ -7667,6 +7680,94 @@ static void add_user_option(String *grant, double value, const char *name)
}
}
+static void add_user_parameters(String *result, ACL_USER* acl_user,
+ bool with_grant)
+{
+ result->append(STRING_WITH_LEN("@'"));
+ result->append(acl_user->host.hostname, acl_user->hostname_length,
+ system_charset_info);
+ result->append('\'');
+
+ if (acl_user->plugin.str == native_password_plugin_name.str ||
+ acl_user->plugin.str == old_password_plugin_name.str)
+ {
+ if (acl_user->auth_string.length)
+ {
+ DBUG_ASSERT(acl_user->salt_len);
+ result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
+ result->append(acl_user->auth_string.str, acl_user->auth_string.length);
+ result->append('\'');
+ }
+ }
+ else
+ {
+ result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
+ result->append(acl_user->plugin.str, acl_user->plugin.length);
+ if (acl_user->auth_string.length)
+ {
+ result->append(STRING_WITH_LEN(" USING '"));
+ result->append(acl_user->auth_string.str, acl_user->auth_string.length);
+ result->append('\'');
+ }
+ }
+ /* "show grants" SSL related stuff */
+ if (acl_user->ssl_type == SSL_TYPE_ANY)
+ result->append(STRING_WITH_LEN(" REQUIRE SSL"));
+ else if (acl_user->ssl_type == SSL_TYPE_X509)
+ result->append(STRING_WITH_LEN(" REQUIRE X509"));
+ else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
+ {
+ int ssl_options = 0;
+ result->append(STRING_WITH_LEN(" REQUIRE "));
+ if (acl_user->x509_issuer)
+ {
+ ssl_options++;
+ result->append(STRING_WITH_LEN("ISSUER \'"));
+ result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
+ result->append('\'');
+ }
+ if (acl_user->x509_subject)
+ {
+ if (ssl_options++)
+ result->append(' ');
+ result->append(STRING_WITH_LEN("SUBJECT \'"));
+ result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
+ system_charset_info);
+ result->append('\'');
+ }
+ if (acl_user->ssl_cipher)
+ {
+ if (ssl_options++)
+ result->append(' ');
+ result->append(STRING_WITH_LEN("CIPHER '"));
+ result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
+ system_charset_info);
+ result->append('\'');
+ }
+ }
+ if (with_grant ||
+ (acl_user->user_resource.questions ||
+ acl_user->user_resource.updates ||
+ acl_user->user_resource.conn_per_hour ||
+ acl_user->user_resource.user_conn ||
+ acl_user->user_resource.max_statement_time != 0.0))
+ {
+ result->append(STRING_WITH_LEN(" WITH"));
+ if (with_grant)
+ result->append(STRING_WITH_LEN(" GRANT OPTION"));
+ add_user_option(result, acl_user->user_resource.questions,
+ "MAX_QUERIES_PER_HOUR", false);
+ add_user_option(result, acl_user->user_resource.updates,
+ "MAX_UPDATES_PER_HOUR", false);
+ add_user_option(result, acl_user->user_resource.conn_per_hour,
+ "MAX_CONNECTIONS_PER_HOUR", false);
+ add_user_option(result, acl_user->user_resource.user_conn,
+ "MAX_USER_CONNECTIONS", true);
+ add_user_option(result, acl_user->user_resource.max_statement_time,
+ "MAX_STATEMENT_TIME");
+ }
+}
+
static const char *command_array[]=
{
"SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
@@ -7713,6 +7814,77 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
}
+bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
+{
+ const char *username = safe_str(lex_user->user.str);
+ const char *hostname = safe_str(lex_user->host.str);
+ char buff[1024]; //Show create user should not take more than 1024 bytes.
+ Protocol *protocol= thd->protocol;
+ bool error= false;
+ ACL_USER *acl_user;
+ DBUG_ENTER("mysql_show_create_user");
+
+ // Check if the command specifies a username or not.
+ if (lex_user->user.str == current_user.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ }
+
+ List<Item> field_list;
+ strxmov(buff, "CREATE USER for ", username, "@", hostname, NullS);
+ Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
+ if (!field)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ field->name= buff;
+ field->max_length= sizeof(buff);
+ field_list.push_back(field, thd->mem_root);
+ if (protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ DBUG_RETURN(true);
+
+ String result(buff, sizeof(buff), system_charset_info);
+ result.length(0);
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+
+ acl_user= find_user_exact(hostname, username);
+
+ // User not found in the internal data structures.
+ if (!acl_user)
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ error= true;
+ goto end;
+ }
+
+ result.append("CREATE USER '");
+ result.append(username);
+ result.append('\'');
+
+ add_user_parameters(&result, acl_user, false);
+
+ protocol->prepare_for_resend();
+ protocol->store(result.ptr(), result.length(), result.charset());
+ if (protocol->write())
+ {
+ error= true;
+ }
+ my_eof(thd);
+
+end:
+ mysql_rwlock_unlock(&LOCK_grant);
+ mysql_mutex_unlock(&acl_cache->lock);
+
+ DBUG_RETURN(error);
+}
+
+
static int show_grants_callback(ACL_USER_BASE *role, void *data)
{
THD *thd= (THD *)data;
@@ -7722,6 +7894,15 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data)
return 0;
}
+void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
+ const char *name)
+{
+ Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
+ field->name= (char *) name;
+ field->max_length=1024;
+ fields->push_back(field, thd->mem_root);
+}
+
/*
SHOW GRANTS; Send grants for a user to the client
@@ -7788,15 +7969,14 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
}
DBUG_ASSERT(rolename || username);
- Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
List<Item> field_list;
- field->name=buff;
- field->max_length=1024;
if (!username)
strxmov(buff,"Grants for ",rolename, NullS);
else
strxmov(buff,"Grants for ",username,"@",hostname, NullS);
- field_list.push_back(field, thd->mem_root);
+
+ mysql_show_grants_get_fields(thd, &field_list, buff);
+
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
@@ -7981,93 +8161,7 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
global.append('\'');
if (!handle_as_role)
- {
- ACL_USER *acl_user= (ACL_USER *)acl_entry;
-
- global.append (STRING_WITH_LEN("@'"));
- global.append(acl_user->host.hostname, acl_user->hostname_length,
- system_charset_info);
- global.append ('\'');
-
- if (acl_user->plugin.str == native_password_plugin_name.str ||
- acl_user->plugin.str == old_password_plugin_name.str)
- {
- if (acl_user->auth_string.length)
- {
- DBUG_ASSERT(acl_user->salt_len);
- global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
- global.append(acl_user->auth_string.str, acl_user->auth_string.length);
- global.append('\'');
- }
- }
- else
- {
- global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
- global.append(acl_user->plugin.str, acl_user->plugin.length);
- if (acl_user->auth_string.length)
- {
- global.append(STRING_WITH_LEN(" USING '"));
- global.append(acl_user->auth_string.str, acl_user->auth_string.length);
- global.append('\'');
- }
- }
- /* "show grants" SSL related stuff */
- if (acl_user->ssl_type == SSL_TYPE_ANY)
- global.append(STRING_WITH_LEN(" REQUIRE SSL"));
- else if (acl_user->ssl_type == SSL_TYPE_X509)
- global.append(STRING_WITH_LEN(" REQUIRE X509"));
- else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
- {
- int ssl_options = 0;
- global.append(STRING_WITH_LEN(" REQUIRE "));
- if (acl_user->x509_issuer)
- {
- ssl_options++;
- global.append(STRING_WITH_LEN("ISSUER \'"));
- global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
- global.append('\'');
- }
- if (acl_user->x509_subject)
- {
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("SUBJECT \'"));
- global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
- system_charset_info);
- global.append('\'');
- }
- if (acl_user->ssl_cipher)
- {
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("CIPHER '"));
- global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
- system_charset_info);
- global.append('\'');
- }
- }
- if ((want_access & GRANT_ACL) ||
- (acl_user->user_resource.questions ||
- acl_user->user_resource.updates ||
- acl_user->user_resource.conn_per_hour ||
- acl_user->user_resource.user_conn ||
- acl_user->user_resource.max_statement_time != 0.0))
- {
- global.append(STRING_WITH_LEN(" WITH"));
- if (want_access & GRANT_ACL)
- global.append(STRING_WITH_LEN(" GRANT OPTION"));
- add_user_option(&global, acl_user->user_resource.questions,
- "MAX_QUERIES_PER_HOUR", false);
- add_user_option(&global, acl_user->user_resource.updates,
- "MAX_UPDATES_PER_HOUR", false);
- add_user_option(&global, acl_user->user_resource.conn_per_hour,
- "MAX_CONNECTIONS_PER_HOUR", false);
- add_user_option(&global, acl_user->user_resource.user_conn,
- "MAX_USER_CONNECTIONS", true);
- add_user_option(&global, acl_user->user_resource.max_statement_time,
- "MAX_STATEMENT_TIME");
- }
- }
+ add_user_parameters(&global, (ACL_USER *)acl_entry, (want_access & GRANT_ACL));
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
@@ -9724,6 +9818,73 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
DBUG_RETURN(result);
}
+/*
+ Alter a user's connection and resource settings.
+
+ SYNOPSIS
+ mysql_alter_user()
+ thd The current thread.
+ list The users to alter.
+
+ RETURN
+ > 0 Error. Error message already sent.
+ 0 OK.
+*/
+int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
+{
+ DBUG_ENTER("mysql_alter_user");
+ int result= 0;
+ TABLE_LIST tables[TABLES_MAX];
+ String wrong_users;
+ // The only table we're altering is the user table.
+ if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user)))
+ DBUG_RETURN(result);
+
+ // Lock ACL data structures until we finish altering all users.
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+
+ LEX_USER *tmp_lex_user;
+ List_iterator<LEX_USER> users_list_iterator(users_list);
+ while ((tmp_lex_user= users_list_iterator++))
+ {
+ LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
+ if (!lex_user ||
+ fix_lex_user(thd, lex_user) ||
+ replace_user_table(thd, tables[USER_TABLE].table, *lex_user,0,
+ false, false, true))
+ {
+ thd->clear_error();
+ append_user(thd, &wrong_users, tmp_lex_user);
+ result= TRUE;
+ continue;
+ }
+ }
+
+ // Unlock ACL data structures.
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+
+ if (result)
+ {
+ // 'if exists' flag leads to warnings instead of errors.
+ if (thd->lex->create_info.if_exists())
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_CANNOT_USER,
+ ER_THD(thd, ER_CANNOT_USER),
+ "ALTER USER", wrong_users.c_ptr_safe());
+ result= FALSE;
+ }
+ else
+ {
+ my_error(ER_CANNOT_USER, MYF(0),
+ "ALTER USER",
+ wrong_users.c_ptr_safe());
+ }
+ }
+ DBUG_RETURN(result);
+}
/*
Revoke all privileges from a list of users.
@@ -10107,7 +10268,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
@return
@retval FALSE Success
- @retval TRUE An error occured. Error message not yet sent.
+ @retval TRUE An error occurred. Error message not yet sent.
*/
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
@@ -10153,15 +10314,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
- combo->password= null_lex_str;
- combo->plugin= empty_lex_str;
- combo->auth= empty_lex_str;
+ combo->reset_auth();
if(au)
{
- if (au->plugin.str != native_password_plugin_name.str &&
- au->plugin.str != old_password_plugin_name.str)
- combo->plugin= au->plugin;
+ combo->plugin= au->plugin;
combo->auth= au->auth_string;
}
@@ -10915,7 +11072,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
/* global privileges */
grant->privilege= sctx->master_access;
- if (!sctx->priv_user)
+ if (!sctx->priv_user[0])
{
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN; // it is slave
@@ -11314,7 +11471,8 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
int2store(end+5, thd->client_capabilities >> 16);
end[7]= data_len;
DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
- bzero(end + 8, 10);
+ bzero(end + 8, 6);
+ int4store(end + 14, thd->client_capabilities >> 32);
end+= 18;
/* write scramble tail */
end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
@@ -11730,18 +11888,27 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
*/
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
- ulong client_capabilities= uint2korr(net->read_pos);
+ ulonglong client_capabilities= uint2korr(net->read_pos);
+ compile_time_assert(sizeof(client_capabilities) >= 8);
if (client_capabilities & CLIENT_PROTOCOL_41)
{
- if (pkt_len < 4)
+ if (pkt_len < 32)
return packet_error;
client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
+ if (!(client_capabilities & CLIENT_MYSQL))
+ {
+ // it is client with mariadb extensions
+ ulonglong ext_client_capabilities=
+ (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
+ client_capabilities|= ext_client_capabilities;
+ }
}
/* Disable those bits which are not supported by the client. */
+ compile_time_assert(sizeof(thd->client_capabilities) >= 8);
thd->client_capabilities&= client_capabilities;
- DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
+ DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
if (thd->client_capabilities & CLIENT_SSL)
{
unsigned long errptr __attribute__((unused));
@@ -11769,8 +11936,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
if (client_capabilities & CLIENT_PROTOCOL_41)
{
- if (pkt_len < 32)
- return packet_error;
thd->max_client_packet_length= uint4korr(net->read_pos+4);
DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
@@ -12547,7 +12712,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
}
DBUG_PRINT("info",
- ("Capabilities: %lu packet_length: %ld Host: '%s' "
+ ("Capabilities: %llu packet_length: %ld Host: '%s' "
"Login user: '%s' Priv_user: '%s' Using password: %s "
"Access: %lu db: '%s'",
thd->client_capabilities, thd->max_client_packet_length,
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 335a558cddb..be206b1f86f 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -241,7 +241,10 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table);
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const char *field_name);
+void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
+ const char *name);
bool mysql_show_grants(THD *thd, LEX_USER *user);
+bool mysql_show_create_user(THD *thd, LEX_USER *user);
int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond);
void get_privilege_desc(char *to, uint max_length, ulong access);
@@ -249,6 +252,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc);
bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
bool mysql_rename_user(THD *thd, List <LEX_USER> &list);
+int mysql_alter_user(THD *thd, List <LEX_USER> &list);
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table);
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 0787aa9e92f..205621c6f9f 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
- Copyright (c) 2012, 2015, MariaDB
+/* Copyright (c) 2010, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -131,7 +131,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(0);
has_mdl_lock= TRUE;
- share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE);
+ share= tdc_acquire_share(thd, table_list, GTS_TABLE);
if (share == NULL)
DBUG_RETURN(0); // Can't open frm file
@@ -288,7 +288,8 @@ static inline bool table_not_corrupt_error(uint sql_errno)
sql_errno == ER_LOCK_WAIT_TIMEOUT ||
sql_errno == ER_LOCK_DEADLOCK ||
sql_errno == ER_CANT_LOCK_LOG_TABLE ||
- sql_errno == ER_OPEN_AS_READONLY);
+ sql_errno == ER_OPEN_AS_READONLY ||
+ sql_errno == ER_WRONG_OBJECT);
}
@@ -368,6 +369,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
char* db = table->db;
bool fatal_error=0;
bool open_error;
+ bool collect_eis= FALSE;
DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
strxmov(table_name, db, ".", table->table_name, NullS);
@@ -399,7 +401,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
lex->query_tables_last= &table->next_global;
lex->query_tables_own_last= 0;
- if (view_operator_func == NULL)
+ /*
+ CHECK TABLE command is allowed for views as well. Check on alter flags
+ to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
+ allowed.
+ */
+ if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
+ view_operator_func == NULL)
{
table->required_type=FRMTYPE_TABLE;
DBUG_ASSERT(!lex->only_view);
@@ -690,34 +698,64 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
{
compl_result_code= result_code= HA_ADMIN_INVALID;
}
+ collect_eis=
+ (table->table->s->table_category == TABLE_CATEGORY_USER &&
+ (get_use_stat_tables_mode(thd) > NEVER ||
+ lex->with_persistent_for_clause));
- if (!lex->column_list)
- {
- uint fields= 0;
- for ( ; *field_ptr; field_ptr++, fields++) ;
- bitmap_set_prefix(tab->read_set, fields);
- }
- else
+ if (collect_eis)
{
- int pos;
- LEX_STRING *column_name;
- List_iterator_fast<LEX_STRING> it(*lex->column_list);
+ if (!lex->column_list)
+ {
+ bitmap_clear_all(tab->read_set);
+ for (uint fields= 0; *field_ptr; field_ptr++, fields++)
+ {
+ enum enum_field_types type= (*field_ptr)->type();
+ if (type < MYSQL_TYPE_MEDIUM_BLOB ||
+ type > MYSQL_TYPE_BLOB)
+ bitmap_set_bit(tab->read_set, fields);
+ else if (collect_eis)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NO_EIS_FOR_FIELD,
+ ER_THD(thd, ER_NO_EIS_FOR_FIELD),
+ (*field_ptr)->field_name);
+ }
+ }
+ else
+ {
+ int pos;
+ LEX_STRING *column_name;
+ List_iterator_fast<LEX_STRING> it(*lex->column_list);
- bitmap_clear_all(tab->read_set);
- while ((column_name= it++))
- {
- if (tab->s->fieldnames.type_names == 0 ||
- (pos= find_type(&tab->s->fieldnames, column_name->str,
- column_name->length, 1)) <= 0)
+ bitmap_clear_all(tab->read_set);
+ while ((column_name= it++))
{
- compl_result_code= result_code= HA_ADMIN_INVALID;
- break;
+ if (tab->s->fieldnames.type_names == 0 ||
+ (pos= find_type(&tab->s->fieldnames, column_name->str,
+ column_name->length, 1)) <= 0)
+ {
+ compl_result_code= result_code= HA_ADMIN_INVALID;
+ break;
+ }
+ pos--;
+ enum enum_field_types type= tab->field[pos]->type();
+ if (type < MYSQL_TYPE_MEDIUM_BLOB ||
+ type > MYSQL_TYPE_BLOB)
+ bitmap_set_bit(tab->read_set, pos);
+ else if (collect_eis)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NO_EIS_FOR_FIELD,
+ ER_THD(thd, ER_NO_EIS_FOR_FIELD),
+ column_name->str);
}
- bitmap_set_bit(tab->read_set, pos-1);
- }
- tab->file->column_bitmaps_signal();
+ tab->file->column_bitmaps_signal();
+ }
}
-
+ else
+ {
+ DBUG_ASSERT(!lex->column_list);
+ }
+
if (!lex->index_list)
{
tab->keys_in_use_for_query.init(tab->s->keys);
@@ -752,11 +790,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
}
- if (compl_result_code == HA_ADMIN_OK &&
- operator_func == &handler::ha_analyze &&
- table->table->s->table_category == TABLE_CATEGORY_USER &&
- (get_use_stat_tables_mode(thd) > NEVER ||
- lex->with_persistent_for_clause))
+ if (compl_result_code == HA_ADMIN_OK && collect_eis)
{
if (!(compl_result_code=
alloc_statistics_for_table(thd, table->table)) &&
@@ -1180,9 +1214,8 @@ bool Sql_cmd_analyze_table::execute(THD *thd)
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error;
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
thd->enable_slow_log= opt_log_slow_admin_statements;
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL);
-
res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
"analyze", lock_type, 1, 0, 0, 0,
&handler::ha_analyze, 0);
@@ -1237,7 +1270,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
thd->enable_slow_log= opt_log_slow_admin_statements;
res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
mysql_recreate_table(thd, first_table, true) :
@@ -1271,7 +1304,7 @@ bool Sql_cmd_repair_table::execute(THD *thd)
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
TL_WRITE, 1,
MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM),
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index b659054a50b..e2132efc528 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -24,6 +24,7 @@ extern int finalize_audit_plugin(st_plugin_int *plugin);
struct st_mysql_event_generic
{
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
unsigned int event_class;
const void *event;
};
@@ -32,8 +33,6 @@ unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
static mysql_mutex_t LOCK_audit_mask;
-static void event_class_dispatch(THD *, unsigned int, const void *);
-
static inline
void set_audit_mask(unsigned long *mask, uint event_class)
@@ -56,101 +55,6 @@ bool check_audit_mask(const unsigned long *lhs,
}
-typedef void (*audit_handler_t)(THD *thd, uint event_subtype, va_list ap);
-
-/**
- MYSQL_AUDIT_GENERAL_CLASS handler
-
- @param[in] thd
- @param[in] event_subtype
- @param[in] error_code
- @param[in] ap
-
-*/
-
-static void general_class_handler(THD *thd, uint event_subtype, va_list ap)
-{
- mysql_event_general event;
- event.event_subclass= event_subtype;
- event.general_error_code= va_arg(ap, int);
- event.general_thread_id= thd ? thd->thread_id : 0;
- event.general_time= va_arg(ap, time_t);
- event.general_user= va_arg(ap, const char *);
- event.general_user_length= va_arg(ap, unsigned int);
- event.general_command= va_arg(ap, const char *);
- event.general_command_length= va_arg(ap, unsigned int);
- event.general_query= va_arg(ap, const char *);
- event.general_query_length= va_arg(ap, unsigned int);
- event.general_charset= va_arg(ap, struct charset_info_st *);
- 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 ? thd->query_id : 0);
- event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
-}
-
-
-static void connection_class_handler(THD *thd, uint event_subclass, va_list ap)
-{
- mysql_event_connection event;
- event.event_subclass= event_subclass;
- event.status= va_arg(ap, int);
- event.thread_id= va_arg(ap, unsigned long);
- event.user= va_arg(ap, const char *);
- event.user_length= va_arg(ap, unsigned int);
- event.priv_user= va_arg(ap, const char *);
- event.priv_user_length= va_arg(ap, unsigned int);
- event.external_user= va_arg(ap, const char *);
- event.external_user_length= va_arg(ap, unsigned int);
- event.proxy_user= va_arg(ap, const char *);
- event.proxy_user_length= va_arg(ap, unsigned int);
- event.host= va_arg(ap, const char *);
- event.host_length= va_arg(ap, unsigned int);
- event.ip= va_arg(ap, const char *);
- event.ip_length= va_arg(ap, unsigned int);
- event.database= va_arg(ap, const char *);
- event.database_length= va_arg(ap, unsigned int);
- event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
-}
-
-
-static void table_class_handler(THD *thd, uint event_subclass, va_list ap)
-{
- mysql_event_table event;
- event.event_subclass= event_subclass;
- event.read_only= va_arg(ap, int);
- event.thread_id= va_arg(ap, unsigned long);
- event.user= va_arg(ap, const char *);
- event.priv_user= va_arg(ap, const char *);
- event.priv_host= va_arg(ap, const char *);
- event.external_user= va_arg(ap, const char *);
- event.proxy_user= va_arg(ap, const char *);
- event.host= va_arg(ap, const char *);
- event.ip= va_arg(ap, const char *);
- event.database= va_arg(ap, const char *);
- event.database_length= va_arg(ap, unsigned int);
- event.table= va_arg(ap, const char *);
- event.table_length= va_arg(ap, unsigned int);
- event.new_database= va_arg(ap, const char *);
- 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 ? thd->query_id : 0);
- event_class_dispatch(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
-}
-
-
-static audit_handler_t audit_handlers[] =
-{
- general_class_handler, connection_class_handler,
- 0,0,0,0,0,0,0,0,0,0,0,0,0, /* placeholders */
- table_class_handler
-};
-
-static const uint audit_handlers_count=
- (sizeof(audit_handlers) / sizeof(audit_handler_t));
-
-
/**
Acquire and lock any additional audit plugins as required
@@ -207,38 +111,16 @@ static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
DBUG_ENTER("mysql_audit_acquire_plugins");
- if (thd && !check_audit_mask(mysql_global_audit_mask, event_class_mask) &&
- check_audit_mask(thd->audit_class_mask, event_class_mask))
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(!check_audit_mask(mysql_global_audit_mask, event_class_mask));
+
+ if (check_audit_mask(thd->audit_class_mask, event_class_mask))
{
plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
add_audit_mask(thd->audit_class_mask, event_class_mask);
}
DBUG_VOID_RETURN;
}
-
-
-/**
- Notify the audit system of an event
-
- @param[in] thd
- @param[in] event_class
- @param[in] event_subtype
- @param[in] error_code
-
-*/
-
-void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
-{
- va_list ap;
- audit_handler_t *handlers= audit_handlers + event_class;
- DBUG_ASSERT(event_class < audit_handlers_count);
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
- set_audit_mask(event_class_mask, event_class);
- mysql_audit_acquire_plugins(thd, event_class_mask);
- va_start(ap, event_subtype);
- (*handlers)(thd, event_subtype, ap);
- va_end(ap);
-}
/**
@@ -496,17 +378,11 @@ static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg)
{
const struct st_mysql_event_generic *event_generic=
(const struct st_mysql_event_generic *) arg;
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
- set_audit_mask(event_class_mask, event_generic->event_class);
-
/* Check to see if the plugin is interested in this event */
- if (check_audit_mask(data->class_mask, event_class_mask))
- return 0;
-
- /* Actually notify the plugin */
- data->event_notify(thd, event_generic->event_class, event_generic->event);
+ if (!check_audit_mask(data->class_mask, event_generic->event_class_mask))
+ data->event_notify(thd, event_generic->event_class, event_generic->event);
return 0;
}
@@ -514,17 +390,18 @@ static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg)
/**
Distributes an audit event to plug-ins
-
+
@param[in] thd
+ @param[in] event_class
@param[in] event
*/
-static void event_class_dispatch(THD *thd, unsigned int event_class,
- const void *event)
+void mysql_audit_notify(THD *thd, uint event_class, const void *event)
{
struct st_mysql_event_generic event_generic;
event_generic.event_class= event_class;
event_generic.event= event;
+ set_audit_mask(event_generic.event_class_mask, event_class);
/*
Check if we are doing a slow global dispatch. This event occurs when
thd == NULL as it is not associated with any particular thread.
@@ -537,6 +414,8 @@ static void event_class_dispatch(THD *thd, unsigned int event_class,
{
plugin_ref *plugins, *plugins_last;
+ mysql_audit_acquire_plugins(thd, event_generic.event_class_mask);
+
/* Use the cached set of audit plugins */
plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
plugins_last= plugins + thd->audit_class_plugins.elements;
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 68106f099cc..d6f670538cd 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -35,8 +35,7 @@ extern void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask);
#ifndef EMBEDDED_LIBRARY
-extern void mysql_audit_notify(THD *thd, uint event_class,
- uint event_subtype, ...);
+extern void mysql_audit_notify(THD *thd, uint event_class, const void *event);
static inline bool mysql_audit_general_enabled()
{
@@ -55,7 +54,7 @@ static inline bool mysql_audit_table_enabled()
#else
static inline void mysql_audit_notify(THD *thd, uint event_class,
- uint event_subtype, ...) { }
+ const void *event) {}
#define mysql_audit_general_enabled() 0
#define mysql_audit_connection_enabled() 0
#define mysql_audit_table_enabled() 0
@@ -94,15 +93,37 @@ void mysql_audit_general_log(THD *thd, time_t time,
{
if (mysql_audit_general_enabled())
{
- 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,
- db, db_length);
+ mysql_event_general event;
+
+ event.event_subclass= MYSQL_AUDIT_GENERAL_LOG;
+ event.general_error_code= 0;
+ event.general_time= time;
+ event.general_user= user;
+ event.general_user_length= userlen;
+ event.general_command= cmd;
+ event.general_command_length= cmdlen;
+ event.general_query= query;
+ event.general_query_length= querylen;
+ event.general_rows= 0;
+
+ if (thd)
+ {
+ event.general_thread_id= (unsigned long)thd->thread_id;
+ event.general_charset= thd->variables.character_set_client;
+ event.database= thd->db;
+ event.database_length= thd->db_length;
+ event.query_id= thd->query_id;
+ }
+ else
+ {
+ event.general_thread_id= 0;
+ event.general_charset= global_system_variables.character_set_client;
+ event.database= "";
+ event.database_length= 0;
+ event.query_id= 0;
+ }
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
}
}
@@ -124,38 +145,41 @@ void mysql_audit_general(THD *thd, uint event_subtype,
{
if (mysql_audit_general_enabled())
{
- time_t time= my_time(0);
- uint msglen= msg ? strlen(msg) : 0;
- const char *user;
- uint userlen;
char user_buff[MAX_USER_HOST_SIZE];
- CSET_STRING query;
- ha_rows rows;
- const char *db;
- size_t db_length;
+ mysql_event_general event;
+
+ event.event_subclass= event_subtype;
+ event.general_error_code= error_code;
+ event.general_time= my_time(0);
+ event.general_command= msg;
+ event.general_command_length= safe_strlen(msg);
if (thd)
{
- query= thd->query_string;
- 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;
+ event.general_user= user_buff;
+ event.general_user_length= make_user_name(thd, user_buff);
+ event.general_thread_id= (unsigned long)thd->thread_id;
+ event.general_query= thd->query_string.str();
+ event.general_query_length= thd->query_string.length();
+ event.general_charset= thd->query_string.charset();
+ event.general_rows= thd->get_stmt_da()->current_row_for_warning();
+ event.database= thd->db;
+ event.database_length= thd->db_length;
+ event.query_id= thd->query_id;
}
else
{
- user= 0;
- userlen= 0;
- rows= 0;
- db= "";
- db_length= 0;
+ event.general_thread_id= 0;
+ event.general_query= NULL;
+ event.general_query_length= 0;
+ event.general_charset= &my_charset_bin;
+ event.general_rows= 0;
+ event.database= "";
+ event.database_length= 0;
+ event.query_id= 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,
- db, db_length);
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
}
}
@@ -165,19 +189,28 @@ void mysql_audit_notify_connection_connect(THD *thd)
if (mysql_audit_connection_enabled())
{
const Security_context *sctx= thd->security_ctx;
- Diagnostics_area *da= thd->get_stmt_da();
- mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
- MYSQL_AUDIT_CONNECTION_CONNECT,
- da->is_error() ? da->sql_errno() : 0,
- thd->thread_id,
- sctx->user, sctx->user ? strlen(sctx->user) : 0,
- sctx->priv_user, strlen(sctx->priv_user),
- sctx->external_user,
- sctx->external_user ? strlen(sctx->external_user) : 0,
- sctx->proxy_user, strlen(sctx->proxy_user),
- sctx->host, sctx->host ? strlen(sctx->host) : 0,
- sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
- thd->db, thd->db ? strlen(thd->db) : 0);
+ mysql_event_connection event;
+
+ event.event_subclass= MYSQL_AUDIT_CONNECTION_CONNECT;
+ event.status= thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.user_length= safe_strlen(sctx->user);
+ event.priv_user= sctx->priv_user;
+ event.priv_user_length= strlen(sctx->priv_user);
+ event.external_user= sctx->external_user;
+ event.external_user_length= safe_strlen(sctx->external_user);
+ event.proxy_user= sctx->proxy_user;
+ event.proxy_user_length= strlen(sctx->proxy_user);
+ event.host= sctx->host;
+ event.host_length= safe_strlen(sctx->host);
+ event.ip= sctx->ip;
+ event.ip_length= safe_strlen(sctx->ip);
+ event.database= thd->db;
+ event.database_length= safe_strlen(thd->db);
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
}
@@ -187,17 +220,27 @@ void mysql_audit_notify_connection_disconnect(THD *thd, int errcode)
if (mysql_audit_connection_enabled())
{
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
- MYSQL_AUDIT_CONNECTION_DISCONNECT,
- errcode, thd->thread_id,
- sctx->user, sctx->user ? strlen(sctx->user) : 0,
- sctx->priv_user, strlen(sctx->priv_user),
- sctx->external_user,
- sctx->external_user ? strlen(sctx->external_user) : 0,
- sctx->proxy_user, strlen(sctx->proxy_user),
- sctx->host, sctx->host ? strlen(sctx->host) : 0,
- sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
- thd->db, thd->db ? strlen(thd->db) : 0);
+ mysql_event_connection event;
+
+ event.event_subclass= MYSQL_AUDIT_CONNECTION_DISCONNECT;
+ event.status= errcode;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.user_length= safe_strlen(sctx->user);
+ event.priv_user= sctx->priv_user;
+ event.priv_user_length= strlen(sctx->priv_user);
+ event.external_user= sctx->external_user;
+ event.external_user_length= safe_strlen(sctx->external_user);
+ event.proxy_user= sctx->proxy_user;
+ event.proxy_user_length= strlen(sctx->proxy_user);
+ event.host= sctx->host;
+ event.host_length= safe_strlen(sctx->host);
+ event.ip= sctx->ip;
+ event.ip_length= safe_strlen(sctx->ip) ;
+ event.database= thd->db;
+ event.database_length= safe_strlen(thd->db);
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
}
@@ -207,19 +250,28 @@ void mysql_audit_notify_connection_change_user(THD *thd)
if (mysql_audit_connection_enabled())
{
const Security_context *sctx= thd->security_ctx;
- Diagnostics_area *da= thd->get_stmt_da();
- mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
- MYSQL_AUDIT_CONNECTION_CHANGE_USER,
- da->is_error() ? da->sql_errno() : 0,
- thd->thread_id,
- sctx->user, sctx->user ? strlen(sctx->user) : 0,
- sctx->priv_user, strlen(sctx->priv_user),
- sctx->external_user,
- sctx->external_user ? strlen(sctx->external_user) : 0,
- sctx->proxy_user, strlen(sctx->proxy_user),
- sctx->host, sctx->host ? strlen(sctx->host) : 0,
- sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
- thd->db, thd->db ? strlen(thd->db) : 0);
+ mysql_event_connection event;
+
+ event.event_subclass= MYSQL_AUDIT_CONNECTION_CHANGE_USER;
+ event.status= thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.user_length= safe_strlen(sctx->user);
+ event.priv_user= sctx->priv_user;
+ event.priv_user_length= strlen(sctx->priv_user);
+ event.external_user= sctx->external_user;
+ event.external_user_length= safe_strlen(sctx->external_user);
+ event.proxy_user= sctx->proxy_user;
+ event.proxy_user_length= strlen(sctx->proxy_user);
+ event.host= sctx->host;
+ event.host_length= safe_strlen(sctx->host);
+ event.ip= sctx->ip;
+ event.ip_length= safe_strlen(sctx->ip);
+ event.database= thd->db;
+ event.database_length= safe_strlen(thd->db);
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
}
@@ -229,13 +281,29 @@ void mysql_audit_external_lock(THD *thd, TABLE_SHARE *share, int lock)
if (lock != F_UNLCK && mysql_audit_table_enabled())
{
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_LOCK,
- (int)(lock == F_RDLCK), (ulong)thd->thread_id,
- sctx->user, sctx->priv_user, sctx->priv_host,
- sctx->external_user, sctx->proxy_user, sctx->host,
- sctx->ip, share->db.str, (uint)share->db.length,
- share->table_name.str, (uint)share->table_name.length,
- 0,0,0,0);
+ mysql_event_table event;
+
+ event.event_subclass= MYSQL_AUDIT_TABLE_LOCK;
+ event.read_only= lock == F_RDLCK;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.priv_user= sctx->priv_user;
+ event.priv_host= sctx->priv_host;
+ event.external_user= sctx->external_user;
+ event.proxy_user= sctx->proxy_user;
+ event.host= sctx->host;
+ event.ip= sctx->ip;
+ event.database= share->db.str;
+ event.database_length= share->db.length;
+ event.table= share->table_name.str;
+ event.table_length= share->table_name.length;
+ event.new_database= 0;
+ event.new_database_length= 0;
+ event.new_table= 0;
+ event.new_table_length= 0;
+ event.query_id= thd->query_id;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
@@ -247,13 +315,29 @@ void mysql_audit_create_table(TABLE *table)
THD *thd= table->in_use;
const TABLE_SHARE *share= table->s;
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_CREATE,
- 0, (ulong)thd->thread_id,
- sctx->user, sctx->priv_user, sctx->priv_host,
- sctx->external_user, sctx->proxy_user, sctx->host,
- sctx->ip, share->db.str, (uint)share->db.length,
- share->table_name.str, (uint)share->table_name.length,
- 0,0,0,0);
+ mysql_event_table event;
+
+ event.event_subclass= MYSQL_AUDIT_TABLE_CREATE;
+ event.read_only= 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.priv_user= sctx->priv_user;
+ event.priv_host= sctx->priv_host;
+ event.external_user= sctx->external_user;
+ event.proxy_user= sctx->proxy_user;
+ event.host= sctx->host;
+ event.ip= sctx->ip;
+ event.database= share->db.str;
+ event.database_length= share->db.length;
+ event.table= share->table_name.str;
+ event.table_length= share->table_name.length;
+ event.new_database= 0;
+ event.new_database_length= 0;
+ event.new_table= 0;
+ event.new_table_length= 0;
+ event.query_id= thd->query_id;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
@@ -263,13 +347,29 @@ void mysql_audit_drop_table(THD *thd, TABLE_LIST *table)
if (mysql_audit_table_enabled())
{
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_DROP,
- 0, (ulong)thd->thread_id,
- sctx->user, sctx->priv_user, sctx->priv_host,
- sctx->external_user, sctx->proxy_user, sctx->host,
- sctx->ip, table->db, (uint)table->db_length,
- table->table_name, (uint)table->table_name_length,
- 0,0,0,0);
+ mysql_event_table event;
+
+ event.event_subclass= MYSQL_AUDIT_TABLE_DROP;
+ event.read_only= 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.priv_user= sctx->priv_user;
+ event.priv_host= sctx->priv_host;
+ event.external_user= sctx->external_user;
+ event.proxy_user= sctx->proxy_user;
+ event.host= sctx->host;
+ event.ip= sctx->ip;
+ event.database= table->db;
+ event.database_length= table->db_length;
+ event.table= table->table_name;
+ event.table_length= table->table_name_length;
+ event.new_database= 0;
+ event.new_database_length= 0;
+ event.new_table= 0;
+ event.new_table_length= 0;
+ event.query_id= thd->query_id;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
@@ -280,13 +380,29 @@ void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb,
if (mysql_audit_table_enabled())
{
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_RENAME,
- 0, (ulong)thd->thread_id,
- sctx->user, sctx->priv_user, sctx->priv_host,
- sctx->external_user, sctx->proxy_user, sctx->host,
- sctx->ip,
- old_db, (uint)strlen(old_db), old_tb, (uint)strlen(old_tb),
- new_db, (uint)strlen(new_db), new_tb, (uint)strlen(new_tb));
+ mysql_event_table event;
+
+ event.event_subclass= MYSQL_AUDIT_TABLE_RENAME;
+ event.read_only= 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.priv_user= sctx->priv_user;
+ event.priv_host= sctx->priv_host;
+ event.external_user= sctx->external_user;
+ event.proxy_user= sctx->proxy_user;
+ event.host= sctx->host;
+ event.ip= sctx->ip;
+ event.database= old_db;
+ event.database_length= strlen(old_db);
+ event.table= old_tb;
+ event.table_length= strlen(old_tb);
+ event.new_database= new_db;
+ event.new_database_length= strlen(new_db);
+ event.new_table= new_tb;
+ event.new_table_length= strlen(new_tb);
+ event.query_id= thd->query_id;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
@@ -296,13 +412,29 @@ void mysql_audit_alter_table(THD *thd, TABLE_LIST *table)
if (mysql_audit_table_enabled())
{
const Security_context *sctx= thd->security_ctx;
- mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_ALTER,
- 0, (ulong)thd->thread_id,
- sctx->user, sctx->priv_user, sctx->priv_host,
- sctx->external_user, sctx->proxy_user, sctx->host,
- sctx->ip, table->db, (uint)table->db_length,
- table->table_name, (uint)table->table_name_length,
- 0,0,0,0);
+ mysql_event_table event;
+
+ event.event_subclass= MYSQL_AUDIT_TABLE_ALTER;
+ event.read_only= 0;
+ event.thread_id= (unsigned long)thd->thread_id;
+ event.user= sctx->user;
+ event.priv_user= sctx->priv_user;
+ event.priv_host= sctx->priv_host;
+ event.external_user= sctx->external_user;
+ event.proxy_user= sctx->proxy_user;
+ event.host= sctx->host;
+ event.ip= sctx->ip;
+ event.database= table->db;
+ event.database_length= table->db_length;
+ event.table= table->table_name;
+ event.table_length= table->table_name_length;
+ event.new_database= 0;
+ event.new_database_length= 0;
+ event.new_table= 0;
+ event.new_table_length= 0;
+ event.query_id= thd->query_id;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
}
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 20f602ce038..819f89d5ac4 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+ Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@
#include "transaction.h"
#include "sql_prepare.h"
#include "sql_statistics.h"
+#include "sql_cte.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -168,11 +169,6 @@ static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
TABLE_SHARE *table_share);
static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
-static bool
-has_write_table_with_auto_increment(TABLE_LIST *tables);
-static bool
-has_write_table_with_auto_increment_and_select(TABLE_LIST *tables);
-static bool has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables);
/**
@@ -353,7 +349,6 @@ void intern_close_table(TABLE *table)
table->s ? table->s->table_name.str : "?",
(long) table));
- free_io_cache(table);
delete table->triggers;
if (table->file) // Not true if placeholder
(void) closefrm(table, 1); // close file
@@ -363,21 +358,6 @@ void intern_close_table(TABLE *table)
}
-/* Free resources allocated by filesort() and read_record() */
-
-void free_io_cache(TABLE *table)
-{
- DBUG_ENTER("free_io_cache");
- if (table->sort.io_cache)
- {
- close_cached_file(table->sort.io_cache);
- my_free(table->sort.io_cache);
- table->sort.io_cache=0;
- }
- DBUG_VOID_RETURN;
-}
-
-
/**
Auxiliary function which allows to kill delayed threads for
particular table identified by its share.
@@ -1233,7 +1213,8 @@ bool close_temporary_tables(THD *thd)
*/
for (;
table && is_user_table(table) &&
- tmpkeyval(thd, table) == thd->variables.pseudo_thread_id &&
+ (ulong) tmpkeyval(thd, table) ==
+ (ulong) thd->variables.pseudo_thread_id &&
table->s->db.length == db.length() &&
memcmp(table->s->db.str, db.ptr(), db.length()) == 0;
table= next)
@@ -1815,7 +1796,6 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table->s->db.str, table->s->table_name.str));
- free_io_cache(table);
closefrm(table, 0);
if (delete_table)
rm_temporary_table(table_type, table->s->path.str);
@@ -2294,8 +2274,17 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
*/
if (dd_frm_is_view(thd, path))
{
- if (!tdc_open_view(thd, table_list, alias, key, key_length,
- CHECK_METADATA_VERSION))
+ /*
+ If parent_l of the table_list is non null then a merge table
+ has this view as child table, which is not supported.
+ */
+ if (table_list->parent_l)
+ {
+ my_error(ER_WRONG_MRG_TABLE, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ if (!tdc_open_view(thd, table_list, CHECK_METADATA_VERSION))
{
DBUG_ASSERT(table_list->view != 0);
DBUG_RETURN(FALSE); // VIEW
@@ -2413,10 +2402,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
retry_share:
- share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
- key, key_length,
- table_list->mdl_request.key.tc_hash_value(),
- gts_flags, &table);
+ share= tdc_acquire_share(thd, table_list, gts_flags, &table);
if (!share)
{
@@ -3266,9 +3252,6 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
@param thd Thread handle
@param table_list TABLE_LIST with db, table_name & belong_to_view
- @param alias Alias name
- @param cache_key Key for table definition cache
- @param cache_key_length Length of cache_key
@param flags Flags which modify how we open the view
@todo This function is needed for special handling of views under
@@ -3277,16 +3260,13 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
@return FALSE if success, TRUE - otherwise.
*/
-bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- const char *cache_key, uint cache_key_length,
- uint flags)
+bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags)
{
TABLE not_used;
TABLE_SHARE *share;
bool err= TRUE;
- if (!(share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
- cache_key, cache_key_length, GTS_VIEW)))
+ if (!(share= tdc_acquire_share(thd, table_list, GTS_VIEW)))
return TRUE;
DBUG_ASSERT(share->is_view);
@@ -3373,7 +3353,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
return result;
- if (!(share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE)))
+ if (!(share= tdc_acquire_share(thd, table_list, GTS_TABLE)))
goto end_free;
DBUG_ASSERT(! share->is_view);
@@ -3592,8 +3572,7 @@ Open_table_context::recover_from_failed_open()
if (open_if_exists)
m_thd->push_internal_handler(&no_such_table_handler);
- result= !tdc_acquire_share(m_thd, m_failed_table->db,
- m_failed_table->table_name,
+ result= !tdc_acquire_share(m_thd, m_failed_table,
GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK);
if (open_if_exists)
{
@@ -3925,6 +3904,26 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
tables->table_name= tables->view_name.str;
tables->table_name_length= tables->view_name.length;
}
+ else if (tables->select_lex)
+ {
+ /*
+ Check whether 'tables' refers to a table defined in a with clause.
+ If so set the reference to the definition in tables->with.
+ */
+ if (!tables->with)
+ tables->with= tables->select_lex->find_table_def_in_with_clauses(tables);
+ /*
+ If 'tables' is defined in a with clause set the pointer to the
+ specification from its definition in tables->derived.
+ */
+ if (tables->with)
+ {
+ if (tables->set_as_with_table(thd, tables->with))
+ DBUG_RETURN(1);
+ else
+ goto end;
+ }
+ }
/*
If this TABLE_LIST object is a placeholder for an information_schema
table, create a temporary table to represent the information_schema
@@ -5416,65 +5415,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
*(ptr++)= table->table;
}
- /*
- DML statements that modify a table with an auto_increment column based on
- rows selected from a table are unsafe as the order in which the rows are
- fetched fron the select tables cannot be determined and may differ on
- master and slave.
- */
- if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables &&
- has_write_table_with_auto_increment_and_select(tables))
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT);
- /* Todo: merge all has_write_table_auto_inc with decide_logging_format */
- if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables)
- {
- if (has_write_table_auto_increment_not_first_in_pk(tables))
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST);
- }
-
-#ifdef NOT_USED_IN_MARIADB
- /*
- INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
- can be unsafe.
- */
- uint unique_keys= 0;
- for (TABLE_LIST *query_table= tables; query_table && unique_keys <= 1;
- query_table= query_table->next_global)
- if(query_table->table)
- {
- uint keys= query_table->table->s->keys, i= 0;
- unique_keys= 0;
- for (KEY* keyinfo= query_table->table->s->key_info;
- i < keys && unique_keys <= 1; i++, keyinfo++)
- {
- if (keyinfo->flags & HA_NOSAME)
- unique_keys++;
- }
- if (!query_table->placeholder() &&
- query_table->lock_type >= TL_WRITE_ALLOW_WRITE &&
- unique_keys > 1 && thd->lex->sql_command == SQLCOM_INSERT &&
- /* Duplicate key update is not supported by INSERT DELAYED */
- thd->get_command() != COM_DELAYED_INSERT &&
- thd->lex->duplicates == DUP_UPDATE)
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
- }
-#endif
-
- /* We have to emulate LOCK TABLES if we are statement needs prelocking. */
- if (thd->lex->requires_prelocking())
- {
-
- /*
- A query that modifies autoinc column in sub-statement can make the
- master and slave inconsistent.
- We can solve these problems in mixed mode by switching to binlogging
- if at least one updated table is used by sub-statement
- */
- if (thd->wsrep_binlog_format() != BINLOG_FORMAT_ROW && tables &&
- has_write_table_with_auto_increment(thd->lex->first_not_own_table()))
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS);
- }
-
DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
@@ -8434,7 +8374,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
temporary table. Thus in this case we can be sure that 'item' is an
Item_field.
*/
- if (any_privileges)
+ if (any_privileges && !tables->is_with_table() && !tables->is_derived())
{
DBUG_ASSERT((tables->field_translation == NULL && table) ||
tables->is_natural_join);
@@ -8635,7 +8575,7 @@ bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
TODO
RETURN
- TRUE if some error occured (e.g. out of memory)
+ TRUE if some error occurred (e.g. out of memory)
FALSE if all is OK
*/
@@ -8745,7 +8685,7 @@ err_no_arena:
function.
@return Status
- @retval true An error occured.
+ @retval true An error occurred.
@retval false OK.
*/
@@ -8907,7 +8847,7 @@ static bool not_null_fields_have_null_values(TABLE *table)
record[1] buffers correspond to new and old versions of row respectively.
@return Status
- @retval true An error occured.
+ @retval true An error occurred.
@retval false OK.
*/
@@ -8967,7 +8907,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
function.
@return Status
- @retval true An error occured.
+ @retval true An error occurred.
@retval false OK.
*/
@@ -8980,6 +8920,9 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
Item *value;
Field *field;
bool abort_on_warning_saved= thd->abort_on_warning;
+ uint autoinc_index= table->next_number_field
+ ? table->next_number_field->field_index
+ : ~0U;
DBUG_ENTER("fill_record");
if (!*ptr)
@@ -9005,7 +8948,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
DBUG_ASSERT(field->table == table);
value=v++;
- if (field == table->next_number_field)
+ if (field->field_index == autoinc_index)
table->auto_increment_field_not_null= TRUE;
if (field->vcol_info &&
value->type() != Item::DEFAULT_VALUE_ITEM &&
@@ -9059,7 +9002,7 @@ err:
record[1] buffers correspond to new and old versions of row respectively.
@return Status
- @retval true An error occured.
+ @retval true An error occurred.
@retval false OK.
*/
@@ -9284,98 +9227,6 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
return a->length == b->length && !strncmp(a->str, b->str, a->length);
}
-
-/*
- Tells if two (or more) tables have auto_increment columns and we want to
- lock those tables with a write lock.
-
- SYNOPSIS
- has_two_write_locked_tables_with_auto_increment
- tables Table list
-
- NOTES:
- Call this function only when you have established the list of all tables
- which you'll want to update (including stored functions, triggers, views
- inside your statement).
-*/
-
-static bool
-has_write_table_with_auto_increment(TABLE_LIST *tables)
-{
- for (TABLE_LIST *table= tables; table; table= table->next_global)
- {
- /* we must do preliminary checks as table->table may be NULL */
- if (!table->placeholder() &&
- table->table->found_next_number_field &&
- (table->lock_type >= TL_WRITE_ALLOW_WRITE))
- return 1;
- }
-
- return 0;
-}
-
-/*
- checks if we have select tables in the table list and write tables
- with auto-increment column.
-
- SYNOPSIS
- has_two_write_locked_tables_with_auto_increment_and_select
- tables Table list
-
- RETURN VALUES
-
- -true if the table list has atleast one table with auto-increment column
-
-
- and atleast one table to select from.
- -false otherwise
-*/
-
-static bool
-has_write_table_with_auto_increment_and_select(TABLE_LIST *tables)
-{
- bool has_select= false;
- bool has_auto_increment_tables = has_write_table_with_auto_increment(tables);
- for(TABLE_LIST *table= tables; table; table= table->next_global)
- {
- if (!table->placeholder() &&
- (table->lock_type <= TL_READ_NO_INSERT))
- {
- has_select= true;
- break;
- }
- }
- return(has_select && has_auto_increment_tables);
-}
-
-/*
- Tells if there is a table whose auto_increment column is a part
- of a compound primary key while is not the first column in
- the table definition.
-
- @param tables Table list
-
- @return true if the table exists, fais if does not.
-*/
-
-static bool
-has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables)
-{
- for (TABLE_LIST *table= tables; table; table= table->next_global)
- {
- /* we must do preliminary checks as table->table may be NULL */
- if (!table->placeholder() &&
- table->table->found_next_number_field &&
- (table->lock_type >= TL_WRITE_ALLOW_WRITE)
- && table->table->s->next_number_keypart != 0)
- return 1;
- }
-
- return 0;
-}
-
-
-
/*
Open and lock system tables for read.
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 58cd18321b7..24a7c7a5a2e 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -267,7 +267,6 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
uint dt_phases);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
-void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
void kill_delayed_threads_for_table(TDC_element *element);
void close_thread_table(THD *thd, TABLE **table_ptr);
@@ -305,16 +304,7 @@ void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
ha_extra_function extra,
TABLE *skip_table);
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
-bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- const char *cache_key, uint cache_key_length, uint flags);
-
-static inline bool tdc_open_view(THD *thd, TABLE_LIST *table_list,
- const char *alias, uint flags)
-{
- const char *key;
- uint key_length= get_table_def_key(table_list, &key);
- return tdc_open_view(thd, table_list, alias, key, key_length, flags);
-}
+bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags);
TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
const char *table_name,
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 03505dec0cf..91dd8ad7325 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1933,6 +1933,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.autocommit));
memcpy((uchar *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
(uchar*) &flags, QUERY_CACHE_FLAGS_SIZE);
+
+#ifdef WITH_WSREP
+ bool once_more;
+ once_more= true;
+lookup:
+#endif /* WITH_WSREP */
+
query_block = (Query_cache_block *) my_hash_search(&queries, (uchar*) sql,
tot_length);
/* Quick abort on unlocked data */
@@ -1945,6 +1952,19 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block));
+#ifdef WITH_WSREP
+ if (once_more && WSREP_CLIENT(thd) && wsrep_must_sync_wait(thd))
+ {
+ unlock();
+ if (wsrep_sync_wait(thd))
+ goto err;
+ if (try_lock(thd, Query_cache::TIMEOUT))
+ goto err;
+ once_more= false;
+ goto lookup;
+ }
+#endif /* WITH_WSREP */
+
/* Now lock and test that nothing changed while blocks was unlocked */
BLOCK_LOCK_RD(query_block);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c5ad66200c8..e3b70566597 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2008, 2015, MariaDB
+ Copyright (c) 2008, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -702,12 +702,6 @@ extern "C"
@param length length of buffer
@param max_query_len how many chars of query to copy (0 for all)
- @req LOCK_thread_count
-
- @note LOCK_thread_count mutex is not necessary when the function is invoked on
- the currently running thread (current_thd) or if the caller in some other
- way guarantees that access to thd->query is serialized.
-
@return Pointer to string
*/
@@ -902,7 +896,7 @@ THD::THD(bool is_wsrep_applier)
wsrep_po_handle(WSREP_PO_INITIALIZER),
wsrep_po_cnt(0),
wsrep_apply_format(0),
- wsrep_skip_append_keys(false)
+ wsrep_ignore_table(false)
#endif
{
ulong tmp;
@@ -953,7 +947,7 @@ THD::THD(bool is_wsrep_applier)
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
user_time.val= start_time= start_time_sec_part= 0;
- start_utime= utime_after_query= prior_thr_create_utime= 0L;
+ start_utime= utime_after_query= 0;
utime_after_lock= 0L;
progress.arena= 0;
progress.report_to_client= 0;
@@ -1027,6 +1021,7 @@ THD::THD(bool is_wsrep_applier)
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
wsrep_info[sizeof(wsrep_info) - 1] = '\0'; /* make sure it is 0-terminated */
+ wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
#endif
/* Call to init() below requires fully initialized Open_tables_state. */
reset_open_tables_state(this);
@@ -1378,6 +1373,12 @@ extern "C" THD *_current_thd_noinline(void)
{
return my_pthread_getspecific_ptr(THD*,THR_THD);
}
+
+extern "C" my_thread_id next_thread_id_noinline()
+{
+#undef next_thread_id
+ return next_thread_id();
+}
#endif
/*
@@ -1425,6 +1426,7 @@ void THD::init(void)
bzero((char *) &org_status_var, sizeof(org_status_var));
start_bytes_received= 0;
last_commit_gtid.seq_no= 0;
+ last_stmt= NULL;
status_in_global= 0;
#ifdef WITH_WSREP
wsrep_exec_mode= wsrep_applier ? REPL_RECV : LOCAL_STATE;
@@ -1441,7 +1443,8 @@ void THD::init(void)
wsrep_mysql_replicated = 0;
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
-#endif
+ wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
+#endif /* WITH_WSREP */
if (variables.sql_log_bin)
variables.option_bits|= OPTION_BIN_LOG;
@@ -1628,6 +1631,10 @@ THD::~THD()
THD *orig_thd= current_thd;
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
+ /* Check that we have already called thd->unlink() */
+ DBUG_ASSERT(prev == 0 && next == 0);
+ /* This takes a long time so we should not do this under LOCK_thread_count */
+ mysql_mutex_assert_not_owner(&LOCK_thread_count);
/*
In error cases, thd may not be current thd. We have to fix this so
@@ -1699,8 +1706,9 @@ THD::~THD()
if (status_var.local_memory_used != 0)
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used));
- SAFEMALLOC_REPORT_MEMORY(my_thread_dbug_id());
- DBUG_ASSERT(status_var.local_memory_used == 0);
+ SAFEMALLOC_REPORT_MEMORY(thread_id);
+ DBUG_ASSERT(status_var.local_memory_used == 0 ||
+ !debug_assert_on_not_freed_memory);
}
set_current_thd(orig_thd == this ? 0 : orig_thd);
@@ -1817,7 +1825,8 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
void THD::awake(killed_state state_to_set)
{
DBUG_ENTER("THD::awake");
- DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd));
+ DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
+ this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this);
mysql_mutex_assert_owner(&LOCK_thd_data);
@@ -2199,6 +2208,10 @@ void THD::cleanup_after_query()
rgi_slave->cleanup_after_query();
#endif
+#ifdef WITH_WSREP
+ wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
+#endif /* WITH_WSREP */
+
DBUG_VOID_RETURN;
}
@@ -4024,6 +4037,12 @@ void thd_increment_bytes_sent(void *thd, ulong length)
}
}
+my_bool thd_net_is_killed()
+{
+ THD *thd= current_thd;
+ return thd && thd->killed ? 1 : 0;
+}
+
void thd_increment_bytes_received(void *thd, ulong length)
{
@@ -4066,7 +4085,7 @@ void Security_context::destroy()
// If not pointer to constant
if (host != my_localhost)
{
- my_free(host);
+ my_free((char*) host);
host= NULL;
}
if (user != delayed_user)
@@ -4329,7 +4348,7 @@ extern "C" void thd_progress_init(MYSQL_THD thd, uint max_stage)
is a high level command (like ALTER TABLE) and we are not in a
stored procedure
*/
- thd->progress.report= ((thd->client_capabilities & CLIENT_PROGRESS) &&
+ thd->progress.report= ((thd->client_capabilities & MARIADB_CLIENT_PROGRESS) &&
thd->progress.report_to_client &&
!thd->in_sub_stmt);
thd->progress.next_report_time= 0;
@@ -5158,9 +5177,7 @@ void THD::get_definer(LEX_USER *definer, bool role)
{
definer->user = invoker_user;
definer->host= invoker_host;
- definer->password= null_lex_str;
- definer->plugin= empty_lex_str;
- definer->auth= empty_lex_str;
+ definer->reset_auth();
}
else
#endif
@@ -5441,6 +5458,94 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg)
&argument);
}
+/*
+ Tells if two (or more) tables have auto_increment columns and we want to
+ lock those tables with a write lock.
+
+ SYNOPSIS
+ has_two_write_locked_tables_with_auto_increment
+ tables Table list
+
+ NOTES:
+ Call this function only when you have established the list of all tables
+ which you'll want to update (including stored functions, triggers, views
+ inside your statement).
+*/
+
+static bool
+has_write_table_with_auto_increment(TABLE_LIST *tables)
+{
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ /* we must do preliminary checks as table->table may be NULL */
+ if (!table->placeholder() &&
+ table->table->found_next_number_field &&
+ (table->lock_type >= TL_WRITE_ALLOW_WRITE))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ checks if we have select tables in the table list and write tables
+ with auto-increment column.
+
+ SYNOPSIS
+ has_two_write_locked_tables_with_auto_increment_and_select
+ tables Table list
+
+ RETURN VALUES
+
+ -true if the table list has atleast one table with auto-increment column
+
+
+ and atleast one table to select from.
+ -false otherwise
+*/
+
+static bool
+has_write_table_with_auto_increment_and_select(TABLE_LIST *tables)
+{
+ bool has_select= false;
+ bool has_auto_increment_tables = has_write_table_with_auto_increment(tables);
+ for(TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ if (!table->placeholder() &&
+ (table->lock_type <= TL_READ_NO_INSERT))
+ {
+ has_select= true;
+ break;
+ }
+ }
+ return(has_select && has_auto_increment_tables);
+}
+
+/*
+ Tells if there is a table whose auto_increment column is a part
+ of a compound primary key while is not the first column in
+ the table definition.
+
+ @param tables Table list
+
+ @return true if the table exists, fais if does not.
+*/
+
+static bool
+has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables)
+{
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ /* we must do preliminary checks as table->table may be NULL */
+ if (!table->placeholder() &&
+ table->table->found_next_number_field &&
+ (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ && table->table->s->next_number_keypart != 0)
+ return 1;
+ }
+
+ return 0;
+}
/**
Decide on logging format to use for the statement and issue errors
@@ -5625,6 +5730,31 @@ int THD::decide_logging_format(TABLE_LIST *tables)
}
#endif
+ if (wsrep_binlog_format() != BINLOG_FORMAT_ROW && tables)
+ {
+ /*
+ DML statements that modify a table with an auto_increment column based on
+ rows selected from a table are unsafe as the order in which the rows are
+ fetched fron the select tables cannot be determined and may differ on
+ master and slave.
+ */
+ if (has_write_table_with_auto_increment_and_select(tables))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT);
+
+ if (has_write_table_auto_increment_not_first_in_pk(tables))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST);
+
+ /*
+ A query that modifies autoinc column in sub-statement can make the
+ master and slave inconsistent.
+ We can solve these problems in mixed mode by switching to binlogging
+ if at least one updated table is used by sub-statement
+ */
+ if (lex->requires_prelocking() &&
+ has_write_table_with_auto_increment(lex->first_not_own_table()))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS);
+ }
+
/*
Get the capabilities vector for all involved storage engines and
mask out the flags for the binary log.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1365642b4fb..f2b8481ff7f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -695,6 +695,7 @@ typedef struct system_status_var
ulong com_create_tmp_table;
ulong com_drop_tmp_table;
ulong com_other;
+ ulong com_multi;
ulong com_stmt_prepare;
ulong com_stmt_reprepare;
@@ -704,6 +705,7 @@ typedef struct system_status_var
ulong com_stmt_reset;
ulong com_stmt_close;
+ ulong com_register_slave;
ulong created_tmp_disk_tables_;
ulong created_tmp_tables_;
ulong ha_commit_count;
@@ -1184,7 +1186,8 @@ public:
priv_user - The user privilege we are using. May be "" for anonymous user.
ip - client IP
*/
- char *host, *user, *ip;
+ const char *host;
+ char *user, *ip;
char priv_user[USERNAME_LENGTH];
char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
@@ -1411,7 +1414,7 @@ public:
Discrete_intervals_list auto_inc_intervals_forced;
ulonglong limit_found_rows;
ha_rows cuted_fields, sent_row_count, examined_row_count;
- ulong client_capabilities;
+ ulonglong client_capabilities;
ulong query_plan_flags;
uint in_sub_stmt;
bool enable_slow_log;
@@ -1962,6 +1965,13 @@ public:
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
+
+ /* Last created prepared statement */
+ Statement *last_stmt;
+ inline void set_last_stmt(Statement *stmt)
+ { last_stmt= (is_error() ? NULL : stmt); }
+ inline void clear_last_stmt() { last_stmt= NULL; }
+
/*
A pointer to the stack frame of handle_one_connection(),
which is called first in the thread for handling a client
@@ -2044,7 +2054,7 @@ public:
/* Needed by MariaDB semi sync replication */
Trans_binlog_info *semisync_info;
- ulong client_capabilities; /* What the client supports */
+ ulonglong client_capabilities; /* What the client supports */
ulong max_client_packet_length;
HASH handler_tables_hash;
@@ -2086,7 +2096,7 @@ public:
bool report_to_client;
/*
true, if we will send progress report packets to a client
- (client has requested them, see CLIENT_PROGRESS; report_to_client
+ (client has requested them, see MARIADB_CLIENT_PROGRESS; report_to_client
is true; not in sub-statement)
*/
bool report;
@@ -2177,7 +2187,7 @@ public:
int is_current_stmt_binlog_format_row() const {
DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
current_stmt_binlog_format == BINLOG_FORMAT_ROW);
- return WSREP_FORMAT(current_stmt_binlog_format) == BINLOG_FORMAT_ROW;
+ return current_stmt_binlog_format == BINLOG_FORMAT_ROW;
}
enum binlog_filter_state
@@ -3990,7 +4000,13 @@ public:
#endif /* GTID_SUPPORT */
void *wsrep_apply_format;
char wsrep_info[128]; /* string for dynamic proc info */
- bool wsrep_skip_append_keys;
+ /*
+ When enabled, do not replicate/binlog updates from the current table that's
+ being processed. At the moment, it is used to keep mysql.gtid_slave_pos
+ table updates from being replicated to other nodes via galera replication.
+ */
+ bool wsrep_ignore_table;
+ wsrep_gtid_t wsrep_sync_wait_gtid;
#endif /* WITH_WSREP */
/* Handling of timeouts for commands */
@@ -4026,8 +4042,41 @@ public:
{
main_lex.restore_set_statement_var();
}
+
+ /*
+ Reset current_linfo
+ Setting current_linfo to 0 needs to be done with LOCK_thread_count to
+ ensure that adjust_linfo_offsets doesn't use a structure that may
+ be deleted.
+ */
+ inline void reset_current_linfo()
+ {
+ mysql_mutex_lock(&LOCK_thread_count);
+ current_linfo= 0;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ }
};
+inline void add_to_active_threads(THD *thd)
+{
+ mysql_mutex_lock(&LOCK_thread_count);
+ threads.append(thd);
+ mysql_mutex_unlock(&LOCK_thread_count);
+}
+
+/*
+ This should be called when you want to delete a thd that was not
+ running any queries.
+ This function will assert if the THD was not linked.
+*/
+
+inline void unlink_not_visible_thd(THD *thd)
+{
+ thd->assert_if_linked();
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->unlink();
+ mysql_mutex_unlock(&LOCK_thread_count);
+}
/** A short cut for thd->get_stmt_da()->set_ok_status(). */
@@ -4460,10 +4509,14 @@ public:
#define TMP_ENGINE_COLUMNDEF MARIA_COLUMNDEF
#define TMP_ENGINE_HTON maria_hton
#define TMP_ENGINE_NAME "Aria"
+inline uint tmp_table_max_key_length() { return maria_max_key_length(); }
+inline uint tmp_table_max_key_parts() { return maria_max_key_segments(); }
#else
#define TMP_ENGINE_COLUMNDEF MI_COLUMNDEF
#define TMP_ENGINE_HTON myisam_hton
#define TMP_ENGINE_NAME "MyISAM"
+inline uint tmp_table_max_key_length() { return MI_MAX_KEY_LENGTH; }
+inline uint tmp_table_max_key_parts() { return MI_MAX_KEY_SEG; }
#endif
/*
@@ -4511,8 +4564,6 @@ public:
uint hidden_field_count;
uint group_parts,group_length,group_null_parts;
uint quick_group;
- /* If >0 convert all blob fields to varchar(convert_blob_length) */
- uint convert_blob_length;
/**
Enabled when we have atleast one outer_sum_func. Needed when used
along with distinct.
@@ -4548,7 +4599,7 @@ public:
TMP_TABLE_PARAM()
:copy_field(0), group_parts(0),
- group_length(0), group_null_parts(0), convert_blob_length(0),
+ group_length(0), group_null_parts(0),
using_outer_summary_function(0),
schema_table(0), materialized_subquery(0), force_not_null_cols(0),
precomputed_group_by(0),
@@ -4973,85 +5024,7 @@ class user_var_entry
user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
bool create_if_not_exists);
-/*
- Unique -- class for unique (removing of duplicates).
- Puts all values to the TREE. If the tree becomes too big,
- it's dumped to the file. User can request sorted values, or
- just iterate through them. In the last case tree merging is performed in
- memory simultaneously with iteration, so it should be ~2-3x faster.
- */
-
-class Unique :public Sql_alloc
-{
- DYNAMIC_ARRAY file_ptrs;
- ulong max_elements;
- ulonglong max_in_memory_size;
- IO_CACHE file;
- TREE tree;
- uchar *record_pointers;
- ulong filtered_out_elems;
- bool flush();
- uint size;
- uint full_size;
- uint min_dupl_count; /* always 0 for unions, > 0 for intersections */
- bool with_counters;
-
- bool merge(TABLE *table, uchar *buff, bool without_last_merge);
-
-public:
- ulong elements;
- Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
- uint size_arg, ulonglong max_in_memory_size_arg,
- uint min_dupl_count_arg= 0);
- ~Unique();
- ulong elements_in_tree() { return tree.elements_in_tree; }
- inline bool unique_add(void *ptr)
- {
- DBUG_ENTER("unique_add");
- DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements));
- if (!(tree.flag & TREE_ONLY_DUPS) &&
- tree.elements_in_tree >= max_elements && flush())
- DBUG_RETURN(1);
- DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg));
- }
-
- bool is_in_memory() { return (my_b_tell(&file) == 0); }
- void close_for_expansion() { tree.flag= TREE_ONLY_DUPS; }
-
- bool get(TABLE *table);
-
- /* Cost of searching for an element in the tree */
- inline static double get_search_cost(ulonglong tree_elems, uint compare_factor)
- {
- return log((double) tree_elems) / (compare_factor * M_LN2);
- }
-
- static double get_use_cost(uint *buffer, size_t nkeys, uint key_size,
- ulonglong max_in_memory_size, uint compare_factor,
- bool intersect_fl, bool *in_memory);
- inline static int get_cost_calc_buff_size(size_t nkeys, uint key_size,
- ulonglong max_in_memory_size)
- {
- register ulonglong max_elems_in_tree=
- max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size);
- return (int) (sizeof(uint)*(1 + nkeys/max_elems_in_tree));
- }
-
- void reset();
- bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg);
-
- uint get_size() const { return size; }
- ulonglong get_max_in_memory_size() const { return max_in_memory_size; }
-
- friend int unique_write_to_file(uchar* key, element_count count, Unique *unique);
- friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique);
-
- friend int unique_write_to_file_with_count(uchar* key, element_count count,
- Unique *unique);
- friend int unique_intersect_write_to_ptrs(uchar* key, element_count count,
- Unique *unique);
-};
-
+class SORT_INFO;
class multi_delete :public select_result_interceptor
{
@@ -5079,7 +5052,7 @@ public:
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
int do_deletes();
- int do_table_deletes(TABLE *table, bool ignore);
+ int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore);
bool send_eof();
inline ha_rows num_deleted()
{
@@ -5319,6 +5292,10 @@ public:
Do not check that wsrep snapshot is ready before allowing this command
*/
#define CF_SKIP_WSREP_CHECK (1U << 2)
+/**
+ Do not allow it for COM_MULTI batch
+*/
+#define CF_NO_COM_MULTI (1U << 3)
/* Inline functions */
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 904578134b4..92b74bb88ab 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -93,6 +93,8 @@ enum enum_sql_command {
SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE,
SQLCOM_COMPOUND,
SQLCOM_SHOW_GENERIC,
+ SQLCOM_ALTER_USER,
+ SQLCOM_SHOW_CREATE_USER,
/*
When a command is added here, be sure it's also added in mysqld.cc
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 7821b96b9bb..ea114bf40a5 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -827,14 +827,17 @@ bool thd_init_client_charset(THD *thd, uint cs_number)
Initialize connection threads
*/
+#ifndef EMBEDDED_LIBRARY
bool init_new_connection_handler_thread()
{
pthread_detach_this_thread();
if (my_thread_init())
{
+ statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
return 1;
}
+ DBUG_EXECUTE_IF("simulate_failed_connection_1", return(1); );
return 0;
}
@@ -850,7 +853,6 @@ bool init_new_connection_handler_thread()
1 error
*/
-#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
{
uint connect_errors= 0;
@@ -951,6 +953,7 @@ static int check_connection(THD *thd)
this is treated as a global server OOM error.
TODO: remove the need for my_strdup.
*/
+ statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by my_strdup(). */
}
@@ -968,7 +971,7 @@ static int check_connection(THD *thd)
if (thd->main_security_ctx.host)
{
if (thd->main_security_ctx.host != my_localhost)
- thd->main_security_ctx.host[MY_MIN(strlen(thd->main_security_ctx.host),
+ ((char*) thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
HOSTNAME_LENGTH)]= 0;
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
}
@@ -1016,6 +1019,7 @@ static int check_connection(THD *thd)
Hence, there is no reason to account on OOM conditions per client IP,
we count failures in the global server status instead.
*/
+ statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by alloc(). */
}
@@ -1054,7 +1058,8 @@ bool setup_connection_thread_globals(THD *thd)
{
close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thd->scheduler, end_thread, (thd, 0));
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ thd->scheduler->end_thread(thd, 0);
return 1; // Error
}
return 0;
@@ -1082,7 +1087,7 @@ bool login_connection(THD *thd)
int error= 0;
DBUG_ENTER("login_connection");
DBUG_PRINT("info", ("login_connection called by thread %lu",
- thd->thread_id));
+ (ulong) thd->thread_id));
/* Use "connect_timeout" value during connection phase */
my_net_set_read_timeout(net, connect_timeout);
@@ -1134,8 +1139,8 @@ void end_connection(THD *thd)
{
wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id);
if (rcode) {
- WSREP_WARN("wsrep failed to free connection context: %lu, code: %d",
- thd->thread_id, rcode);
+ WSREP_WARN("wsrep failed to free connection context: %lld code: %d",
+ (longlong) thd->thread_id, rcode);
}
}
thd->wsrep_client_thread= 0;
@@ -1254,11 +1259,11 @@ void prepare_new_connection_state(THD* thd)
pthread_handler_t handle_one_connection(void *arg)
{
- THD *thd= (THD*) arg;
+ CONNECT *connect= (CONNECT*) arg;
- mysql_thread_set_psi_id(thd->thread_id);
+ mysql_thread_set_psi_id(connect->thread_id);
- do_handle_one_connection(thd);
+ do_handle_one_connection(connect);
return 0;
}
@@ -1290,19 +1295,17 @@ bool thd_is_connection_alive(THD *thd)
return FALSE;
}
-void do_handle_one_connection(THD *thd_arg)
-{
- THD *thd= thd_arg;
-
- thd->thr_create_utime= microsecond_interval_timer();
- /* We need to set this because of time_out_user_resource_limits */
- thd->start_utime= thd->thr_create_utime;
- if (MYSQL_CALLBACK_ELSE(thd->scheduler, init_new_connection_thread, (), 0))
+void do_handle_one_connection(CONNECT *connect)
+{
+ ulonglong thr_create_utime= microsecond_interval_timer();
+ THD *thd;
+ if (connect->scheduler->init_new_connection_thread() ||
+ !(thd= connect->create_thd()))
{
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thd->scheduler, end_thread, (thd, 0));
+ scheduler_functions *scheduler= connect->scheduler;
+ connect->close_with_error(0, 0, ER_OUT_OF_RESOURCES);
+ scheduler->end_thread(0, 0);
return;
}
@@ -1311,14 +1314,22 @@ void do_handle_one_connection(THD *thd_arg)
increment slow_launch_threads counter if it took more than
slow_launch_time seconds to create the thread.
*/
- if (thd->prior_thr_create_utime)
+
+ if (connect->prior_thr_create_utime)
{
- ulong launch_time= (ulong) (thd->thr_create_utime -
- thd->prior_thr_create_utime);
+ ulong launch_time= (ulong) (thr_create_utime -
+ connect->prior_thr_create_utime);
if (launch_time >= slow_launch_time*1000000L)
statistic_increment(slow_launch_threads, &LOCK_status);
- thd->prior_thr_create_utime= 0;
}
+ delete connect;
+
+ /* Make THD visible in show processlist */
+ add_to_active_threads(thd);
+
+ thd->thr_create_utime= thr_create_utime;
+ /* We need to set this because of time_out_user_resource_limits */
+ thd->start_utime= thr_create_utime;
/*
handle_one_connection() is normally the only way a thread would
@@ -1365,7 +1376,7 @@ end_thread:
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
- if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
+ if (thd->scheduler->end_thread(thd, 1))
return; // Probably no-threads
/*
@@ -1377,3 +1388,110 @@ end_thread:
}
}
#endif /* EMBEDDED_LIBRARY */
+
+
+/* Handling of CONNECT objects */
+
+/*
+ Close connection without error and delete the connect object
+ This and close_with_error are only called if we didn't manage to
+ create a new thd object.
+*/
+
+void CONNECT::close_and_delete()
+{
+ DBUG_ENTER("close_and_delete");
+
+ if (vio)
+ vio_close(vio);
+ if (thread_count_incremented)
+ {
+ /*
+ Normally this is handled by THD::unlink. As we haven't yet created
+ a THD and put it in the thread list, we have to manage counting here.
+ */
+ dec_thread_count();
+ dec_connection_count(scheduler);
+ }
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ statistic_increment(aborted_connects,&LOCK_status);
+
+ delete this;
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Close a connection with a possible error to the end user
+ Alse deletes the connection object, like close_and_delete()
+*/
+
+void CONNECT::close_with_error(uint sql_errno,
+ const char *message, uint close_error)
+{
+ THD *thd= create_thd();
+ if (thd)
+ {
+ if (sql_errno)
+ net_send_error(thd, sql_errno, message, NULL);
+ close_connection(thd, close_error);
+ delete thd;
+ set_current_thd(0);
+ if (thread_count_incremented)
+ {
+ dec_thread_count();
+ dec_connection_count(scheduler);
+ }
+ delete this;
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ statistic_increment(aborted_connects,&LOCK_status);
+ }
+ else
+ {
+ /*
+ Out of memory; We can't generate an error, just close the connection
+ close_and_delete() will increment statistics.
+ */
+ close_and_delete();
+ }
+}
+
+
+CONNECT::~CONNECT()
+{
+ if (vio)
+ vio_delete(vio);
+}
+
+/* Create a THD based on a CONNECT object */
+
+THD *CONNECT::create_thd()
+{
+ my_bool res;
+ THD *thd;
+ DBUG_ENTER("create_thd");
+
+ DBUG_EXECUTE_IF("simulate_failed_connection_2", DBUG_RETURN(0); );
+
+ if (!(thd= new THD))
+ DBUG_RETURN(0);
+
+ set_current_thd(thd);
+ res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
+ vio= 0; // Vio now handled by thd
+
+ if (res)
+ {
+ delete thd;
+ set_current_thd(0);
+ DBUG_RETURN(0);
+ }
+
+ init_net_server_extension(thd);
+
+ thd->security_ctx->host= host;
+ thd->extra_port= extra_port;
+ thd->scheduler= scheduler;
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id;
+ thd->real_id= real_id;
+ DBUG_RETURN(thd);
+}
diff --git a/sql/sql_connect.h b/sql/sql_connect.h
index bab171606ba..22a12e845c7 100644
--- a/sql/sql_connect.h
+++ b/sql/sql_connect.h
@@ -19,8 +19,43 @@
#include "my_sys.h" /* pthread_handler_t */
#include "mysql_com.h" /* enum_server_command */
#include "structs.h"
+#include <mysql/psi/mysql_socket.h>
#include <hash.h>
+/*
+ Object to hold connect information to be given to the newly created thread
+*/
+
+struct scheduler_functions;
+
+class CONNECT : public ilink {
+public:
+ /* To be copied to THD */
+ Vio *vio; /* Copied to THD with my_net_init() */
+ const char *host;
+ scheduler_functions *scheduler;
+ my_thread_id thread_id;
+ pthread_t real_id;
+ bool extra_port;
+
+ /* Own variables */
+ bool thread_count_incremented;
+ ulonglong prior_thr_create_utime;
+
+ CONNECT()
+ :vio(0), host(0), scheduler(thread_scheduler), thread_id(0), real_id(0),
+ extra_port(0),
+ thread_count_incremented(0), prior_thr_create_utime(0)
+ {
+ };
+ ~CONNECT();
+ void close_and_delete();
+ void close_with_error(uint sql_errno,
+ const char *message, uint close_error);
+ THD *create_thd();
+};
+
+
class THD;
typedef struct st_lex_user LEX_USER;
typedef struct user_conn USER_CONN;
@@ -37,7 +72,7 @@ void free_global_index_stats(void);
void free_global_client_stats(void);
pthread_handler_t handle_one_connection(void *arg);
-void do_handle_one_connection(THD *thd_arg);
+void do_handle_one_connection(CONNECT *connect);
bool init_new_connection_handler_thread();
void reset_mqh(LEX_USER *lu, bool get_them);
bool check_mqh(THD *thd, uint check_command);
diff --git a/sql/sql_const.h b/sql/sql_const.h
index 76e47bd278b..31ee4603dc9 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -235,6 +235,8 @@
that does not respond to "initial server greeting" timely
*/
#define CONNECT_TIMEOUT 10
+ /* Wait 5 minutes before removing thread from thread cache */
+#define THREAD_CACHE_TIMEOUT 5*60
/* The following can also be changed from the command line */
#define DEFAULT_CONCURRENCY 10
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
new file mode 100644
index 00000000000..1203a4ce0c8
--- /dev/null
+++ b/sql/sql_cte.cc
@@ -0,0 +1,601 @@
+#include "sql_class.h"
+#include "sql_lex.h"
+#include "sql_cte.h"
+#include "sql_view.h" // for make_valid_column_names
+#include "sql_parse.h"
+
+
+/**
+ @brief
+ Check dependencies between tables defined in a list of with clauses
+
+ @param
+ with_clauses_list Pointer to the first clause in the list
+
+ @details
+ The procedure just calls the method With_clause::check_dependencies
+ for each member of the given list.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool check_dependencies_in_with_clauses(With_clause *with_clauses_list)
+{
+ for (With_clause *with_clause= with_clauses_list;
+ with_clause;
+ with_clause= with_clause->next_with_clause)
+ {
+ if (with_clause->check_dependencies())
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Check dependencies between tables defined in this with clause
+
+ @details
+ The method performs the following actions for this with clause:
+
+ 1. Test for definitions of the tables with the same name.
+ 2. For each table T defined in this with clause look for tables
+ from the same with clause that are used in the query that
+ specifies T and set the dependencies of T on these tables
+ in dependency_map.
+ 3. Build the transitive closure of the above direct dependencies
+ to find out all recursive definitions.
+ 4. If this with clause is not specified as recursive then
+ for each with table T defined in this with clause check whether
+ it is used in any definition that follows the definition of T.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool With_clause::check_dependencies()
+{
+ if (dependencies_are_checked)
+ return false;
+ /*
+ Look for for definitions with the same query name.
+ When found report an error and return true immediately.
+ For each table T defined in this with clause look for all other tables from
+ the same with with clause that are used in the specification of T.
+ For each such table set the dependency bit in the dependency map of
+ with element for T.
+ */
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ for (With_element *elem= first_elem;
+ elem != with_elem;
+ elem= elem->next_elem)
+ {
+ if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
+ elem->query_name->str) == 0)
+ {
+ my_error(ER_DUP_QUERY_NAME, MYF(0), with_elem->query_name->str);
+ return true;
+ }
+ }
+ with_elem->check_dependencies_in_unit(with_elem->spec);
+ }
+ /* Build the transitive closure of the direct dependencies found above */
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ table_map with_elem_map= with_elem->get_elem_map();
+ for (With_element *elem= first_elem; elem != NULL; elem= elem->next_elem)
+ {
+ if (elem->dependency_map & with_elem_map)
+ elem->dependency_map |= with_elem->dependency_map;
+ }
+ }
+
+ /*
+ Mark those elements where tables are defined with direct or indirect recursion.
+ Report an error when recursion (direct or indirect) is used to define a table.
+ */
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ if (with_elem->dependency_map & with_elem->get_elem_map())
+ with_elem->is_recursive= true;
+ }
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ if (with_elem->is_recursive)
+ {
+ my_error(ER_RECURSIVE_QUERY_IN_WITH_CLAUSE, MYF(0),
+ with_elem->query_name->str);
+ return true;
+ }
+ }
+
+ if (!with_recursive)
+ {
+ /*
+ For each with table T defined in this with clause check whether
+ it is used in any definition that follows the definition of T.
+ */
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ With_element *checked_elem= with_elem->next_elem;
+ for (uint i = with_elem->number+1;
+ i < elements;
+ i++, checked_elem= checked_elem->next_elem)
+ {
+ if (with_elem->check_dependency_on(checked_elem))
+ {
+ my_error(ER_WRONG_ORDER_IN_WITH_CLAUSE, MYF(0),
+ with_elem->query_name->str, checked_elem->query_name->str);
+ return true;
+ }
+ }
+ }
+ }
+
+ dependencies_are_checked= true;
+ return false;
+}
+
+
+/**
+ @brief
+ Check dependencies on the sibling with tables used in the given unit
+
+ @param unit The unit where the siblings are to be searched for
+
+ @details
+ The method recursively looks through all from lists encountered
+ the given unit. If it finds a reference to a table that is
+ defined in the same with clause to which this element belongs
+ the method set the bit of dependency on this table in the
+ dependency_map of this element.
+*/
+
+void With_element::check_dependencies_in_unit(st_select_lex_unit *unit)
+{
+ st_select_lex *sl= unit->first_select();
+ for (; sl; sl= sl->next_select())
+ {
+ for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local)
+ {
+ if (!tbl->with)
+ tbl->with= owner->find_table_def(tbl);
+ if (!tbl->with && tbl->select_lex)
+ tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
+ if (tbl->with && tbl->with->owner== this->owner)
+ set_dependency_on(tbl->with);
+ }
+ st_select_lex_unit *inner_unit= sl->first_inner_unit();
+ for (; inner_unit; inner_unit= inner_unit->next_unit())
+ check_dependencies_in_unit(inner_unit);
+ }
+}
+
+
+/**
+ @brief
+ Search for the definition of a table among the elements of this with clause
+
+ @param table The reference to the table that is looked for
+
+ @details
+ The function looks through the elements of this with clause trying to find
+ the definition of the given table. When it encounters the element with
+ the same query name as the table's name it returns this element. If no
+ such definitions are found the function returns NULL.
+
+ @retval
+ found with element if the search succeeded
+ NULL - otherwise
+*/
+
+With_element *With_clause::find_table_def(TABLE_LIST *table)
+{
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ if (my_strcasecmp(system_charset_info, with_elem->query_name->str, table->table_name) == 0)
+ {
+ return with_elem;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ @brief
+ Perform context analysis for all unreferenced tables defined in with clause
+
+ @param thd The context of the statement containing this with clause
+
+ @details
+ For each unreferenced table T defined in this with clause the method
+ calls the method With_element::prepare_unreferenced that performs
+ context analysis of the element with the definition of T.
+
+ @retval
+ false If context analysis does not report any error
+ true Otherwise
+*/
+
+bool With_clause::prepare_unreferenced_elements(THD *thd)
+{
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ if (!with_elem->is_referenced() && with_elem->prepare_unreferenced(thd))
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ @brief
+ Save the specification of the given with table as a string
+
+ @param thd The context of the statement containing this with element
+ @param spec_start The beginning of the specification in the input string
+ @param spec_end The end of the specification in the input string
+
+ @details
+ The method creates for a string copy of the specification used in this element.
+ The method is called when the element is parsed. The copy may be used to
+ create clones of the specification whenever they are needed.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end)
+{
+ unparsed_spec.length= spec_end - spec_start;
+ unparsed_spec.str= (char*) thd->memdup(spec_start, unparsed_spec.length+1);
+ unparsed_spec.str[unparsed_spec.length]= '\0';
+
+ if (!unparsed_spec.str)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(unparsed_spec.length));
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Create a clone of the specification for the given with table
+
+ @param thd The context of the statement containing this with element
+ @param with_table The reference to the table defined in this element for which
+ the clone is created.
+
+ @details
+ The method creates a clone of the specification used in this element.
+ The clone is created for the given reference to the table defined by
+ this element.
+ The clone is created when the string with the specification saved in
+ unparsed_spec is fed into the parser as an input string. The parsing
+ this string a unit object representing the specification is build.
+ A chain of all table references occurred in the specification is also
+ formed.
+ The method includes the new unit and its sub-unit into hierarchy of
+ the units of the main query. I also insert the constructed chain of the
+ table references into the chain of all table references of the main query.
+
+ @note
+ Clones is created only for not first references to tables defined in
+ the with clause. They are necessary for merged specifications because
+ the optimizer handles any such specification as independent on the others.
+ When a table defined in the with clause is materialized in a temporary table
+ one could do without specification clones. However in this case they
+ are created as well, because currently different table references to a
+ the same temporary table cannot share the same definition structure.
+
+ @retval
+ pointer to the built clone if succeeds
+ NULL - otherwise
+*/
+
+st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
+ TABLE_LIST *with_table)
+{
+ LEX *lex;
+ st_select_lex_unit *res= NULL;
+ Query_arena backup;
+ Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ if (!(lex= (LEX*) new(thd->mem_root) st_lex_local))
+ {
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ return res;
+ }
+ LEX *old_lex= thd->lex;
+ thd->lex= lex;
+
+ bool parse_status= false;
+ Parser_state parser_state;
+ TABLE_LIST *spec_tables;
+ TABLE_LIST *spec_tables_tail;
+ st_select_lex *with_select;
+
+ if (parser_state.init(thd, unparsed_spec.str, unparsed_spec.length))
+ goto err;
+ lex_start(thd);
+ with_select= &lex->select_lex;
+ with_select->select_number= ++thd->select_number;
+ parse_status= parse_sql(thd, &parser_state, 0);
+ if (parse_status)
+ goto err;
+ spec_tables= lex->query_tables;
+ spec_tables_tail= 0;
+ for (TABLE_LIST *tbl= spec_tables;
+ tbl;
+ tbl= tbl->next_global)
+ {
+ tbl->grant.privilege= with_table->grant.privilege;
+ spec_tables_tail= tbl;
+ }
+ if (spec_tables)
+ {
+ if (with_table->next_global)
+ {
+ spec_tables_tail->next_global= with_table->next_global;
+ with_table->next_global->prev_global= &spec_tables_tail->next_global;
+ }
+ else
+ {
+ old_lex->query_tables_last= &spec_tables_tail->next_global;
+ }
+ spec_tables->prev_global= &with_table->next_global;
+ with_table->next_global= spec_tables;
+ }
+ res= &lex->unit;
+
+ lex->unit.include_down(with_table->select_lex);
+ lex->unit.set_slave(with_select);
+ old_lex->all_selects_list=
+ (st_select_lex*) (lex->all_selects_list->
+ insert_chain_before(
+ (st_select_lex_node **) &(old_lex->all_selects_list),
+ with_select));
+ lex_end(lex);
+err:
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->lex= old_lex;
+ return res;
+}
+
+
+/**
+ @brief
+ Rename columns of the unit derived from the spec of this with element
+ @param thd The context of the statement containing the with element
+ @param unit The specification of the with element or its clone
+
+ @details
+ The method assumes that the parameter unit is either specification itself
+ of this with element or a clone of this specification. The looks through
+ the column list in this with element. It reports an error if the cardinality
+ of this list differs from the cardinality of select lists in 'unit'.
+ Otherwise it renames the columns of the first select list and sets the flag
+ unit->column_list_is_processed to true preventing renaming columns for the
+ second time.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool
+With_element::rename_columns_of_derived_unit(THD *thd,
+ st_select_lex_unit *unit)
+{
+ if (unit->columns_are_renamed)
+ return false;
+
+ st_select_lex *select= unit->first_select();
+
+ if (column_list.elements) // The column list is optional
+ {
+ List_iterator_fast<Item> it(select->item_list);
+ List_iterator_fast<LEX_STRING> nm(column_list);
+ Item *item;
+ LEX_STRING *name;
+
+ if (column_list.elements != select->item_list.elements)
+ {
+ my_error(ER_WITH_COL_WRONG_LIST, MYF(0));
+ return true;
+ }
+ /* Rename the columns of the first select in the unit */
+ while ((item= it++, name= nm++))
+ {
+ item->set_name(thd, name->str, (uint) name->length, system_charset_info);
+ item->is_autogenerated_name= false;
+ }
+ }
+
+ make_valid_column_names(thd, select->item_list);
+
+ unit->columns_are_renamed= true;
+
+ return false;
+}
+
+
+/**
+ @brief
+ Perform context analysis the definition of an unreferenced table
+
+ @param thd The context of the statement containing this with element
+
+ @details
+ The method assumes that this with element contains the definition
+ of a table that is not used anywhere. In this case one has to check
+ that context conditions are met.
+
+ @retval
+ true if an error was reported
+ false otherwise
+*/
+
+bool With_element::prepare_unreferenced(THD *thd)
+{
+ bool rc= false;
+ st_select_lex *first_sl= spec->first_select();
+
+ /* Prevent name resolution for field references out of with elements */
+ for (st_select_lex *sl= first_sl;
+ sl;
+ sl= sl->next_select())
+ sl->context.outer_context= 0;
+
+ thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
+ if (!spec->prepared &&
+ (spec->prepare(thd, 0, 0) ||
+ rename_columns_of_derived_unit(thd, spec) ||
+ check_duplicate_names(thd, first_sl->item_list, 1)))
+ rc= true;
+
+ thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
+ return rc;
+}
+
+
+/**
+ @brief
+ Search for the definition of the given table referred in this select node
+
+ @param table reference to the table whose definition is searched for
+
+ @details
+ The method looks for the definition the table whose reference is occurred
+ in the FROM list of this select node. First it searches for it in the
+ with clause attached to the unit this select node belongs to. If such a
+ definition is not found there the embedding units are looked through.
+
+ @retval
+ pointer to the found definition if the search has been successful
+ NULL - otherwise
+*/
+
+With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
+{
+ With_element *found= NULL;
+ for (st_select_lex *sl= this;
+ sl;
+ sl= sl->master_unit()->outer_select())
+ {
+ With_clause *with_clause=sl->get_with_clause();
+ if (with_clause && (found= with_clause->find_table_def(table)))
+ return found;
+ }
+ return found;
+}
+
+
+/**
+ @brief
+ Set the specifying unit in this reference to a with table
+
+ @details
+ The method assumes that the given element with_elem defines the table T
+ this table reference refers to.
+ If this is the first reference to T the method just sets its specification
+ in the field 'derived' as the unit that yields T. Otherwise the method
+ first creates a clone specification and sets rather this clone in this field.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
+{
+ with= with_elem;
+ if (!with_elem->is_referenced())
+ derived= with_elem->spec;
+ else
+ {
+ if(!(derived= with_elem->clone_parsed_spec(thd, this)))
+ return true;
+ derived->with_element= with_elem;
+ }
+ with_elem->inc_references();
+ return false;
+}
+
+
+/**
+ @brief
+ Print this with clause
+
+ @param str Where to print to
+ @param query_type The mode of printing
+
+ @details
+ The method prints a string representation of this clause in the
+ string str. The parameter query_type specifies the mode of printing.
+*/
+
+void With_clause::print(String *str, enum_query_type query_type)
+{
+ str->append(STRING_WITH_LEN("WITH "));
+ if (with_recursive)
+ str->append(STRING_WITH_LEN("RECURSIVE "));
+ for (With_element *with_elem= first_elem;
+ with_elem != NULL;
+ with_elem= with_elem->next_elem)
+ {
+ with_elem->print(str, query_type);
+ if (with_elem != first_elem)
+ str->append(", ");
+ }
+}
+
+
+/**
+ @brief
+ Print this with element
+
+ @param str Where to print to
+ @param query_type The mode of printing
+
+ @details
+ The method prints a string representation of this with element in the
+ string str. The parameter query_type specifies the mode of printing.
+*/
+
+void With_element::print(String *str, enum_query_type query_type)
+{
+ str->append(query_name);
+ str->append(STRING_WITH_LEN(" AS "));
+ str->append('(');
+ spec->print(str, query_type);
+ str->append(')');
+}
+
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
new file mode 100644
index 00000000000..0cbc9247af9
--- /dev/null
+++ b/sql/sql_cte.h
@@ -0,0 +1,178 @@
+#ifndef SQL_CTE_INCLUDED
+#define SQL_CTE_INCLUDED
+#include "sql_list.h"
+#include "sql_lex.h"
+
+class With_clause;
+
+/**
+ @class With_clause
+ @brief Set of with_elements
+
+ It has a reference to the first with element from this with clause.
+ This reference allows to navigate through all the elements of the with clause.
+ It contains a reference to the unit to which this with clause is attached.
+ It also contains a flag saying whether this with clause was specified as recursive.
+*/
+
+class With_element : public Sql_alloc
+{
+private:
+ With_clause *owner; // with clause this object belongs to
+ With_element *next_elem; // next element in the with clause
+ uint number; // number of the element in the with clause (starting from 0)
+ /*
+ The map dependency_map has 1 in the i-th position if the query that
+ specifies this element contains a reference to the element number i
+ in the query FROM list.
+ */
+ table_map elem_map; // The map where with only one 1 set in this->number
+ table_map dependency_map;
+ /*
+ Total number of references to this element in the FROM lists of
+ the queries that are in the scope of the element (including
+ subqueries and specifications of other with elements).
+ */
+ uint references;
+ /*
+ Unparsed specification of the query that specifies this element.
+ It used to build clones of the specification if they are needed.
+ */
+ LEX_STRING unparsed_spec;
+
+ /* Return the map where 1 is set only in the position for this element */
+ table_map get_elem_map() { return 1 << number; }
+
+public:
+ /*
+ The name of the table introduced by this with elememt. The name
+ can be used in FROM lists of the queries in the scope of the element.
+ */
+ LEX_STRING *query_name;
+ /*
+ Optional list of column names to name the columns of the table introduced
+ by this with element. It is used in the case when the names are not
+ inherited from the query that specified the table. Otherwise the list is
+ always empty.
+ */
+ List <LEX_STRING> column_list;
+ /* The query that specifies the table introduced by this with element */
+ st_select_lex_unit *spec;
+ /*
+ Set to true is recursion is used (directly or indirectly)
+ for the definition of this element
+ */
+ bool is_recursive;
+
+ With_element(LEX_STRING *name,
+ List <LEX_STRING> list,
+ st_select_lex_unit *unit)
+ : next_elem(NULL), dependency_map(0), references(0),
+ query_name(name), column_list(list), spec(unit),
+ is_recursive(false) {}
+
+ void check_dependencies_in_unit(st_select_lex_unit *unit);
+
+ void set_dependency_on(With_element *with_elem)
+ { dependency_map|= with_elem->get_elem_map(); }
+
+ bool check_dependency_on(With_element *with_elem)
+ { return dependency_map & with_elem->get_elem_map(); }
+
+ bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end);
+
+ st_select_lex_unit *clone_parsed_spec(THD *thd, TABLE_LIST *with_table);
+
+ bool is_referenced() { return references != 0; }
+
+ void inc_references() { references++; }
+
+ bool rename_columns_of_derived_unit(THD *thd, st_select_lex_unit *unit);
+
+ bool prepare_unreferenced(THD *thd);
+
+ void print(String *str, enum_query_type query_type);
+
+ friend class With_clause;
+};
+
+
+/**
+ @class With_element
+ @brief Definition of a CTE table
+
+ It contains a reference to the name of the table introduced by this with element,
+ and a reference to the unit that specificies this table. Also it contains
+ a reference to the with clause to which this element belongs to.
+*/
+
+class With_clause : public Sql_alloc
+{
+private:
+ st_select_lex_unit *owner; // the unit this with clause attached to
+ With_element *first_elem; // the first definition in this with clause
+ With_element **last_next; // here is set the link for the next added element
+ uint elements; // number of the elements/defintions in this with clauses
+ /*
+ The with clause immediately containing this with clause if there is any,
+ otherwise NULL. Now used only at parsing.
+ */
+ With_clause *embedding_with_clause;
+ /*
+ The next with the clause of the chain of with clauses encountered
+ in the current statement
+ */
+ With_clause *next_with_clause;
+ /* Set to true if dependencies between with elements have been checked */
+ bool dependencies_are_checked;
+
+public:
+ /* If true the specifier RECURSIVE is present in the with clause */
+ bool with_recursive;
+
+ With_clause(bool recursive_fl, With_clause *emb_with_clause)
+ : owner(NULL), first_elem(NULL), elements(0),
+ embedding_with_clause(emb_with_clause), next_with_clause(NULL),
+ dependencies_are_checked(false),
+ with_recursive(recursive_fl)
+ { last_next= &first_elem; }
+
+ /* Add a new element to the current with clause */
+ bool add_with_element(With_element *elem)
+ {
+ elem->owner= this;
+ elem->number= elements;
+ owner= elem->spec;
+ owner->with_element= elem;
+ *last_next= elem;
+ last_next= &elem->next_elem;
+ elements++;
+ return false;
+ }
+
+ /* Add this with clause to the list of with clauses used in the statement */
+ void add_to_list(With_clause ** &last_next)
+ {
+ *last_next= this;
+ last_next= &this->next_with_clause;
+ }
+
+ With_clause *pop() { return embedding_with_clause; }
+
+ bool check_dependencies();
+
+ With_element *find_table_def(TABLE_LIST *table);
+
+ With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
+
+ bool prepare_unreferenced_elements(THD *thd);
+
+ void print(String *str, enum_query_type query_type);
+
+ friend
+ bool check_dependencies_in_with_clauses(With_clause *with_clauses_list);
+
+};
+
+
+#endif /* SQL_CTE_INCLUDED */
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 3377a6736a0..0a76ee0a699 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -40,6 +40,8 @@
#include "sql_statistics.h"
#include "transaction.h"
#include "records.h" // init_read_record,
+#include "filesort.h"
+#include "uniques.h"
#include "sql_derived.h" // mysql_handle_list_of_derived
// end_read_record
#include "sql_partition.h" // make_used_partitions_str
@@ -227,10 +229,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
int error, loc_error;
TABLE *table;
SQL_SELECT *select=0;
+ SORT_INFO *file_sort= 0;
READ_RECORD info;
bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, safe_update, const_cond;
bool const_cond_result;
+ bool return_error= 0;
ha_rows deleted= 0;
bool reverse= FALSE;
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
@@ -405,7 +409,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->covering_keys.clear_all();
table->quick_keys.clear_all(); // Can't use 'only index'
- select=make_select(table, 0, 0, conds, 0, &error);
+ select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
if (error)
DBUG_RETURN(TRUE);
if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
@@ -486,28 +490,17 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (query_plan.using_filesort)
{
- ha_rows examined_rows;
- ha_rows found_rows;
{
Filesort fsort(order, HA_POS_ERROR, select);
DBUG_ASSERT(query_plan.index == MAX_KEY);
- table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL |
- MY_THREAD_SPECIFIC));
Filesort_tracker *fs_tracker=
thd->lex->explain->get_upd_del_plan()->filesort_tracker;
- if ((table->sort.found_records= filesort(thd, table, &fsort, true,
- &examined_rows, &found_rows,
- fs_tracker))
- == HA_POS_ERROR)
- {
- delete select;
- free_underlaid_joins(thd, &thd->lex->select_lex);
- DBUG_RETURN(TRUE);
- }
- thd->inc_examined_row_count(examined_rows);
+ if (!(file_sort= filesort(thd, table, &fsort, true, fs_tracker)))
+ goto got_error;
+
+ thd->inc_examined_row_count(file_sort->examined_rows);
/*
Filesort has already found and selected the rows we want to delete,
so we don't need the where clause
@@ -527,24 +520,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
/* If quick select is used, initialize it before retrieving rows. */
if (select && select->quick && select->quick->reset())
- {
- delete select;
- free_underlaid_joins(thd, select_lex);
- DBUG_RETURN(TRUE);
- }
+ goto got_error;
if (query_plan.index == MAX_KEY || (select && select->quick))
- error= init_read_record(&info, thd, table, select, 1, 1, FALSE);
+ error= init_read_record(&info, thd, table, select, file_sort, 1, 1, FALSE);
else
error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
reverse);
if (error)
- {
- delete select;
- free_underlaid_joins(thd, select_lex);
- DBUG_RETURN(TRUE);
- }
-
+ goto got_error;
+
init_ftfuncs(thd, select_lex, 1);
THD_STAGE_INFO(thd, stage_updating);
@@ -700,8 +685,6 @@ cleanup:
}
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
-
- free_underlaid_joins(thd, select_lex);
if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
@@ -714,6 +697,8 @@ cleanup:
my_ok(thd, deleted);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
+ delete file_sort;
+ free_underlaid_joins(thd, select_lex);
DBUG_RETURN(error >= 0 || thd->is_error());
/* Special exits */
@@ -732,9 +717,16 @@ send_nothing_and_leave:
*/
delete select;
+ delete file_sort;
free_underlaid_joins(thd, select_lex);
//table->set_keyread(false);
- DBUG_RETURN((thd->is_error() || thd->killed) ? 1 : 0);
+
+ DBUG_ASSERT(!return_error || thd->is_error() || thd->killed);
+ DBUG_RETURN((return_error || thd->is_error() || thd->killed) ? 1 : 0);
+
+got_error:
+ return_error= 1;
+ goto send_nothing_and_leave;
}
@@ -1187,7 +1179,8 @@ int multi_delete::do_deletes()
if (tempfiles[counter]->get(table))
DBUG_RETURN(1);
- local_error= do_table_deletes(table, thd->lex->ignore);
+ local_error= do_table_deletes(table, &tempfiles[counter]->sort,
+ thd->lex->ignore);
if (thd->killed && !local_error)
DBUG_RETURN(1);
@@ -1217,14 +1210,15 @@ int multi_delete::do_deletes()
@retval 1 Triggers or handler reported error.
@retval -1 End of file from handler.
*/
-int multi_delete::do_table_deletes(TABLE *table, bool ignore)
+int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info,
+ bool ignore)
{
int local_error= 0;
READ_RECORD info;
ha_rows last_deleted= deleted;
DBUG_ENTER("do_deletes_for_table");
- if (init_read_record(&info, thd, table, NULL, 0, 1, FALSE))
+ if (init_read_record(&info, thd, table, NULL, sort_info, 0, 1, FALSE))
DBUG_RETURN(1);
/*
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 53628335508..79e57cded81 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -30,6 +30,7 @@
#include "sql_base.h"
#include "sql_view.h" // check_duplicate_names
#include "sql_acl.h" // SELECT_ACL
+#include "sql_cte.h"
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
@@ -670,6 +671,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
// st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived->derived_result, 0)))
goto exit;
+ if (derived->with &&
+ (res= derived->with->rename_columns_of_derived_unit(thd, unit)))
+ goto exit;
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
if ((res= check_duplicate_names(thd, unit->types, 0)))
goto exit;
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index f3001d5942a..ae54f8bd455 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -843,6 +843,12 @@ void Explain_select::print_explain_json(Explain_query *query,
writer->add_member("const_condition");
write_item(writer, exec_const_cond);
}
+ if (outer_ref_cond)
+ {
+ writer->add_member("outer_ref_condition");
+ write_item(writer, outer_ref_cond);
+ }
+
/* we do not print HAVING which always evaluates to TRUE */
if (having || (having_value == Item::COND_FALSE))
{
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 3f57f7ac937..9f8a0361cd5 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -225,9 +225,10 @@ public:
/* Expensive constant condition */
Item *exec_const_cond;
+ Item *outer_ref_cond;
/* HAVING condition */
- COND *having;
+ Item *having;
Item::cond_result having_value;
/* Global join attributes. In tabular form, they are printed on the first row */
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index a0e836da203..a50b90fc111 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -194,7 +194,8 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
DBUG_ENTER("search_topics");
/* Should never happen. As this is part of help, we can ignore this */
- if (init_read_record(&read_record_info, thd, topics, select, 1, 0, FALSE))
+ if (init_read_record(&read_record_info, thd, topics, select, NULL, 1, 0,
+ FALSE))
DBUG_RETURN(0);
while (!read_record_info.read_record(&read_record_info))
@@ -229,14 +230,16 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
2 found more then one topic matching the mask
*/
-int search_keyword(THD *thd, TABLE *keywords, struct st_find_field *find_fields,
+int search_keyword(THD *thd, TABLE *keywords,
+ struct st_find_field *find_fields,
SQL_SELECT *select, int *key_id)
{
int count= 0;
READ_RECORD read_record_info;
DBUG_ENTER("search_keyword");
/* Should never happen. As this is part of help, we can ignore this */
- if (init_read_record(&read_record_info, thd, keywords, select, 1, 0, FALSE))
+ if (init_read_record(&read_record_info, thd, keywords, select, NULL, 1, 0,
+ FALSE))
DBUG_RETURN(0);
while (!read_record_info.read_record(&read_record_info) && count<2)
@@ -370,7 +373,8 @@ int search_categories(THD *thd, TABLE *categories,
DBUG_ENTER("search_categories");
/* Should never happen. As this is part of help, we can ignore this */
- if (init_read_record(&read_record_info, thd, categories, select,1,0,FALSE))
+ if (init_read_record(&read_record_info, thd, categories, select, NULL,
+ 1, 0, FALSE))
DBUG_RETURN(0);
while (!read_record_info.read_record(&read_record_info))
{
@@ -406,7 +410,8 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
DBUG_ENTER("get_all_items_for_category");
/* Should never happen. As this is part of help, we can ignore this */
- if (init_read_record(&read_record_info, thd, items, select,1,0,FALSE))
+ if (init_read_record(&read_record_info, thd, items, select, NULL, 1, 0,
+ FALSE))
DBUG_VOID_RETURN;
while (!read_record_info.read_record(&read_record_info))
@@ -608,7 +613,7 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
/* Assume that no indexes cover all required fields */
table->covering_keys.clear_all();
- SQL_SELECT *res= make_select(table, 0, 0, cond, 0, error);
+ SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error);
if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)) ||
(res && res->quick && res->quick->reset()))
{
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 15a04cbb170..6e40ac02274 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2071,6 +2071,7 @@ public:
delayed_lock= global_system_variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY : TL_WRITE;
mysql_mutex_unlock(&LOCK_thread_count);
+ thread_safe_increment32(&thread_count);
DBUG_VOID_RETURN;
}
~Delayed_insert()
@@ -2084,17 +2085,24 @@ public:
close_thread_tables(&thd);
thd.mdl_context.release_transactional_locks();
}
- mysql_mutex_lock(&LOCK_thread_count);
mysql_mutex_destroy(&mutex);
mysql_cond_destroy(&cond);
mysql_cond_destroy(&cond_client);
+
+ /*
+ We could use unlink_not_visible_threads() here, but as
+ delayed_insert_threads also needs to be protected by
+ the LOCK_thread_count mutex, we open code this.
+ */
+ mysql_mutex_lock(&LOCK_thread_count);
thd.unlink(); // Must be unlinked under lock
- my_free(thd.query());
- thd.security_ctx->user= thd.security_ctx->host=0;
delayed_insert_threads--;
mysql_mutex_unlock(&LOCK_thread_count);
- thread_safe_decrement32(&thread_count);
- mysql_cond_broadcast(&COND_thread_count); /* Tell main we are ready */
+
+ my_free(thd.query());
+ thd.security_ctx->user= 0;
+ thd.security_ctx->host= 0;
+ dec_thread_count();
}
/* The following is for checking when we can delete ourselves */
@@ -2229,8 +2237,6 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
if (!(di= new Delayed_insert(thd->lex->current_select)))
goto end_create;
- thread_safe_increment32(&thread_count);
-
/*
Annotating delayed inserts is not supported.
*/
@@ -2806,15 +2812,13 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_detach_this_thread();
/* Add thread to THD list so that's it's visible in 'show processlist' */
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
thd->set_current_time();
- threads.append(thd);
+ add_to_active_threads(thd);
if (abort_loop)
thd->killed= KILL_CONNECTION;
else
thd->reset_killed();
- mysql_mutex_unlock(&LOCK_thread_count);
mysql_thread_set_psi_id(thd->thread_id);
@@ -3896,7 +3900,7 @@ Field *Item::create_field_for_create_select(THD *thd, TABLE *table)
{
Field *def_field, *tmp_field;
return ::create_tmp_field(thd, table, this, type(),
- (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0, 0);
+ (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0);
}
@@ -4256,6 +4260,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_RETURN(1);
table->mark_columns_needed_for_insert();
table->file->extra(HA_EXTRA_WRITE_CACHE);
+ // Mark table as used
+ table->query_id= thd->query_id;
DBUG_RETURN(0);
}
@@ -4342,8 +4348,9 @@ bool select_create::send_eof()
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
if (thd->wsrep_conflict_state != NO_CONFLICT)
{
- WSREP_DEBUG("select_create commit failed, thd: %lu err: %d %s",
- thd->thread_id, thd->wsrep_conflict_state, thd->query());
+ WSREP_DEBUG("select_create commit failed, thd: %lld err: %d %s",
+ (longlong) thd->thread_id, thd->wsrep_conflict_state,
+ thd->query());
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
abort_result_set();
DBUG_RETURN(true);
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index f84440afa90..818598110ca 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -2206,7 +2206,7 @@ finish:
for a match for any record from join_tab. To iterate over the candidates
for a match the virtual function get_next_candidate_for_match is used,
while the virtual function prepare_look_for_matches is called to prepare
- for such iteration proccess.
+ for such iteration process.
NOTES
The function produces all matching extensions for the records in the
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5bfa062a628..6056b03f4eb 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_select.h"
+#include "sql_cte.h"
static int lex_one_token(YYSTYPE *yylval, THD *thd);
@@ -324,9 +325,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
DBUG_ASSERT(begin_ptr);
DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length);
- uint body_utf8_length=
- (m_buf_length / thd->variables.character_set_client->mbminlen) *
- my_charset_utf8_bin.mbmaxlen;
+ uint body_utf8_length= get_body_utf8_maximum_length(thd);
m_body_utf8= (char *) thd->alloc(body_utf8_length + 1);
m_body_utf8_ptr= m_body_utf8;
@@ -335,6 +334,22 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
m_cpp_utf8_processed_ptr= begin_ptr;
}
+
+uint Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
+{
+ /*
+ String literals can grow during escaping:
+ 1a. Character string '<TAB>' can grow to '\t', 3 bytes to 4 bytes growth.
+ 1b. Character string '1000 times <TAB>' grows from
+ 1002 to 2002 bytes (including quotes), which gives a little bit
+ less than 2 times growth.
+ "2" should be a reasonable multiplier that safely covers escaping needs.
+ */
+ return (m_buf_length / thd->variables.character_set_client->mbminlen) *
+ my_charset_utf8_bin.mbmaxlen * 2/*for escaping*/;
+}
+
+
/**
@brief The operation appends unprocessed part of pre-processed buffer till
the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to end_ptr.
@@ -402,15 +417,15 @@ void Lex_input_stream::body_utf8_append(const char *ptr)
operation.
*/
-void Lex_input_stream::body_utf8_append_literal(THD *thd,
- const LEX_STRING *txt,
- CHARSET_INFO *txt_cs,
- const char *end_ptr)
+void Lex_input_stream::body_utf8_append_ident(THD *thd,
+ const LEX_STRING *txt,
+ const char *end_ptr)
{
if (!m_cpp_utf8_processed_ptr)
return;
LEX_STRING utf_txt;
+ CHARSET_INFO *txt_cs= thd->charset();
if (!my_charset_same(txt_cs, &my_charset_utf8_general_ci))
{
@@ -434,6 +449,189 @@ void Lex_input_stream::body_utf8_append_literal(THD *thd,
m_cpp_utf8_processed_ptr= end_ptr;
}
+
+
+
+extern "C" {
+
+/**
+ Escape a character. Consequently puts "escape" and "wc" characters into
+ the destination utf8 string.
+ @param cs - the character set (utf8)
+ @param escape - the escape character (backslash, single quote, double quote)
+ @param wc - the character to be escaped
+ @param str - the destination string
+ @param end - the end of the destination string
+ @returns - a code according to the wc_mb() convension.
+*/
+int my_wc_mb_utf8_with_escape(CHARSET_INFO *cs, my_wc_t escape, my_wc_t wc,
+ uchar *str, uchar *end)
+{
+ DBUG_ASSERT(escape > 0);
+ if (str + 1 >= end)
+ return MY_CS_TOOSMALL2; // Not enough space, need at least two bytes.
+ *str= escape;
+ int cnvres= my_charset_utf8_handler.wc_mb(cs, wc, str + 1, end);
+ if (cnvres > 0)
+ return cnvres + 1; // The character was normally put
+ if (cnvres == MY_CS_ILUNI)
+ return MY_CS_ILUNI; // Could not encode "wc" (e.g. non-BMP character)
+ DBUG_ASSERT(cnvres <= MY_CS_TOOSMALL);
+ return cnvres - 1; // Not enough space
+}
+
+
+/**
+ Optionally escape a character.
+ If "escape" is non-zero, then both "escape" and "wc" are put to
+ the destination string. Otherwise, only "wc" is put.
+ @param cs - the character set (utf8)
+ @param wc - the character to be optionally escaped
+ @param escape - the escape character, or 0
+ @param ewc - the escaped replacement of "wc" (e.g. 't' for '\t')
+ @param str - the destination string
+ @param end - the end of the destination string
+ @returns - a code according to the wc_mb() conversion.
+*/
+int my_wc_mb_utf8_opt_escape(CHARSET_INFO *cs,
+ my_wc_t wc, my_wc_t escape, my_wc_t ewc,
+ uchar *str, uchar *end)
+{
+ return escape ? my_wc_mb_utf8_with_escape(cs, escape, ewc, str, end) :
+ my_charset_utf8_handler.wc_mb(cs, wc, str, end);
+}
+
+/**
+ Encode a character with optional backlash escaping and quote escaping.
+ Quote marks are escaped using another quote mark.
+ Additionally, if "escape" is non-zero, then special characters are
+ also escaped using "escape".
+ Otherwise (if "escape" is zero, e.g. in case of MODE_NO_BACKSLASH_ESCAPES),
+ then special characters are not escaped and handled as normal characters.
+
+ @param cs - the character set (utf8)
+ @param wc - the character to be encoded
+ @param str - the destination string
+ @param end - the end of the destination string
+ @param sep - the string delimiter (e.g. ' or ")
+ @param escape - the escape character (backslash, or 0)
+ @returns - a code according to the wc_mb() convension.
+*/
+int my_wc_mb_utf8_escape(CHARSET_INFO *cs, my_wc_t wc, uchar *str, uchar *end,
+ my_wc_t sep, my_wc_t escape)
+{
+ DBUG_ASSERT(escape == 0 || escape == '\\');
+ DBUG_ASSERT(sep == '"' || sep == '\'');
+ switch (wc) {
+ case 0: return my_wc_mb_utf8_opt_escape(cs, wc, escape, '0', str, end);
+ case '\t': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 't', str, end);
+ case '\r': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'r', str, end);
+ case '\n': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'n', str, end);
+ case '\032': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'Z', str, end);
+ case '\'':
+ case '\"':
+ if (wc == sep)
+ return my_wc_mb_utf8_with_escape(cs, wc, wc, str, end);
+ }
+ return my_charset_utf8_handler.wc_mb(cs, wc, str, end); // No escaping needed
+}
+
+
+/** wc_mb() compatible routines for all sql_mode and delimiter combinations */
+int my_wc_mb_utf8_escape_single_quote_and_backslash(CHARSET_INFO *cs,
+ my_wc_t wc,
+ uchar *str, uchar *end)
+{
+ return my_wc_mb_utf8_escape(cs, wc, str, end, '\'', '\\');
+}
+
+
+int my_wc_mb_utf8_escape_double_quote_and_backslash(CHARSET_INFO *cs,
+ my_wc_t wc,
+ uchar *str, uchar *end)
+{
+ return my_wc_mb_utf8_escape(cs, wc, str, end, '"', '\\');
+}
+
+
+int my_wc_mb_utf8_escape_single_quote(CHARSET_INFO *cs, my_wc_t wc,
+ uchar *str, uchar *end)
+{
+ return my_wc_mb_utf8_escape(cs, wc, str, end, '\'', 0);
+}
+
+
+int my_wc_mb_utf8_escape_double_quote(CHARSET_INFO *cs, my_wc_t wc,
+ uchar *str, uchar *end)
+{
+ return my_wc_mb_utf8_escape(cs, wc, str, end, '"', 0);
+}
+
+}; // End of extern "C"
+
+
+/**
+ Get an escaping function, depending on the current sql_mode and the
+ string separator.
+*/
+my_charset_conv_wc_mb
+Lex_input_stream::get_escape_func(THD *thd, my_wc_t sep) const
+{
+ return thd->backslash_escapes() ?
+ (sep == '"' ? my_wc_mb_utf8_escape_double_quote_and_backslash:
+ my_wc_mb_utf8_escape_single_quote_and_backslash) :
+ (sep == '"' ? my_wc_mb_utf8_escape_double_quote:
+ my_wc_mb_utf8_escape_single_quote);
+}
+
+
+/**
+ Append a text literal to the end of m_body_utf8.
+ The string is escaped according to the current sql_mode and the
+ string delimiter (e.g. ' or ").
+
+ @param thd - current THD
+ @param txt - the string to be appended to m_body_utf8.
+ Note, the string must be already unescaped.
+ @param cs - the character set of the string
+ @param end_ptr - m_cpp_utf8_processed_ptr will be set to this value
+ (see body_utf8_append_ident for details)
+ @param sep - the string delimiter (single or double quote)
+*/
+void Lex_input_stream::body_utf8_append_escape(THD *thd,
+ const LEX_STRING *txt,
+ CHARSET_INFO *cs,
+ const char *end_ptr,
+ my_wc_t sep)
+{
+ DBUG_ASSERT(sep == '\'' || sep == '"');
+ if (!m_cpp_utf8_processed_ptr)
+ return;
+ uint errors;
+ /**
+ We previously alloced m_body_utf8 to be able to store the query with all
+ strings properly escaped. See get_body_utf8_maximum_length().
+ So here we have guaranteedly enough space to append any string literal
+ with escaping. Passing txt->length*2 as "available space" is always safe.
+ For better safety purposes we could calculate get_body_utf8_maximum_length()
+ every time we append a string, but this would affect performance negatively,
+ so let's check that we don't get beyond the allocated buffer in
+ debug build only.
+ */
+ DBUG_ASSERT(m_body_utf8 + get_body_utf8_maximum_length(thd) >=
+ m_body_utf8_ptr + txt->length * 2);
+ uint32 cnv_length= my_convert_using_func(m_body_utf8_ptr, txt->length * 2,
+ &my_charset_utf8_general_ci,
+ get_escape_func(thd, sep),
+ txt->str, txt->length,
+ cs, cs->cset->mb_wc,
+ &errors);
+ m_body_utf8_ptr+= cnv_length;
+ *m_body_utf8_ptr= 0;
+ m_cpp_utf8_processed_ptr= end_ptr;
+}
+
+
void Lex_input_stream::add_digest_token(uint token, LEX_YYSTYPE yylval)
{
if (m_digest != NULL)
@@ -471,11 +669,15 @@ void lex_start(THD *thd)
/* 'parent_lex' is used in init_query() so it must be before it. */
lex->select_lex.parent_lex= lex;
lex->select_lex.init_query();
+ lex->curr_with_clause= 0;
+ lex->with_clauses_list= 0;
+ lex->with_clauses_list_last_next= &lex->with_clauses_list;
lex->value_list.empty();
lex->update_list.empty();
lex->set_var_list.empty();
lex->param_list.empty();
lex->view_list.empty();
+ lex->with_column_list.empty();
lex->with_persistent_for_clause= FALSE;
lex->column_list= NULL;
lex->index_list= NULL;
@@ -804,14 +1006,14 @@ Lex_input_stream::unescape(CHARSET_INFO *cs, char *to,
Fix sometimes to do only one scan of the string
*/
-bool Lex_input_stream::get_text(LEX_STRING *dst, int pre_skip, int post_skip)
+bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep,
+ int pre_skip, int post_skip)
{
- reg1 uchar c,sep;
+ reg1 uchar c;
uint found_escape=0;
CHARSET_INFO *cs= m_thd->charset();
tok_bitmap= 0;
- sep= yyGetLast(); // String should end with this
while (! eof())
{
c= yyGet();
@@ -1176,6 +1378,8 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
return((int) c);
case MY_LEX_IDENT_OR_NCHAR:
+ {
+ uint sep;
if (lip->yyPeek() != '\'')
{
state= MY_LEX_IDENT;
@@ -1183,14 +1387,20 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
}
/* Found N'string' */
lip->yySkip(); // Skip '
- if (lip->get_text(&yylval->lex_str, 2, 1))
+ if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 2, 1))
{
state= MY_LEX_CHAR; // Read char by char
break;
}
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+ lip->body_utf8_append_escape(thd, &yylval->lex_str,
+ national_charset_info,
+ lip->m_cpp_text_end, sep);
+
lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
return(NCHAR_STRING);
-
+ }
case MY_LEX_IDENT_OR_HEX:
if (lip->yyPeek() == '\'')
{ // Found x'hex-number'
@@ -1209,28 +1419,22 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
if (use_mb(cs))
{
result_state= IDENT_QUOTED;
- if (my_mbcharlen(cs, lip->yyGetLast()) > 1)
+ int char_length= my_charlen(cs, lip->get_ptr() - 1,
+ lip->get_end_of_query());
+ if (char_length <= 0)
{
- int l = my_ismbchar(cs,
- lip->get_ptr() -1,
- lip->get_end_of_query());
- if (l == 0) {
- state = MY_LEX_CHAR;
- continue;
- }
- lip->skip_binary(l - 1);
+ state= MY_LEX_CHAR;
+ continue;
}
+ lip->skip_binary(char_length - 1);
+
while (ident_map[c=lip->yyGet()])
{
- if (my_mbcharlen(cs, c) > 1)
- {
- int l;
- if ((l = my_ismbchar(cs,
- lip->get_ptr() -1,
- lip->get_end_of_query())) == 0)
- break;
- lip->skip_binary(l-1);
- }
+ char_length= my_charlen(cs, lip->get_ptr() - 1,
+ lip->get_end_of_query());
+ if (char_length <= 0)
+ break;
+ lip->skip_binary(char_length - 1);
}
}
else
@@ -1293,8 +1497,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
- lip->m_cpp_text_end);
+ lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
return(result_state); // IDENT or IDENT_QUOTED
@@ -1372,15 +1575,11 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
result_state= IDENT_QUOTED;
while (ident_map[c=lip->yyGet()])
{
- if (my_mbcharlen(cs, c) > 1)
- {
- int l;
- if ((l = my_ismbchar(cs,
- lip->get_ptr() -1,
- lip->get_end_of_query())) == 0)
- break;
- lip->skip_binary(l-1);
- }
+ int char_length= my_charlen(cs, lip->get_ptr() - 1,
+ lip->get_end_of_query());
+ if (char_length <= 0)
+ break;
+ lip->skip_binary(char_length - 1);
}
}
else
@@ -1398,8 +1597,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
- lip->m_cpp_text_end);
+ lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
return(result_state);
@@ -1409,8 +1607,9 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
char quote_char= c; // Used char
while ((c=lip->yyGet()))
{
- int var_length;
- if ((var_length= my_mbcharlen(cs, c)) == 1)
+ int var_length= my_charlen(cs, lip->get_ptr() - 1,
+ lip->get_end_of_query());
+ if (var_length == 1)
{
if (c == quote_char)
{
@@ -1422,11 +1621,9 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
}
}
#ifdef USE_MB
- else if (use_mb(cs))
+ else if (var_length > 1)
{
- if ((var_length= my_ismbchar(cs, lip->get_ptr() - 1,
- lip->get_end_of_query())))
- lip->skip_binary(var_length-1);
+ lip->skip_binary(var_length - 1);
}
#endif
}
@@ -1442,8 +1639,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
- lip->m_cpp_text_end);
+ lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
return(IDENT_QUOTED);
}
@@ -1548,23 +1744,23 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
}
/* " used for strings */
case MY_LEX_STRING: // Incomplete text string
- if (lip->get_text(&yylval->lex_str, 1, 1))
+ {
+ uint sep;
+ if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 1, 1))
{
state= MY_LEX_CHAR; // Read char by char
break;
}
-
+ CHARSET_INFO *strcs= lip->m_underscore_cs ? lip->m_underscore_cs : cs;
lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_literal(thd, &yylval->lex_str,
- lip->m_underscore_cs ? lip->m_underscore_cs : cs,
- lip->m_cpp_text_end);
-
+ lip->body_utf8_append_escape(thd, &yylval->lex_str, strcs,
+ lip->m_cpp_text_end, sep);
lip->m_underscore_cs= NULL;
lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
return(TEXT_STRING);
-
+ }
case MY_LEX_COMMENT: // Comment
lex->select_lex.options|= OPTION_FOUND_COMMENT;
while ((c = lip->yyGet()) != '\n' && c) ;
@@ -1813,8 +2009,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
lip->body_utf8_append(lip->m_cpp_text_start);
- lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
- lip->m_cpp_text_end);
+ lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);
return(result_state);
}
@@ -1883,6 +2078,9 @@ void st_select_lex_unit::init_query()
found_rows_for_union= 0;
insert_table_with_stored_vcol= 0;
derived= 0;
+ with_clause= 0;
+ with_element= 0;
+ columns_are_renamed= false;
}
void st_select_lex::init_query()
@@ -2068,6 +2266,37 @@ void st_select_lex_node::fast_exclude()
}
+/**
+ @brief
+ Insert a new chain of nodes into another chain before a particular link
+
+ @param in/out
+ ptr_pos_to_insert the address of the chain pointer pointing to the link
+ before which the subchain has to be inserted
+ @param
+ end_chain_node the last link of the subchain to be inserted
+
+ @details
+ The method inserts the chain of nodes starting from this node and ending
+ with the node nd_chain_node into another chain of nodes before the node
+ pointed to by *ptr_pos_to_insert.
+ It is assumed that ptr_pos_to_insert belongs to the chain where we insert.
+ So it must be updated.
+
+ @retval
+ The method returns the pointer to the first link of the inserted chain
+*/
+
+st_select_lex_node *st_select_lex_node:: insert_chain_before(
+ st_select_lex_node **ptr_pos_to_insert,
+ st_select_lex_node *end_chain_node)
+{
+ end_chain_node->link_next= *ptr_pos_to_insert;
+ (*ptr_pos_to_insert)->link_prev= &end_chain_node->link_next;
+ this->link_prev= ptr_pos_to_insert;
+ return this;
+}
+
/*
Exclude a node from the tree lex structure, but leave it in the global
list of nodes.
@@ -2464,6 +2693,8 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
bool union_all= !union_distinct;
+ if (with_clause)
+ with_clause->print(str, query_type);
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
if (sl != first_select())
@@ -2504,30 +2735,22 @@ void st_select_lex::print_order(String *str,
{
if (order->counter_used)
{
- if (query_type != QT_VIEW_INTERNAL)
+ char buffer[20];
+ size_t length= my_snprintf(buffer, 20, "%d", order->counter);
+ str->append(buffer, (uint) length);
+ }
+ else
+ {
+ /* replace numeric reference with equivalent for ORDER constant */
+ if (order->item[0]->type() == Item::INT_ITEM &&
+ order->item[0]->basic_const_item())
{
- char buffer[20];
- size_t length= my_snprintf(buffer, 20, "%d", order->counter);
- str->append(buffer, (uint) length);
+ /* make it expression instead of integer constant */
+ str->append(STRING_WITH_LEN("''"));
}
else
- {
- /* replace numeric reference with expression */
- if (order->item[0]->type() == Item::INT_ITEM &&
- order->item[0]->basic_const_item())
- {
- char buffer[20];
- size_t length= my_snprintf(buffer, 20, "%d", order->counter);
- str->append(buffer, (uint) length);
- /* make it expression instead of integer constant */
- str->append(STRING_WITH_LEN("+0"));
- }
- else
- (*order->item)->print(str, query_type);
- }
+ (*order->item)->print(str, query_type);
}
- else
- (*order->item)->print(str, query_type);
if (order->direction == ORDER::ORDER_DESC)
str->append(STRING_WITH_LEN(" desc"));
if (order->next)
@@ -3932,6 +4155,19 @@ void SELECT_LEX::update_used_tables()
tl->on_expr->update_used_tables();
tl->on_expr->walk(&Item::eval_not_null_tables, 0, NULL);
}
+ /*
+ - There is no need to check sj_on_expr, because merged semi-joins inject
+ sj_on_expr into the parent's WHERE clase.
+ - For non-merged semi-joins (aka JTBMs), we need to check their
+ left_expr. There is no need to check the rest of the subselect, we know
+ it is uncorrelated and so cannot refer to any tables in this select.
+ */
+ if (tl->jtbm_subselect)
+ {
+ Item *left_expr= tl->jtbm_subselect->left_expr;
+ left_expr->walk(&Item::update_table_bitmaps_processor, FALSE, NULL);
+ }
+
embedding= tl->embedding;
while (embedding)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index d4acf98c5d4..fcfa96023a4 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,6 +50,8 @@ class File_parser;
class Key_part_spec;
class Item_window_func;
struct sql_digest_state;
+class With_clause;
+
#define ALLOC_ROOT_SET 1024
@@ -180,6 +182,7 @@ const LEX_STRING sp_data_access_name[]=
#define DERIVED_SUBQUERY 1
#define DERIVED_VIEW 2
+#define DERIVED_WITH 4
enum enum_view_create_mode
{
@@ -542,7 +545,9 @@ public:
List<String> *partition_names= 0,
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
-
+ void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
+ st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert,
+ st_select_lex_node *end_chain_node);
friend class st_select_lex_unit;
friend bool mysql_new_select(LEX *lex, bool move_down);
friend bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
@@ -629,7 +634,7 @@ public:
return saved_fake_select_lex;
return first_select();
};
- //node on wich we should return current_select pointer after parsing subquery
+ //node on which we should return current_select pointer after parsing subquery
st_select_lex *return_to;
/* LIMIT clause runtime counters */
ha_rows select_limit_cnt, offset_limit_cnt;
@@ -640,6 +645,10 @@ public:
derived tables/views handling.
*/
TABLE_LIST *derived;
+ /* With clause attached to this unit (if any) */
+ With_clause *with_clause;
+ /* With element where this unit is used as the specification (if any) */
+ With_element *with_element;
/* thread handler */
THD *thd;
/*
@@ -648,7 +657,7 @@ public:
*/
st_select_lex *fake_select_lex;
/**
- SELECT_LEX that stores LIMIT and OFFSET for UNION ALL when no
+ SELECT_LEX that stores LIMIT and OFFSET for UNION ALL when noq
fake_select_lex is used.
*/
st_select_lex *saved_fake_select_lex;
@@ -664,12 +673,15 @@ public:
*/
TABLE *insert_table_with_stored_vcol;
+ bool columns_are_renamed;
+
void init_query();
st_select_lex* outer_select();
st_select_lex* first_select()
{
return reinterpret_cast<st_select_lex*>(slave);
}
+ void set_with_clause(With_clause *with_cl) { with_clause= with_cl; }
st_select_lex_unit* next_unit()
{
return reinterpret_cast<st_select_lex_unit*>(next);
@@ -1071,6 +1083,19 @@ public:
void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; }
void set_agg_func_used(bool val) { m_agg_func_used= val; }
+ void set_with_clause(With_clause *with_clause)
+ {
+ master_unit()->with_clause= with_clause;
+ }
+ With_clause *get_with_clause()
+ {
+ return master_unit()->with_clause;
+ }
+ With_element *get_with_element()
+ {
+ return master_unit()->with_element;
+ }
+ With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
List<Window_spec> window_specs;
void prepare_add_window_spec(THD *thd);
@@ -1097,6 +1122,13 @@ private:
index_clause_map current_index_hint_clause;
/* a list of USE/FORCE/IGNORE INDEX */
List<Index_hint> *index_hints;
+
+public:
+ inline void add_where_field(st_select_lex *sel)
+ {
+ DBUG_ASSERT(this != sel);
+ select_n_where_fields+= sel->select_n_where_fields;
+ }
};
typedef class st_select_lex SELECT_LEX;
@@ -1432,6 +1464,11 @@ public:
return get_stmt_unsafe_flags() != 0;
}
+ inline bool is_stmt_unsafe(enum_binlog_stmt_unsafe unsafe)
+ {
+ return binlog_stmt_flags & (1 << unsafe);
+ }
+
/**
Flag the current (top-level) statement as unsafe.
The flag will be reset after the statement has finished.
@@ -1828,6 +1865,7 @@ class Lex_input_stream
{
size_t unescape(CHARSET_INFO *cs, char *to,
const char *str, const char *end, int sep);
+ my_charset_conv_wc_mb get_escape_func(THD *thd, my_wc_t sep) const;
public:
Lex_input_stream()
{
@@ -2098,14 +2136,23 @@ public:
return (uint) (m_body_utf8_ptr - m_body_utf8);
}
+ /**
+ Get the maximum length of the utf8-body buffer.
+ The utf8 body can grow because of the character set conversion and escaping.
+ */
+ uint get_body_utf8_maximum_length(THD *thd);
+
void body_utf8_start(THD *thd, const char *begin_ptr);
void body_utf8_append(const char *ptr);
void body_utf8_append(const char *ptr, const char *end_ptr);
- void body_utf8_append_literal(THD *thd,
- const LEX_STRING *txt,
- CHARSET_INFO *txt_cs,
- const char *end_ptr);
-
+ void body_utf8_append_ident(THD *thd,
+ const LEX_STRING *txt,
+ const char *end_ptr);
+ void body_utf8_append_escape(THD *thd,
+ const LEX_STRING *txt,
+ CHARSET_INFO *txt_cs,
+ const char *end_ptr,
+ my_wc_t sep);
/** Current thread. */
THD *m_thd;
@@ -2126,7 +2173,7 @@ public:
/** LALR(2) resolution, value of the look ahead token.*/
LEX_YYSTYPE lookahead_yylval;
- bool get_text(LEX_STRING *to, int pre_skip, int post_skip);
+ bool get_text(LEX_STRING *to, uint sep, int pre_skip, int post_skip);
void add_digest_token(uint token, LEX_YYSTYPE yylval);
@@ -2412,7 +2459,16 @@ struct LEX: public Query_tables_list
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
-
+ /* current with clause in parsing if any, otherwise 0*/
+ With_clause *curr_with_clause;
+ /* pointer to the first with clause in the current statemant */
+ With_clause *with_clauses_list;
+ /*
+ (*with_clauses_list_last_next) contains a pointer to the last
+ with clause in the current statement
+ */
+ With_clause **with_clauses_list_last_next;
+
/* Query Plan Footprint of a currently running select */
Explain_query *explain;
@@ -2478,6 +2534,7 @@ public:
List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
List<LEX_STRING> view_list; // view list (list of field names in view)
+ List<LEX_STRING> with_column_list; // list of column names in with_list_element
List<LEX_STRING> *column_list; // list of column names (in ANALYZE)
List<LEX_STRING> *index_list; // list of index names (in ANALYZE)
/*
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 718306c2193..113af35bad7 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -650,6 +650,10 @@ struct ilink
if (next) next->prev=prev;
prev=0 ; next=0;
}
+ inline void assert_if_linked()
+ {
+ DBUG_ASSERT(prev != 0 && next != 0);
+ }
virtual ~ilink() { unlink(); } /*lint -e1740 */
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8f73087979f..79d284a5952 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -92,6 +92,7 @@
#include "transaction.h"
#include "sql_audit.h"
#include "sql_prepare.h"
+#include "sql_cte.h"
#include "debug_sync.h"
#include "probes_mysql.h"
#include "set_var.h"
@@ -110,7 +111,7 @@
#include "wsrep_thd.h"
static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state);
+ Parser_state *parser_state, bool is_next_command);
/**
@defgroup Runtime_Environment Runtime Environment
@@ -118,8 +119,6 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
*/
/* Used in error handling only */
-#define SP_TYPE_STRING(LP) \
- ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
#define SP_COM_STRING(LP) \
((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
(LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
@@ -136,38 +135,263 @@ static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
-const LEX_STRING command_name[]={
- { C_STRING_WITH_LEN("Sleep") },
- { C_STRING_WITH_LEN("Quit") },
- { C_STRING_WITH_LEN("Init DB") },
- { C_STRING_WITH_LEN("Query") },
- { C_STRING_WITH_LEN("Field List") },
- { C_STRING_WITH_LEN("Create DB") },
- { C_STRING_WITH_LEN("Drop DB") },
- { C_STRING_WITH_LEN("Refresh") },
- { C_STRING_WITH_LEN("Shutdown") },
- { C_STRING_WITH_LEN("Statistics") },
- { C_STRING_WITH_LEN("Processlist") },
- { C_STRING_WITH_LEN("Connect") },
- { C_STRING_WITH_LEN("Kill") },
- { C_STRING_WITH_LEN("Debug") },
- { C_STRING_WITH_LEN("Ping") },
- { C_STRING_WITH_LEN("Time") },
- { C_STRING_WITH_LEN("Delayed insert") },
- { C_STRING_WITH_LEN("Change user") },
- { C_STRING_WITH_LEN("Binlog Dump") },
- { C_STRING_WITH_LEN("Table Dump") },
- { C_STRING_WITH_LEN("Connect Out") },
- { C_STRING_WITH_LEN("Register Slave") },
- { C_STRING_WITH_LEN("Prepare") },
- { C_STRING_WITH_LEN("Execute") },
- { C_STRING_WITH_LEN("Long Data") },
- { C_STRING_WITH_LEN("Close stmt") },
- { C_STRING_WITH_LEN("Reset stmt") },
- { C_STRING_WITH_LEN("Set option") },
- { C_STRING_WITH_LEN("Fetch") },
- { C_STRING_WITH_LEN("Daemon") },
- { C_STRING_WITH_LEN("Error") } // Last command number
+const LEX_STRING command_name[257]={
+ { C_STRING_WITH_LEN("Sleep") }, //0
+ { C_STRING_WITH_LEN("Quit") }, //1
+ { C_STRING_WITH_LEN("Init DB") }, //2
+ { C_STRING_WITH_LEN("Query") }, //3
+ { C_STRING_WITH_LEN("Field List") }, //4
+ { C_STRING_WITH_LEN("Create DB") }, //5
+ { C_STRING_WITH_LEN("Drop DB") }, //6
+ { C_STRING_WITH_LEN("Refresh") }, //7
+ { C_STRING_WITH_LEN("Shutdown") }, //8
+ { C_STRING_WITH_LEN("Statistics") }, //9
+ { C_STRING_WITH_LEN("Processlist") }, //10
+ { C_STRING_WITH_LEN("Connect") }, //11
+ { C_STRING_WITH_LEN("Kill") }, //12
+ { C_STRING_WITH_LEN("Debug") }, //13
+ { C_STRING_WITH_LEN("Ping") }, //14
+ { C_STRING_WITH_LEN("Time") }, //15
+ { C_STRING_WITH_LEN("Delayed insert") }, //16
+ { C_STRING_WITH_LEN("Change user") }, //17
+ { C_STRING_WITH_LEN("Binlog Dump") }, //18
+ { C_STRING_WITH_LEN("Table Dump") }, //19
+ { C_STRING_WITH_LEN("Connect Out") }, //20
+ { C_STRING_WITH_LEN("Register Slave") }, //21
+ { C_STRING_WITH_LEN("Prepare") }, //22
+ { C_STRING_WITH_LEN("Execute") }, //23
+ { C_STRING_WITH_LEN("Long Data") }, //24
+ { C_STRING_WITH_LEN("Close stmt") }, //25
+ { C_STRING_WITH_LEN("Reset stmt") }, //26
+ { C_STRING_WITH_LEN("Set option") }, //27
+ { C_STRING_WITH_LEN("Fetch") }, //28
+ { C_STRING_WITH_LEN("Daemon") }, //29
+ { 0, 0 }, //30
+ { 0, 0 }, //31
+ { 0, 0 }, //32
+ { 0, 0 }, //33
+ { 0, 0 }, //34
+ { 0, 0 }, //35
+ { 0, 0 }, //36
+ { 0, 0 }, //37
+ { 0, 0 }, //38
+ { 0, 0 }, //39
+ { 0, 0 }, //40
+ { 0, 0 }, //41
+ { 0, 0 }, //42
+ { 0, 0 }, //43
+ { 0, 0 }, //44
+ { 0, 0 }, //45
+ { 0, 0 }, //46
+ { 0, 0 }, //47
+ { 0, 0 }, //48
+ { 0, 0 }, //49
+ { 0, 0 }, //50
+ { 0, 0 }, //51
+ { 0, 0 }, //52
+ { 0, 0 }, //53
+ { 0, 0 }, //54
+ { 0, 0 }, //55
+ { 0, 0 }, //56
+ { 0, 0 }, //57
+ { 0, 0 }, //58
+ { 0, 0 }, //59
+ { 0, 0 }, //60
+ { 0, 0 }, //61
+ { 0, 0 }, //62
+ { 0, 0 }, //63
+ { 0, 0 }, //64
+ { 0, 0 }, //65
+ { 0, 0 }, //66
+ { 0, 0 }, //67
+ { 0, 0 }, //68
+ { 0, 0 }, //69
+ { 0, 0 }, //70
+ { 0, 0 }, //71
+ { 0, 0 }, //72
+ { 0, 0 }, //73
+ { 0, 0 }, //74
+ { 0, 0 }, //75
+ { 0, 0 }, //76
+ { 0, 0 }, //77
+ { 0, 0 }, //78
+ { 0, 0 }, //79
+ { 0, 0 }, //80
+ { 0, 0 }, //81
+ { 0, 0 }, //82
+ { 0, 0 }, //83
+ { 0, 0 }, //84
+ { 0, 0 }, //85
+ { 0, 0 }, //86
+ { 0, 0 }, //87
+ { 0, 0 }, //88
+ { 0, 0 }, //89
+ { 0, 0 }, //90
+ { 0, 0 }, //91
+ { 0, 0 }, //92
+ { 0, 0 }, //93
+ { 0, 0 }, //94
+ { 0, 0 }, //95
+ { 0, 0 }, //96
+ { 0, 0 }, //97
+ { 0, 0 }, //98
+ { 0, 0 }, //99
+ { 0, 0 }, //100
+ { 0, 0 }, //101
+ { 0, 0 }, //102
+ { 0, 0 }, //103
+ { 0, 0 }, //104
+ { 0, 0 }, //105
+ { 0, 0 }, //106
+ { 0, 0 }, //107
+ { 0, 0 }, //108
+ { 0, 0 }, //109
+ { 0, 0 }, //110
+ { 0, 0 }, //111
+ { 0, 0 }, //112
+ { 0, 0 }, //113
+ { 0, 0 }, //114
+ { 0, 0 }, //115
+ { 0, 0 }, //116
+ { 0, 0 }, //117
+ { 0, 0 }, //118
+ { 0, 0 }, //119
+ { 0, 0 }, //120
+ { 0, 0 }, //121
+ { 0, 0 }, //122
+ { 0, 0 }, //123
+ { 0, 0 }, //124
+ { 0, 0 }, //125
+ { 0, 0 }, //126
+ { 0, 0 }, //127
+ { 0, 0 }, //128
+ { 0, 0 }, //129
+ { 0, 0 }, //130
+ { 0, 0 }, //131
+ { 0, 0 }, //132
+ { 0, 0 }, //133
+ { 0, 0 }, //134
+ { 0, 0 }, //135
+ { 0, 0 }, //136
+ { 0, 0 }, //137
+ { 0, 0 }, //138
+ { 0, 0 }, //139
+ { 0, 0 }, //140
+ { 0, 0 }, //141
+ { 0, 0 }, //142
+ { 0, 0 }, //143
+ { 0, 0 }, //144
+ { 0, 0 }, //145
+ { 0, 0 }, //146
+ { 0, 0 }, //147
+ { 0, 0 }, //148
+ { 0, 0 }, //149
+ { 0, 0 }, //150
+ { 0, 0 }, //151
+ { 0, 0 }, //152
+ { 0, 0 }, //153
+ { 0, 0 }, //154
+ { 0, 0 }, //155
+ { 0, 0 }, //156
+ { 0, 0 }, //157
+ { 0, 0 }, //158
+ { 0, 0 }, //159
+ { 0, 0 }, //160
+ { 0, 0 }, //161
+ { 0, 0 }, //162
+ { 0, 0 }, //163
+ { 0, 0 }, //164
+ { 0, 0 }, //165
+ { 0, 0 }, //166
+ { 0, 0 }, //167
+ { 0, 0 }, //168
+ { 0, 0 }, //169
+ { 0, 0 }, //170
+ { 0, 0 }, //171
+ { 0, 0 }, //172
+ { 0, 0 }, //173
+ { 0, 0 }, //174
+ { 0, 0 }, //175
+ { 0, 0 }, //176
+ { 0, 0 }, //177
+ { 0, 0 }, //178
+ { 0, 0 }, //179
+ { 0, 0 }, //180
+ { 0, 0 }, //181
+ { 0, 0 }, //182
+ { 0, 0 }, //183
+ { 0, 0 }, //184
+ { 0, 0 }, //185
+ { 0, 0 }, //186
+ { 0, 0 }, //187
+ { 0, 0 }, //188
+ { 0, 0 }, //189
+ { 0, 0 }, //190
+ { 0, 0 }, //191
+ { 0, 0 }, //192
+ { 0, 0 }, //193
+ { 0, 0 }, //194
+ { 0, 0 }, //195
+ { 0, 0 }, //196
+ { 0, 0 }, //197
+ { 0, 0 }, //198
+ { 0, 0 }, //199
+ { 0, 0 }, //200
+ { 0, 0 }, //201
+ { 0, 0 }, //202
+ { 0, 0 }, //203
+ { 0, 0 }, //204
+ { 0, 0 }, //205
+ { 0, 0 }, //206
+ { 0, 0 }, //207
+ { 0, 0 }, //208
+ { 0, 0 }, //209
+ { 0, 0 }, //210
+ { 0, 0 }, //211
+ { 0, 0 }, //212
+ { 0, 0 }, //213
+ { 0, 0 }, //214
+ { 0, 0 }, //215
+ { 0, 0 }, //216
+ { 0, 0 }, //217
+ { 0, 0 }, //218
+ { 0, 0 }, //219
+ { 0, 0 }, //220
+ { 0, 0 }, //221
+ { 0, 0 }, //222
+ { 0, 0 }, //223
+ { 0, 0 }, //224
+ { 0, 0 }, //225
+ { 0, 0 }, //226
+ { 0, 0 }, //227
+ { 0, 0 }, //228
+ { 0, 0 }, //229
+ { 0, 0 }, //230
+ { 0, 0 }, //231
+ { 0, 0 }, //232
+ { 0, 0 }, //233
+ { 0, 0 }, //234
+ { 0, 0 }, //235
+ { 0, 0 }, //236
+ { 0, 0 }, //237
+ { 0, 0 }, //238
+ { 0, 0 }, //239
+ { 0, 0 }, //240
+ { 0, 0 }, //241
+ { 0, 0 }, //242
+ { 0, 0 }, //243
+ { 0, 0 }, //244
+ { 0, 0 }, //245
+ { 0, 0 }, //246
+ { 0, 0 }, //247
+ { 0, 0 }, //248
+ { 0, 0 }, //249
+ { 0, 0 }, //250
+ { 0, 0 }, //251
+ { 0, 0 }, //252
+ { 0, 0 }, //253
+ { C_STRING_WITH_LEN("Com_multi") }, //254
+ { C_STRING_WITH_LEN("Error") } // Last command number 255
};
const char *xa_state_names[]={
@@ -269,7 +493,7 @@ void init_update_queries(void)
memset(server_command_flags, 0, sizeof(server_command_flags));
server_command_flags[COM_STATISTICS]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK;
- server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK;
+ server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK | CF_NO_COM_MULTI;
server_command_flags[COM_QUIT]= CF_SKIP_WSREP_CHECK;
server_command_flags[COM_PROCESS_INFO]= CF_SKIP_WSREP_CHECK;
@@ -278,6 +502,10 @@ void init_update_queries(void)
server_command_flags[COM_SLEEP]= CF_SKIP_WSREP_CHECK;
server_command_flags[COM_TIME]= CF_SKIP_WSREP_CHECK;
server_command_flags[COM_END]= CF_SKIP_WSREP_CHECK;
+ for (uint i= COM_MDB_GAP_BEG; i <= COM_MDB_GAP_END; i++)
+ {
+ server_command_flags[i]= CF_SKIP_WSREP_CHECK;
+ }
/*
COM_QUERY, COM_SET_OPTION and COM_STMT_XXX are allowed to pass the early
@@ -290,6 +518,7 @@ void init_update_queries(void)
server_command_flags[COM_STMT_RESET]= CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK;
server_command_flags[COM_STMT_EXECUTE]= CF_SKIP_WSREP_CHECK;
server_command_flags[COM_STMT_SEND_LONG_DATA]= CF_SKIP_WSREP_CHECK;
+ server_command_flags[COM_MULTI]= CF_SKIP_WSREP_CHECK | CF_NO_COM_MULTI;
/* Initialize the sql command flags array. */
memset(sql_command_flags, 0, sizeof(sql_command_flags));
@@ -417,6 +646,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_USER]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
@@ -438,6 +668,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_ALTER_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA;
@@ -494,6 +725,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CHECKSUM]= CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS;
@@ -587,6 +819,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_ALTER_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_USER]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_RENAME_USER]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
@@ -637,7 +870,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
mysql_rwlock_t *var_lock)
{
Vio* save_vio;
- ulong save_client_capabilities;
+ ulonglong save_client_capabilities;
mysql_rwlock_rdlock(var_lock);
if (!init_command->length)
@@ -669,7 +902,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
*/
save_vio= thd->net.vio;
thd->net.vio= 0;
- dispatch_command(COM_QUERY, thd, buf, len);
+ dispatch_command(COM_QUERY, thd, buf, len, FALSE, FALSE);
thd->client_capabilities= save_client_capabilities;
thd->net.vio= save_vio;
@@ -787,7 +1020,7 @@ static void handle_bootstrap_impl(THD *thd)
break;
}
- mysql_parse(thd, thd->query(), length, &parser_state);
+ mysql_parse(thd, thd->query(), length, &parser_state, FALSE);
bootstrap_error= thd->is_error();
thd->protocol->end_statement();
@@ -844,12 +1077,13 @@ end:
delete thd;
#ifndef EMBEDDED_LIBRARY
- thread_safe_decrement32(&thread_count);
+ DBUG_ASSERT(thread_count == 1);
in_bootstrap= FALSE;
-
- mysql_mutex_lock(&LOCK_thread_count);
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
+ /*
+ dec_thread_count will signal bootstrap() function that we have ended as
+ thread_count will become 0.
+ */
+ dec_thread_count();
my_thread_end();
pthread_exit(0);
#endif
@@ -884,6 +1118,23 @@ void cleanup_items(Item *item)
DBUG_VOID_RETURN;
}
+static enum enum_server_command fetch_command(THD *thd, char *packet)
+{
+ enum enum_server_command
+ command= (enum enum_server_command) (uchar) packet[0];
+ NET *net= &thd->net;
+ DBUG_ENTER("fetch_command");
+
+ if (command >= COM_END ||
+ (command >= COM_MDB_GAP_BEG && command <= COM_MDB_GAP_END))
+ command= COM_END; // Wrong command
+
+ DBUG_PRINT("info",("Command on %s = %d (%s)",
+ vio_description(net->vio), command,
+ command_name[command].str));
+ DBUG_RETURN(command);
+}
+
#ifndef EMBEDDED_LIBRARY
@@ -954,7 +1205,6 @@ bool do_command(THD *thd)
if(!thd->skip_wait_timeout)
my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
-
/*
XXX: this code is here only to clear possible errors of init_connect.
Consider moving to init_connect() instead.
@@ -1073,14 +1323,8 @@ bool do_command(THD *thd)
/* Do not rely on my_net_read, extra safety against programming errors. */
packet[packet_length]= '\0'; /* safety */
- command= (enum enum_server_command) (uchar) packet[0];
- if (command >= COM_END)
- command= COM_END; // Wrong command
-
- DBUG_PRINT("info",("Command on %s = %d (%s)",
- vio_description(net->vio), command,
- command_name[command].str));
+ command= fetch_command(thd, packet);
#ifdef WITH_WSREP
/*
@@ -1106,7 +1350,8 @@ bool do_command(THD *thd)
DBUG_ASSERT(packet_length);
DBUG_ASSERT(!thd->apc_target.is_enabled());
- return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
+ return_value= dispatch_command(command, thd, packet+1,
+ (uint) (packet_length-1), FALSE, FALSE);
#ifdef WITH_WSREP
if (WSREP(thd))
{
@@ -1124,7 +1369,7 @@ bool do_command(THD *thd)
my_charset_latin1.csname);
}
return_value= dispatch_command(command, thd, thd->wsrep_retry_query,
- thd->wsrep_retry_query_len);
+ thd->wsrep_retry_query_len, FALSE, FALSE);
thd->variables.character_set_client = current_charset;
}
@@ -1216,6 +1461,44 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
/**
+ check COM_MULTI packet
+
+ @param thd thread handle
+ @param packet pointer on the packet of commands
+ @param packet_length length of this packet
+
+ @retval 0 - Error
+ @retval # - Number of commands in the batch
+*/
+
+uint maria_multi_check(THD *thd, char *packet, uint packet_length)
+{
+ uint counter= 0;
+ DBUG_ENTER("maria_multi_check");
+ while (packet_length)
+ {
+ // length of command + 3 bytes where that length was stored
+ uint subpacket_length= (uint3korr(packet) + 3);
+ DBUG_PRINT("info", ("sub-packet length: %d command: %x",
+ subpacket_length, packet[3]));
+
+ if (subpacket_length == 3 ||
+ subpacket_length > packet_length)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
+ MYF(0));
+ DBUG_RETURN(0);
+ }
+
+ counter++;
+ packet+= subpacket_length;
+ packet_length-= subpacket_length;
+ }
+ DBUG_RETURN(counter);
+}
+
+
+/**
Perform one connection-level (COM_XXXX) command.
@param command type of command to perform
@@ -1224,6 +1507,8 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
@param packet_length length of packet + 1 (to show that data is
null-terminated) except for COM_SLEEP, where it
can be zero.
+ @param is_com_multi recursive call from COM_MULTI
+ @param is_next_command there will be more command in the COM_MULTI batch
@todo
set thd->lex->sql_command to SQLCOM_END here.
@@ -1237,15 +1522,24 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
COM_QUIT/COM_SHUTDOWN
*/
bool dispatch_command(enum enum_server_command command, THD *thd,
- char* packet, uint packet_length)
+ char* packet, uint packet_length, bool is_com_multi,
+ bool is_next_command)
{
NET *net= &thd->net;
bool error= 0;
bool do_end_of_statement= true;
DBUG_ENTER("dispatch_command");
- DBUG_PRINT("info", ("command: %d", command));
+ DBUG_PRINT("info", ("command: %d %s", command,
+ (command_name[command].str != 0 ?
+ command_name[command].str :
+ "<?>")));
+ bool drop_more_results= 0;
+
+ if (!is_com_multi)
+ inc_thread_running();
- inc_thread_running();
+ /* keep it withing 1 byte */
+ compile_time_assert(COM_END == 255);
#ifdef WITH_WSREP
if (WSREP(thd))
@@ -1335,6 +1629,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
beginning of each command.
*/
thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
+ if (is_next_command)
+ {
+ drop_more_results= !MY_TEST(thd->server_status &
+ SERVER_MORE_RESULTS_EXISTS);
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
+ }
+
switch (command) {
case COM_INIT_DB:
{
@@ -1353,6 +1654,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#ifdef HAVE_REPLICATION
case COM_REGISTER_SLAVE:
{
+ status_var_increment(thd->status_var.com_register_slave);
if (!register_slave(thd, (uchar*)packet, packet_length))
my_ok(thd);
break;
@@ -1482,9 +1784,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
if (WSREP_ON)
- wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
+ wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+ is_next_command);
else
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+ is_next_command);
while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
! thd->is_error())
@@ -1569,9 +1873,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
if (WSREP_ON)
- wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
+ wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
+ is_next_command);
else
- mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
+ mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
+ is_next_command);
}
@@ -1623,6 +1929,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
packet= arg_end + 1;
thd->reset_for_next_command();
+ // thd->reset_for_next_command reset state => restore it
+ if (is_next_command)
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
lex_start(thd);
/* Must be before we init the table list. */
if (lower_case_table_names)
@@ -1901,6 +2210,66 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
general_log_print(thd, command, NullS);
my_eof(thd);
break;
+ case COM_MULTI:
+ {
+ uint counter;
+ uint current_com= 0;
+ DBUG_ASSERT(!is_com_multi);
+ if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS))
+ {
+ /* The client does not support multiple result sets being sent back */
+ my_error(ER_COMMULTI_BADCONTEXT, MYF(0));
+ break;
+ }
+
+ if (!(counter= maria_multi_check(thd, packet, packet_length)))
+ break;
+
+ {
+ /* We have to store next length because it will be destroyed by '\0' */
+ uint next_subpacket_length= uint3korr(packet);
+ unsigned char *readbuff= net->buff;
+
+ if (net_allocate_new_packet(net, thd, MYF(0)))
+ break;
+
+ while (packet_length)
+ {
+ current_com++;
+ uint subpacket_length= next_subpacket_length + 3;
+ if (subpacket_length < packet_length)
+ next_subpacket_length= uint3korr(packet + subpacket_length);
+ /* safety like in do_command() */
+ packet[subpacket_length]= '\0';
+
+ enum enum_server_command subcommand= fetch_command(thd, (packet + 3));
+
+ if (server_command_flags[subcommand] & CF_NO_COM_MULTI)
+ {
+ my_error(ER_BAD_COMMAND_IN_MULTI, MYF(0), command_name[subcommand]);
+ goto com_multi_end;
+ }
+
+ if (dispatch_command(subcommand, thd, packet + (1 + 3),
+ subpacket_length - (1 + 3), TRUE,
+ (current_com != counter)))
+ {
+ DBUG_ASSERT(thd->is_error());
+ goto com_multi_end;
+ }
+
+ DBUG_ASSERT(subpacket_length <= packet_length);
+ packet+= subpacket_length;
+ packet_length-= subpacket_length;
+ }
+
+com_multi_end:
+ /* release old buffer */
+ DBUG_ASSERT(net->buff == net->write_pos); // nothing to send
+ my_free(readbuff);
+ }
+ break;
+ }
case COM_SLEEP:
case COM_CONNECT: // Impossible here
case COM_TIME: // Impossible from client
@@ -1937,9 +2306,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd_proc_info(thd, "updating status");
/* Finalize server status flags after executing a command. */
thd->update_server_status();
- thd->protocol->end_statement();
- query_cache_end_of_result(thd);
+ if (command != COM_MULTI)
+ {
+ thd->protocol->end_statement();
+ query_cache_end_of_result(thd);
+ }
}
+ if (drop_more_results)
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
if (!thd->is_error() && !thd->killed_errno())
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
@@ -1963,8 +2337,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
- dec_thread_running();
- thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+ if (!is_com_multi)
+ {
+ dec_thread_running();
+ thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+ }
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
#if defined(ENABLED_PROFILING)
@@ -2829,7 +3206,8 @@ mysql_execute_command(THD *thd)
thd->mdl_context.release_transactional_locks();
if (commit_failed)
{
- WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("implicit commit failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
}
@@ -4516,6 +4894,11 @@ end_with_restore_list:
db_name.str= db_name_buff;
db_name.length= lex->name.length;
strmov(db_name.str, lex->name.str);
+
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
+
if (check_db_name(&db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
@@ -4569,6 +4952,9 @@ end_with_restore_list:
/* lex->unit.cleanup() is called outside, no need to call it here */
break;
case SQLCOM_SHOW_CREATE_EVENT:
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
res= Events::show_create_event(thd, lex->spname->m_db,
lex->spname->m_name);
break;
@@ -4628,6 +5014,7 @@ end_with_restore_list:
my_ok(thd);
break;
}
+ case SQLCOM_ALTER_USER:
case SQLCOM_RENAME_USER:
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
@@ -4635,7 +5022,11 @@ end_with_restore_list:
break;
/* Conditionally writes to binlog */
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
- if (!(res= mysql_rename_user(thd, lex->users_list)))
+ if (lex->sql_command == SQLCOM_ALTER_USER)
+ res= mysql_alter_user(thd, lex->users_list);
+ else
+ res= mysql_rename_user(thd, lex->users_list);
+ if (!res)
my_ok(thd);
break;
}
@@ -4797,16 +5188,29 @@ end_with_restore_list:
#ifdef WITH_WSREP
if (lex->type & (
- REFRESH_GRANT |
- REFRESH_HOSTS |
- REFRESH_DES_KEY_FILE |
+ REFRESH_GRANT |
+ REFRESH_HOSTS |
+#ifdef HAVE_OPENSSL
+ REFRESH_DES_KEY_FILE |
+#endif
+ /*
+ Write all flush log statements except
+ FLUSH LOGS
+ FLUSH BINARY LOGS
+ Check reload_acl_and_cache for why.
+ */
+ REFRESH_RELAY_LOG |
+ REFRESH_SLOW_LOG |
+ REFRESH_GENERAL_LOG |
+ REFRESH_ENGINE_LOG |
+ REFRESH_ERROR_LOG |
#ifdef HAVE_QUERY_CACHE
- REFRESH_QUERY_CACHE_FREE |
+ REFRESH_QUERY_CACHE_FREE |
#endif /* HAVE_QUERY_CACHE */
- REFRESH_STATUS |
- REFRESH_USER_RESOURCES))
+ REFRESH_STATUS |
+ REFRESH_USER_RESOURCES))
{
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL)
}
#endif /* WITH_WSREP*/
@@ -4840,11 +5244,11 @@ end_with_restore_list:
*/
if (first_table)
{
- WSREP_TO_ISOLATION_BEGIN(NULL, NULL, first_table);
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
}
else
{
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL);
}
}
#endif /* WITH_WSREP */
@@ -4915,6 +5319,7 @@ end_with_restore_list:
break;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ case SQLCOM_SHOW_CREATE_USER:
case SQLCOM_SHOW_GRANTS:
{
LEX_USER *grant_user= lex->grant_user;
@@ -4931,7 +5336,10 @@ end_with_restore_list:
grant_user->user.str == current_user_and_current_role.str ||
!check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
{
- res = mysql_show_grants(thd, grant_user);
+ if (lex->sql_command == SQLCOM_SHOW_GRANTS)
+ res = mysql_show_grants(thd, grant_user);
+ else
+ res = mysql_show_create_user(thd, grant_user);
}
break;
}
@@ -4968,7 +5376,8 @@ end_with_restore_list:
if (trans_begin(thd, lex->start_transaction_opt))
{
thd->mdl_context.release_transactional_locks();
- WSREP_DEBUG("BEGIN failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("BEGIN failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
my_ok(thd);
@@ -4987,7 +5396,8 @@ end_with_restore_list:
thd->mdl_context.release_transactional_locks();
if (commit_failed)
{
- WSREP_DEBUG("COMMIT failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("COMMIT failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
/* Begin transaction with the same isolation level. */
@@ -5034,7 +5444,8 @@ end_with_restore_list:
if (rollback_failed)
{
- WSREP_DEBUG("rollback failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("rollback failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
/* Begin transaction with the same isolation level. */
@@ -5082,7 +5493,6 @@ end_with_restore_list:
{
uint namelen;
char *name;
- int sp_result= SP_INTERNAL_ERROR;
DBUG_ASSERT(lex->sphead != 0);
DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
@@ -5093,23 +5503,12 @@ end_with_restore_list:
if (check_db_name(&lex->sphead->m_db))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
- goto create_sp_error;
+ goto error;
}
if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
NULL, NULL, 0, 0))
- goto create_sp_error;
-
- /*
- Check that a database directory with this name
- exists. Design note: This won't work on virtual databases
- like information_schema.
- */
- if (check_db_dir_existence(lex->sphead->m_db.str))
- {
- my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
- goto create_sp_error;
- }
+ goto error;
/* Checking the drop permissions if CREATE OR REPLACE is used */
if (lex->create_info.or_replace())
@@ -5117,7 +5516,7 @@ end_with_restore_list:
if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
lex->spname->m_name.str,
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
- goto create_sp_error;
+ goto error;
}
name= lex->sphead->name(&namelen);
@@ -5129,18 +5528,17 @@ end_with_restore_list:
if (udf)
{
my_error(ER_UDF_EXISTS, MYF(0), name);
- goto create_sp_error;
+ goto error;
}
}
#endif
if (sp_process_definer(thd))
- goto create_sp_error;
+ goto error;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
- res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead));
- switch (sp_result) {
- case SP_OK: {
+ if (!sp_create_routine(thd, lex->sphead->m_type, lex->sphead))
+ {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* only add privileges if really neccessary */
@@ -5205,31 +5603,8 @@ end_with_restore_list:
}
#endif
- break;
}
- case SP_WRITE_ROW_FAILED:
- my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
- break;
- case SP_BAD_IDENTIFIER:
- my_error(ER_TOO_LONG_IDENT, MYF(0), name);
- break;
- case SP_BODY_TOO_LONG:
- my_error(ER_TOO_LONG_BODY, MYF(0), name);
- break;
- case SP_FLD_STORE_FAILED:
- my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name);
- break;
- default:
- my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
- break;
- } /* end switch */
-
- /*
- Capture all errors within this CASE and
- clean up the environment.
- */
-create_sp_error:
- if (sp_result != SP_OK )
+ else
goto error;
my_ok(thd);
break; /* break super switch */
@@ -5446,12 +5821,18 @@ create_sp_error:
}
case SQLCOM_SHOW_CREATE_PROC:
{
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
goto error;
break;
}
case SQLCOM_SHOW_CREATE_FUNC:
{
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
goto error;
break;
@@ -5464,6 +5845,9 @@ create_sp_error:
stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
goto error;
if (!sp || sp->show_routine_code(thd))
@@ -5488,6 +5872,9 @@ create_sp_error:
goto error;
}
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (show_create_trigger(thd, lex->spname))
goto error; /* Error has been already logged. */
@@ -5548,7 +5935,8 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
if (commit_failed)
{
- WSREP_DEBUG("XA commit failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("XA commit failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
/*
@@ -5566,7 +5954,8 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
if (rollback_failed)
{
- WSREP_DEBUG("XA rollback failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("XA rollback failed, MDL released: %lld",
+ (longlong) thd->thread_id);
goto error;
}
/*
@@ -5615,6 +6004,8 @@ create_sp_error:
if (check_global_access(thd, SUPER_ACL))
break;
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+
res= create_server(thd, &lex->server_options);
break;
}
@@ -5626,6 +6017,8 @@ create_sp_error:
if (check_global_access(thd, SUPER_ACL))
break;
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+
if ((error= alter_server(thd, &lex->server_options)))
{
DBUG_PRINT("info", ("problem altering server <%s>",
@@ -5644,6 +6037,8 @@ create_sp_error:
if (check_global_access(thd, SUPER_ACL))
break;
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+
if ((err_code= drop_server(thd, &lex->server_options)))
{
if (! lex->if_exists() && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
@@ -5797,8 +6192,8 @@ finish:
! thd->in_active_multi_stmt_transaction() &&
thd->mdl_context.has_transactional_locks())
{
- WSREP_DEBUG("Forcing release of transactional locks for thd %lu",
- thd->thread_id);
+ WSREP_DEBUG("Forcing release of transactional locks for thd: %lld",
+ (longlong) thd->thread_id);
thd->mdl_context.release_transactional_locks();
}
#endif /* WITH_WSREP */
@@ -5820,6 +6215,9 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
new (thd->mem_root) Item_int(thd,
(ulonglong) thd->variables.select_limit);
}
+ if (check_dependencies_in_with_clauses(lex->with_clauses_list))
+ return 1;
+
if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
{
if (lex->describe)
@@ -7102,7 +7500,7 @@ void mysql_init_multi_delete(LEX *lex)
}
static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state)
+ Parser_state *parser_state, bool is_next_command)
{
#ifdef WITH_WSREP
bool is_autocommit=
@@ -7121,7 +7519,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
thd->query_length());
}
- mysql_parse(thd, rawbuf, length, parser_state);
+ mysql_parse(thd, rawbuf, length, parser_state, is_next_command);
if (WSREP(thd)) {
/* wsrep BF abort in query exec phase */
@@ -7164,10 +7562,11 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
}
else
{
- WSREP_DEBUG("%s, thd: %lu is_AC: %d, retry: %lu - %lu SQL: %s",
+ WSREP_DEBUG("%s, thd: %lld is_AC: %d, retry: %lu - %lu SQL: %s",
(thd->wsrep_conflict_state == ABORTED) ?
"BF Aborted" : "cert failure",
- thd->thread_id, is_autocommit, thd->wsrep_retry_counter,
+ (longlong) thd->thread_id, is_autocommit,
+ thd->wsrep_retry_counter,
thd->variables.wsrep_retry_autocommit, thd->query());
my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
thd->killed= NOT_KILLED;
@@ -7218,10 +7617,11 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
@param length Length of the query text
@param[out] found_semicolon For multi queries, position of the character of
the next query in the query text.
+ @param is_next_command there will be more command in the COM_MULTI batch
*/
void mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state)
+ Parser_state *parser_state, bool is_next_command)
{
int error __attribute__((unused));
DBUG_ENTER("mysql_parse");
@@ -7245,6 +7645,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
*/
lex_start(thd);
thd->reset_for_next_command();
+ if (is_next_command)
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
{
@@ -7278,7 +7680,6 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
and Query_log_event::print() would give ';;' output).
This also helps display only the current query in SHOW
PROCESSLIST.
- Note that we don't need LOCK_thread_count to modify query_length.
*/
if (found_semicolon && (ulong) (found_semicolon - thd->query()))
thd->set_query_inner(thd->query(),
@@ -7332,6 +7733,12 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
sql_statement_info[SQLCOM_SELECT].m_key);
status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
thd->update_stats();
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd))
+ {
+ thd->wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
+ }
+#endif /* WITH_WSREP */
}
DBUG_VOID_RETURN;
}
@@ -7987,7 +8394,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
@retval
FALSE if all is OK
@retval
- TRUE if a memory allocation error occured
+ TRUE if a memory allocation error occurred
*/
bool
@@ -8999,9 +9406,7 @@ void get_default_definer(THD *thd, LEX_USER *definer, bool role)
}
definer->user.length= strlen(definer->user.str);
- definer->password= null_lex_str;
- definer->plugin= empty_lex_str;
- definer->auth= empty_lex_str;
+ definer->reset_auth();
}
@@ -9059,7 +9464,7 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
definer->user= *user_name;
definer->host= *host_name;
- definer->password= null_lex_str;
+ definer->reset_auth();
return definer;
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 6cb49f267d2..53a9ed3b24c 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -35,6 +35,7 @@ enum enum_mysql_completiontype {
extern "C" int test_if_data_home_dir(const char *dir);
int error_if_data_home_dir(const char *path, const char *what);
+my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags);
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
@@ -87,7 +88,7 @@ bool is_log_table_write_query(enum enum_sql_command command);
bool alloc_query(THD *thd, const char *packet, uint packet_length);
void mysql_init_select(LEX *lex);
void mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state);
+ Parser_state *parser_state, bool is_com_multi);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void create_table_set_open_action_and_adjust_tables(LEX *lex);
@@ -99,7 +100,8 @@ int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
void do_handle_bootstrap(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
- char* packet, uint packet_length);
+ char* packet, uint packet_length,
+ bool is_com_multi, bool is_next_command);
void log_slow_statement(THD *thd);
bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 0158ab7d206..dbe19674cf2 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1796,7 +1796,8 @@ static void plugin_load(MEM_ROOT *tmp_root)
goto end;
}
- if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE))
+ if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
+ FALSE))
{
sql_print_error("Could not initialize init_read_record; Plugins not "
"loaded");
@@ -2152,7 +2153,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
*/
unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
{ MYSQL_AUDIT_GENERAL_CLASSMASK };
- mysql_audit_acquire_plugins(thd, event_class_mask);
+ if (mysql_audit_general_enabled())
+ mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
error= plugin_add(thd->mem_root, name, &dl, REPORT_TO_USER);
@@ -2282,7 +2284,8 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
*/
unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
{ MYSQL_AUDIT_GENERAL_CLASSMASK };
- mysql_audit_acquire_plugins(thd, event_class_mask);
+ if (mysql_audit_general_enabled())
+ mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic
index da0cc17250b..c3dfde18ab6 100644
--- a/sql/sql_plugin_services.ic
+++ b/sql/sql_plugin_services.ic
@@ -133,7 +133,7 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_thd_query_state_str,
wsrep_thd_retry_counter,
wsrep_thd_set_conflict_state,
- wsrep_thd_skip_append_keys,
+ wsrep_thd_ignore_table,
wsrep_thd_trx_seqno,
wsrep_thd_ws_handle,
wsrep_trx_is_aborting,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index f309084c1bf..2d6a7302afc 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -102,7 +102,11 @@ When one supplies long data for a placeholder:
#include "sql_acl.h" // *_ACL
#include "sql_derived.h" // mysql_derived_prepare,
// mysql_handle_derived
+#include "sql_cte.h"
#include "sql_cursor.h"
+#include "sql_show.h"
+#include "sql_repl.h"
+#include "slave.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
@@ -323,8 +327,14 @@ find_prepared_statement(THD *thd, ulong id)
To strictly separate namespaces of SQL prepared statements and C API
prepared statements find() will return 0 if there is a named prepared
statement with such id.
+
+ LAST_STMT_ID is special value which mean last prepared statement ID
+ (it was made for COM_MULTI to allow prepare and execute a statement
+ in the same command but usage is not limited by COM_MULTI only).
*/
- Statement *stmt= thd->stmt_map.find(id);
+ Statement *stmt= ((id == LAST_STMT_ID) ?
+ thd->last_stmt :
+ thd->stmt_map.find(id));
if (stmt == 0 || stmt->type() != Query_arena::PREPARED_STATEMENT)
return NULL;
@@ -1499,6 +1509,8 @@ static int mysql_test_select(Prepared_statement *stmt,
lex->select_lex.context.resolve_in_select_list= TRUE;
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
+ if (check_dependencies_in_with_clauses(lex->with_clauses_list))
+ goto error;
if (tables)
{
if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
@@ -1802,6 +1814,191 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
}
+static int send_stmt_metadata(THD *thd, Prepared_statement *stmt, List<Item> *fields)
+{
+ if (stmt->is_sql_prepare())
+ return 0;
+
+ if (send_prep_stmt(stmt, fields->elements) ||
+ thd->protocol->send_result_set_metadata(fields, Protocol::SEND_EOF) ||
+ thd->protocol->flush())
+ return 1;
+
+ return 2;
+}
+
+
+/**
+ Validate and prepare for execution SHOW CREATE TABLE statement.
+
+ @param stmt prepared statement
+ @param tables list of tables used in this query
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_create_table(Prepared_statement *stmt,
+ TABLE_LIST *tables)
+{
+ DBUG_ENTER("mysql_test_show_create_table");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+
+ if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+
+/**
+ Validate and prepare for execution SHOW CREATE DATABASE statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_create_db(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_show_create_db");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ mysqld_show_create_db_get_fields(thd, &fields);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Validate and prepare for execution SHOW GRANTS statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_grants(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_show_grants");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ mysql_show_grants_get_fields(thd, &fields, "Grants for");
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+
+
+#ifndef EMBEDDED_LIBRARY
+/**
+ Validate and prepare for execution SHOW SLAVE STATUS statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_slave_status(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_show_slave_status");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ show_master_info_get_fields(thd, &fields, 0, 0);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+
+/**
+ Validate and prepare for execution SHOW MASTER STATUS statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_master_status(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_show_master_status");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ show_binlog_info_get_fields(thd, &fields);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+
+/**
+ Validate and prepare for execution SHOW BINLOGS statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_binlogs(Prepared_statement *stmt)
+{
+ DBUG_ENTER("mysql_test_show_binlogs");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ show_binlogs_get_fields(thd, &fields);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
+
+/**
+ Validate and prepare for execution SHOW CREATE PROC/FUNC statement.
+
+ @param stmt prepared statement
+
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
+*/
+
+static int mysql_test_show_create_routine(Prepared_statement *stmt, int type)
+{
+ DBUG_ENTER("mysql_test_show_binlogs");
+ THD *thd= stmt->thd;
+ List<Item> fields;
+
+ sp_head::show_create_routine_get_fields(thd, type, &fields);
+
+ DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
+}
+
+
/**
@brief Validate and prepare for execution CREATE VIEW statement
@@ -1810,7 +2007,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
@note This function handles create view commands.
@retval FALSE Operation was a success.
- @retval TRUE An error occured.
+ @retval TRUE An error occurred.
*/
static bool mysql_test_create_view(Prepared_statement *stmt)
@@ -2135,7 +2332,66 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_CREATE_TABLE:
res= mysql_test_create_table(stmt);
break;
-
+ case SQLCOM_SHOW_CREATE:
+ if ((res= mysql_test_show_create_table(stmt, tables)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_DB:
+ if ((res= mysql_test_show_create_db(stmt)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ case SQLCOM_SHOW_GRANTS:
+ if ((res= mysql_test_show_grants(stmt)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+#ifndef EMBEDDED_LIBRARY
+ case SQLCOM_SHOW_SLAVE_STAT:
+ if ((res= mysql_test_show_slave_status(stmt)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_MASTER_STAT:
+ if ((res= mysql_test_show_master_status(stmt)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_BINLOGS:
+ if ((res= mysql_test_show_binlogs(stmt)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+#endif /* EMBEDDED_LIBRARY */
+ case SQLCOM_SHOW_CREATE_PROC:
+ if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_FUNC:
+ if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
case SQLCOM_CREATE_VIEW:
if (lex->create_view_mode == VIEW_ALTER)
{
@@ -2202,10 +2458,14 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_CREATE_USER:
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_USER:
+ case SQLCOM_CREATE_ROLE:
+ case SQLCOM_DROP_ROLE:
case SQLCOM_ASSIGN_TO_KEYCACHE:
case SQLCOM_PRELOAD_KEYS:
case SQLCOM_GRANT:
+ case SQLCOM_GRANT_ROLE:
case SQLCOM_REVOKE:
+ case SQLCOM_REVOKE_ROLE:
case SQLCOM_KILL:
case SQLCOM_COMPOUND:
case SQLCOM_SHUTDOWN:
@@ -2325,7 +2585,10 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
+ thd->clear_last_stmt();
}
+ else
+ thd->set_last_stmt(stmt);
thd->protocol= save_protocol;
@@ -2911,6 +3174,9 @@ void mysqld_stmt_close(THD *thd, char *packet)
stmt->deallocate();
general_log_print(thd, thd->get_command(), NullS);
+ if (thd->last_stmt == stmt)
+ thd->clear_last_stmt();
+
DBUG_VOID_RETURN;
}
@@ -3173,7 +3439,8 @@ end:
Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(NULL, &main_mem_root,
- STMT_INITIALIZED, ++thd_arg->statement_id_counter),
+ STMT_INITIALIZED,
+ ((++thd_arg->statement_id_counter) & STMT_ID_MASK)),
thd(thd_arg),
result(thd_arg),
param_array(0),
@@ -3649,8 +3916,9 @@ reexecute:
switch (thd->wsrep_conflict_state)
{
case CERT_FAILURE:
- WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %ld err: %d",
- thd->thread_id, thd->get_stmt_da()->sql_errno() );
+ WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %lld err: %d",
+ (longlong) thd->thread_id,
+ thd->get_stmt_da()->sql_errno() );
thd->wsrep_conflict_state = NO_CONFLICT;
break;
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
index b468ac1bf9b..aec4ac40036 100644
--- a/sql/sql_prepare.h
+++ b/sql/sql_prepare.h
@@ -18,6 +18,10 @@
#include "sql_error.h"
+
+#define LAST_STMT_ID 0xFFFFFFFF
+#define STMT_ID_MASK 0x7FFFFFFF
+
class THD;
struct LEX;
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 95102c82044..b15a80a889a 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -320,11 +320,11 @@
/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
#define UNDEF_POS (-1)
+#endif /* !MYSQL_CLIENT */
+
/* BINLOG_DUMP options */
#define BINLOG_DUMP_NON_BLOCK 1
-#endif /* !MYSQL_CLIENT */
-
#define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
#ifndef MYSQL_CLIENT
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index e524153ad15..c9e2b3a586d 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -2181,9 +2181,7 @@ static int init_binlog_sender(binlog_send_info *info,
linfo->pos= *pos;
// note: publish that we use file, before we open it
- mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo= linfo;
- mysql_mutex_unlock(&LOCK_thread_count);
if (check_start_offset(info, linfo->log_file_name, *pos))
return 1;
@@ -2922,9 +2920,7 @@ err:
mysql_file_close(file, MYF(MY_WME));
}
- mysql_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = 0;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->reset_current_linfo();
thd->variables.max_allowed_packet= old_max_allowed_packet;
delete info->fdev;
@@ -3285,7 +3281,7 @@ int reset_slave(THD *thd, Master_info* mi)
char fname[FN_REFLEN];
int thread_mask= 0, error= 0;
uint sql_errno=ER_UNKNOWN_ERROR;
- const char* errmsg= "Unknown error occured while reseting slave";
+ const char* errmsg= "Unknown error occurred while reseting slave";
char master_info_file_tmp[FN_REFLEN];
char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("reset_slave");
@@ -3379,10 +3375,8 @@ err:
SYNOPSIS
kill_zombie_dump_threads()
slave_server_id the slave's server id
-
*/
-
void kill_zombie_dump_threads(uint32 slave_server_id)
{
mysql_mutex_lock(&LOCK_thread_count);
@@ -3890,7 +3884,7 @@ bool mysql_show_binlog_events(THD* thd)
DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS ||
thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
- /* select wich binary log to use: binlog or relay */
+ /* select which binary log to use: binlog or relay */
if ( thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS )
{
binary_log= &mysql_bin_log;
@@ -3946,9 +3940,7 @@ bool mysql_show_binlog_events(THD* thd)
goto err;
}
- mysql_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = &linfo;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->current_linfo= &linfo;
if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
goto err;
@@ -4080,14 +4072,31 @@ err:
else
my_eof(thd);
- mysql_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = 0;
- mysql_mutex_unlock(&LOCK_thread_count);
+ thd->reset_current_linfo();
thd->variables.max_allowed_packet= old_max_allowed_packet;
DBUG_RETURN(ret);
}
+void show_binlog_info_get_fields(THD *thd, List<Item> *field_list)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "File", FN_REFLEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Position", 20,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Binlog_Do_DB", 255),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Binlog_Ignore_DB", 255),
+ mem_root);
+}
+
+
/**
Execute a SHOW MASTER STATUS statement.
@@ -4100,23 +4109,10 @@ err:
bool show_binlog_info(THD* thd)
{
Protocol *protocol= thd->protocol;
- MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("show_binlog_info");
List<Item> field_list;
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "File", FN_REFLEN),
- mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "Position", 20,
- MYSQL_TYPE_LONGLONG),
- mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Binlog_Do_DB", 255),
- mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Binlog_Ignore_DB", 255),
- mem_root);
+ show_binlog_info_get_fields(thd, &field_list);
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
@@ -4140,6 +4136,19 @@ bool show_binlog_info(THD* thd)
}
+void show_binlogs_get_fields(THD *thd, List<Item> *field_list)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Log_name", 255),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "File_size", 20,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+}
+
+
/**
Execute a SHOW BINARY LOGS statement.
@@ -4159,7 +4168,6 @@ bool show_binlogs(THD* thd)
uint length;
int cur_dir_len;
Protocol *protocol= thd->protocol;
- MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("show_binlogs");
if (!mysql_bin_log.is_open())
@@ -4168,13 +4176,8 @@ bool show_binlogs(THD* thd)
DBUG_RETURN(TRUE);
}
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Log_name", 255),
- mem_root);
- field_list.push_back(new (mem_root)
- Item_return_int(thd, "File_size", 20,
- MYSQL_TYPE_LONGLONG),
- mem_root);
+ show_binlogs_get_fields(thd, &field_list);
+
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 774e43c0a87..e2000bbca73 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -52,6 +52,7 @@ bool purge_master_logs(THD* thd, const char* to_log);
bool purge_master_logs_before_date(THD* thd, time_t purge_time);
bool log_in_use(const char* log_name);
void adjust_linfo_offsets(my_off_t purge_offset);
+void show_binlogs_get_fields(THD *thd, List<Item> *field_list);
bool show_binlogs(THD* thd);
extern int init_master_info(Master_info* mi);
void kill_zombie_dump_threads(uint32 slave_server_id);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 30be455b3d3..cc1310f1632 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015 Oracle and/or its affiliates.
- Copyright (c) 2009, 2015 MariaDB
+ Copyright (c) 2009, 2016 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,6 +53,7 @@
#include "log_slow.h"
#include "sql_derived.h"
#include "sql_statistics.h"
+#include "sql_cte.h"
#include "sql_window.h"
#include "debug_sync.h" // DEBUG_SYNC
@@ -446,7 +447,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
this field from inner subqueries.
@return Status
- @retval true An error occured.
+ @retval true An error occurred.
@retval false OK.
*/
@@ -854,6 +855,10 @@ JOIN::prepare(TABLE_LIST *tables_init,
DBUG_RETURN(-1);
}
}
+
+ With_clause *with_clause=select_lex->get_with_clause();
+ if (with_clause && with_clause->prepare_unreferenced_elements(thd))
+ DBUG_RETURN(1);
int res= check_and_do_in_subquery_rewrites(this);
@@ -1477,7 +1482,7 @@ JOIN::optimize_inner()
}
select= make_select(*table, const_table_map,
- const_table_map, conds, 1, &error);
+ const_table_map, conds, (SORT_INFO*) 0, 1, &error);
if (error)
{ /* purecov: inspected */
error= -1; /* purecov: inspected */
@@ -2642,7 +2647,7 @@ bool JOIN::make_aggr_tables_info()
select_limit : unit->select_limit_cnt;
}
if (!only_const_tables() &&
- !join_tab[const_tables].table->sort.io_cache &&
+ !join_tab[const_tables].filesort &&
!(select_options & SELECT_DESCRIBE))
{
/*
@@ -2990,8 +2995,6 @@ JOIN::reinit()
continue;
tmp_table->file->extra(HA_EXTRA_RESET_STATE);
tmp_table->file->ha_delete_all_rows();
- free_io_cache(tmp_table);
- filesort_free_buffers(tmp_table,0);
}
}
clear_sj_tmp_tables(this);
@@ -3353,7 +3356,6 @@ JOIN::destroy()
for (JOIN_TAB *tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES);
tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
- DBUG_ASSERT(!tab->table || !tab->table->sort.record_pointers);
if (tab->aggr)
{
free_tmp_table(thd, tab->table);
@@ -4167,6 +4169,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
select= make_select(s->table, found_const_table_map,
found_const_table_map,
*s->on_expr_ref ? *s->on_expr_ref : join->conds,
+ (SORT_INFO*) 0,
1, &error);
if (!select)
goto error;
@@ -9114,6 +9117,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
keyuse->val, FALSE);
}
+
inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2)
{
if (*e1)
@@ -9223,7 +9227,7 @@ static void add_not_null_conds(JOIN *join)
if (!referred_tab)
continue;
if (!(notnull= new (join->thd->mem_root)
- Item_func_isnotnull(join->thd, not_null_item)))
+ Item_func_isnotnull(join->thd, item)))
DBUG_VOID_RETURN;
/*
We need to do full fix_fields() call here in order to have correct
@@ -11400,13 +11404,16 @@ bool error_if_full_join(JOIN *join)
void JOIN_TAB::cleanup()
{
DBUG_ENTER("JOIN_TAB::cleanup");
- DBUG_PRINT("enter", ("table %s.%s",
+ DBUG_PRINT("enter", ("tab: %p table %s.%s",
+ this,
(table ? table->s->db.str : "?"),
(table ? table->s->table_name.str : "?")));
delete select;
select= 0;
delete quick;
quick= 0;
+ delete filesort;
+ filesort= 0;
if (cache)
{
cache->free();
@@ -11424,9 +11431,6 @@ void JOIN_TAB::cleanup()
{
table->set_keyread(FALSE);
table->file->ha_index_or_rnd_end();
-
- free_io_cache(table);
- filesort_free_buffers(table, true);
}
if (table)
{
@@ -11829,7 +11833,11 @@ void JOIN::cleanup(bool full)
for (tab= first_breadth_first_tab(); tab;
tab= next_breadth_first_tab(first_breadth_first_tab(),
top_join_tab_count, tab))
+ {
tab->cleanup();
+ delete tab->filesort_result;
+ tab->filesort_result= NULL;
+ }
}
cleaned= true;
//psergey2: added (Q: why not in the above loop?)
@@ -11843,6 +11851,9 @@ void JOIN::cleanup(bool full)
delete curr_tab->tmp_table_param;
curr_tab->tmp_table_param= NULL;
curr_tab->aggr= NULL;
+
+ delete curr_tab->filesort_result;
+ curr_tab->filesort_result= NULL;
}
}
aggr_tables= 0; // psergey3
@@ -11869,8 +11880,8 @@ void JOIN::cleanup(bool full)
tab->table->file->print_error(tmp, MYF(0));
}
}
- free_io_cache(tab->table);
- filesort_free_buffers(tab->table, full);
+ delete tab->filesort_result;
+ tab->filesort_result= NULL;
}
}
}
@@ -12840,7 +12851,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
equality predicates that is equivalent to the conjunction.
Thus, =(a1,a2,a3) can substitute for ((a1=a3) AND (a2=a3) AND (a2=a1)) as
it is equivalent to ((a1=a2) AND (a2=a3)).
- The function always makes a substitution of all equality predicates occured
+ The function always makes a substitution of all equality predicates occurred
in a conjuction for a minimal set of multiple equality predicates.
This set can be considered as a canonical representation of the
sub-conjunction of the equality predicates.
@@ -15615,8 +15626,6 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
the record in the original table.
If item == NULL then fill_record() will update
the temporary table
- @param convert_blob_length If >0 create a varstring(convert_blob_length)
- field instead of blob.
@retval
NULL on error
@@ -15626,23 +15635,12 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
Field *create_tmp_field_from_field(THD *thd, Field *org_field,
const char *name, TABLE *table,
- Item_field *item, uint convert_blob_length)
+ Item_field *item)
{
Field *new_field;
- /*
- Make sure that the blob fits into a Field_varstring which has
- 2-byte lenght.
- */
- if (convert_blob_length && convert_blob_length <= Field_varstring::MAX_SIZE &&
- (org_field->flags & BLOB_FLAG))
- new_field= new Field_varstring(convert_blob_length,
- org_field->maybe_null(),
- org_field->field_name, table->s,
- org_field->charset());
- else
- new_field= org_field->make_new_field(thd->mem_root, table,
- table == org_field->table);
+ new_field= org_field->make_new_field(thd->mem_root, table,
+ table == org_field->table);
if (new_field)
{
new_field->init(table);
@@ -15669,9 +15667,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
}
-Field *Item::create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length,
- uint convert_int_length)
+Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length)
{
Field *UNINIT_VAR(new_field);
MEM_ROOT *mem_root= table->in_use->mem_root;
@@ -15705,16 +15701,6 @@ Field *Item::create_tmp_field(bool group, TABLE *table,
*/
if (field_type() == MYSQL_TYPE_GEOMETRY)
new_field= tmp_table_field_from_field_type(table, true, false);
- /*
- Make sure that the blob fits into a Field_varstring which has
- 2-byte lenght.
- */
- else if (max_length / collation.collation->mbmaxlen > 255 &&
- convert_blob_length <= Field_varstring::MAX_SIZE &&
- convert_blob_length)
- new_field= new (mem_root)
- Field_varstring(convert_blob_length, maybe_null,
- name, table->s, collation.collation);
else
new_field= make_string_field(table);
new_field->set_derivation(collation.derivation);
@@ -15760,12 +15746,11 @@ Field *Item::create_tmp_field(bool group, TABLE *table,
*/
static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
- Item ***copy_func, bool modify_item,
- uint convert_blob_length)
+ Item ***copy_func, bool modify_item)
{
Field *UNINIT_VAR(new_field);
DBUG_ASSERT(thd == table->in_use);
- new_field= item->Item::create_tmp_field(false, table, convert_blob_length);
+ new_field= item->Item::create_tmp_field(false, table);
if (copy_func && item->real_item()->is_result_field())
*((*copy_func)++) = item; // Save for copy_funcs
@@ -15828,8 +15813,6 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
the record in the original table.
If modify_item is 0 then fill_record() will update
the temporary table
- @param convert_blob_length If >0 create a varstring(convert_blob_length)
- field instead of blob.
@retval
0 on error
@@ -15842,8 +15825,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Field **default_field,
bool group, bool modify_item,
bool table_cant_handle_bit_fields,
- bool make_copy_field,
- uint convert_blob_length)
+ bool make_copy_field)
{
Field *result;
Item::Type orig_type= type;
@@ -15860,7 +15842,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
switch (type) {
case Item::SUM_FUNC_ITEM:
{
- result= item->create_tmp_field(group, table, convert_blob_length);
+ result= item->create_tmp_field(group, table);
if (!result)
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return result;
@@ -15896,7 +15878,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
item->maybe_null= orig_item->maybe_null;
}
result= create_tmp_field_from_item(thd, item, table, NULL,
- modify_item, convert_blob_length);
+ modify_item);
*from_field= field->field;
if (result && modify_item)
field->result_field= result;
@@ -15908,7 +15890,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
{
*from_field= field->field;
result= create_tmp_field_from_item(thd, item, table, copy_func,
- modify_item, convert_blob_length);
+ modify_item);
if (result && modify_item)
field->result_field= result;
}
@@ -15918,8 +15900,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
item->name,
table,
modify_item ? field :
- NULL,
- convert_blob_length);
+ NULL);
if (orig_type == Item::REF_ITEM && orig_modify)
((Item_ref*)orig_item)->set_result_field(result);
/*
@@ -15953,8 +15934,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
sp_result_field,
item_func_sp->name,
table,
- NULL,
- convert_blob_length);
+ NULL);
if (modify_item)
item->set_result_field(result_field);
@@ -15987,7 +15967,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
}
return create_tmp_field_from_item(thd, item, table,
(make_copy_field ? 0 : copy_func),
- modify_item, convert_blob_length);
+ modify_item);
case Item::TYPE_HOLDER:
result= ((Item_type_holder *)item)->make_field_by_type(table);
result->set_derivation(item->collation.derivation);
@@ -16125,7 +16105,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
{
/* if we run out of slots or we are not using tempool */
sprintf(path, "%s%lx_%lx_%x", tmp_file_prefix,current_pid,
- thd->thread_id, thd->tmp_table++);
+ (ulong) thd->thread_id, thd->tmp_table++);
}
/*
@@ -16297,8 +16277,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
create_tmp_field(thd, table, arg, arg->type(), &copy_func,
tmp_from_field, &default_field[fieldnr],
group != 0,not_all_columns,
- distinct, 0,
- param->convert_blob_length);
+ distinct, false);
if (!new_field)
goto err; // Should be OOM
tmp_from_field++;
@@ -16368,8 +16347,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
to be usable in this case too.
*/
item->marker == 4 || param->bit_fields_as_long,
- force_copy_fields,
- param->convert_blob_length);
+ force_copy_fields);
if (!new_field)
{
@@ -17100,6 +17078,12 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
+ /*
+ Note that a similar check is performed during
+ subquery_types_allow_materialization. See MDEV-7122 for more details as
+ to why. Whenever this changes, it must be updated there as well, for
+ all tmp_table engines.
+ */
if (keyinfo->key_length > table->file->max_key_length() ||
keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
@@ -17299,6 +17283,12 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
goto err;
bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
+ /*
+ Note that a similar check is performed during
+ subquery_types_allow_materialization. See MDEV-7122 for more details as
+ to why. Whenever this changes, it must be updated there as well, for
+ all tmp_table engines.
+ */
if (keyinfo->key_length > table->file->max_key_length() ||
keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
@@ -17564,7 +17554,6 @@ free_tmp_table(THD *thd, TABLE *entry)
/* free blobs */
for (Field **ptr=entry->field ; *ptr ; ptr++)
(*ptr)->free();
- free_io_cache(entry);
if (entry->temp_pool_slot != MY_BIT_NONE)
bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot);
@@ -18891,7 +18880,18 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
}
}
+ /*
+ The following is needed when one makes ref (or eq_ref) access from row
+ comparisons: one must call row->bring_value() to get the new values.
+ */
+ if (tab && tab->bush_children)
+ {
+ TABLE_LIST *emb_sj_nest= tab->bush_children->start->emb_sj_nest;
+ emb_sj_nest->sj_subq_pred->left_expr->bring_value();
+ }
+
/* TODO: Why don't we do "Late NULLs Filtering" here? */
+
if (cmp_buffer_with_ref(thd, table, table_ref) ||
(table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
{
@@ -19152,7 +19152,7 @@ int join_init_read_record(JOIN_TAB *tab)
if (!tab->preread_init_done && tab->preread_init())
return 1;
if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select,1,1, FALSE))
+ tab->select, tab->filesort_result, 1,1, FALSE))
return 1;
return (*tab->read_record.read_record)(&tab->read_record);
}
@@ -19170,7 +19170,7 @@ join_read_record_no_init(JOIN_TAB *tab)
save_copy_end= tab->read_record.copy_field_end;
init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select,1,1, FALSE);
+ tab->select, tab->filesort_result, 1, 1, FALSE);
tab->read_record.copy_field= save_copy;
tab->read_record.copy_field_end= save_copy_end;
@@ -19440,11 +19440,9 @@ 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;
- if (table->sort.record_pointers ||
- (table->sort.io_cache && my_b_inited(table->sort.io_cache)))
+ if (jt->filesort_result) // If filesort was used
{
- /* Using filesort */
- join->send_records= table->sort.found_records;
+ join->send_records= jt->filesort_result->found_rows;
}
else
{
@@ -20787,7 +20785,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT)
- ref_key= -1;
+ {
+ /*
+ we set ref_key=MAX_KEY instead of -1, because test_if_cheaper ordering
+ assumes that "ref_key==-1" means doing full index scan.
+ (This is not very straightforward and we got into this situation for
+ historical reasons. Should be fixed at some point).
+ */
+ ref_key= MAX_KEY;
+ }
else
{
ref_key= select->quick->index;
@@ -21171,8 +21177,7 @@ use_filesort:
'join' is modified to use this index.
- If no index, create with filesort() an index file that can be used to
retrieve rows in order (should be done with 'read_record').
- The sorted data is stored in tab->table and will be freed when calling
- free_io_cache(tab->table).
+ The sorted data is stored in tab->filesort
RETURN VALUES
0 ok
@@ -21183,12 +21188,11 @@ use_filesort:
int
create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
{
- ha_rows examined_rows;
- ha_rows found_rows;
- ha_rows filesort_retval= HA_POS_ERROR;
+ uint length;
TABLE *table;
SQL_SELECT *select;
bool quick_created= FALSE;
+ SORT_INFO *file_sort= 0;
DBUG_ENTER("create_sort_index");
if (fsort == NULL)
@@ -21202,10 +21206,14 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
+ /*
+ Calculate length of join->order as this may be longer than 'order',
+ which may come from 'group by'. This is needed as join->sortorder is
+ used both for grouping and ordering.
+ */
+ length= 0;
+
- table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_WME | MY_ZEROFILL|
- MY_THREAD_SPECIFIC));
table->status=0; // May be wrong if quick_select
if (!tab->preread_init_done && tab->preread_init())
@@ -21261,11 +21269,16 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
- filesort_retval= filesort(thd, table, fsort, tab->keep_current_rowid,
- &examined_rows, &found_rows,
- fsort->tracker);
- table->sort.found_records= filesort_retval;
- tab->records= found_rows; // For SQL_CALC_ROWS
+ file_sort= filesort(thd, table, fsort, tab->keep_current_rowid, fsort->tracker);
+ DBUG_ASSERT(tab->filesort_result == 0);
+ tab->filesort_result= file_sort;
+ tab->records= 0;
+ if (file_sort)
+ {
+ tab->records= join->select_options & OPTION_FOUND_ROWS ?
+ file_sort->found_rows : file_sort->return_rows;
+ tab->join->join_examined_rows+= file_sort->examined_rows;
+ }
if (quick_created)
{
@@ -21273,13 +21286,13 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
select->cleanup();
}
- tab->join->join_examined_rows+= examined_rows;
table->set_keyread(FALSE); // Restore if we used indexes
if (tab->type == JT_FT)
table->file->ft_end();
else
table->file->ha_index_or_rnd_end();
- DBUG_RETURN(filesort_retval == HA_POS_ERROR);
+
+ DBUG_RETURN(file_sort == 0);
err:
DBUG_RETURN(-1);
}
@@ -21394,7 +21407,6 @@ JOIN_TAB::remove_duplicates()
if (thd->killed == ABORT_QUERY)
thd->reset_killed();
- free_io_cache(table); // Safety
table->file->info(HA_STATUS_VARIABLE);
if (table->s->db_type() == heap_hton ||
(!table->s->blob_fields &&
@@ -21732,7 +21744,11 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables
*/
if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item())
{ /* Order by position */
- uint count= (uint) order_item->val_int();
+ uint count;
+ if (order->counter_used)
+ count= order->counter; // counter was once resolved
+ else
+ count= (uint) order_item->val_int();
if (!count || count > fields.elements)
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
@@ -21749,7 +21765,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables
select_item= find_item_in_list(order_item, fields, &counter,
REPORT_EXCEPT_NOT_FOUND, &resolution);
if (!select_item)
- return TRUE; /* The item is not unique, or some other error occured. */
+ return TRUE; /* The item is not unique, or some other error occurred. */
/* Check whether the resolved field is not ambiguos. */
@@ -23106,8 +23122,8 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
}
join_tab->set_select_cond(cond, __LINE__);
}
- else if ((join_tab->select= make_select(join_tab->table, 0, 0, cond, 0,
- &error)))
+ else if ((join_tab->select= make_select(join_tab->table, 0, 0, cond,
+ (SORT_INFO*) 0, 0, &error)))
join_tab->set_select_cond(cond, __LINE__);
DBUG_RETURN(error ? TRUE : FALSE);
@@ -24204,9 +24220,8 @@ int JOIN::save_explain_data_intern(Explain_query *output,
/* There should be no attempts to save query plans for merged selects */
DBUG_ASSERT(!join->select_lex->master_unit()->derived ||
- join->select_lex->master_unit()->derived->is_materialized_derived());
-
- explain= NULL;
+ join->select_lex->master_unit()->derived->is_materialized_derived() ||
+ join->select_lex->master_unit()->derived->is_with_table());
/* Don't log this into the slow query log */
@@ -24260,6 +24275,7 @@ int JOIN::save_explain_data_intern(Explain_query *output,
save_agg_explain_data(this, xpl_sel);
xpl_sel->exec_const_cond= exec_const_cond;
+ xpl_sel->outer_ref_cond= outer_ref_cond;
if (tmp_having)
xpl_sel->having= tmp_having;
else
@@ -24699,11 +24715,19 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
}
else if (derived)
{
- // A derived table
- str->append('(');
- derived->print(str, query_type);
- str->append(')');
- cmp_name= ""; // Force printing of alias
+ if (!derived->derived->is_with_table())
+ {
+ // A derived table
+ str->append('(');
+ derived->print(str, query_type);
+ str->append(')');
+ cmp_name= ""; // Force printing of alias
+ }
+ else
+ {
+ append_identifier(thd, str, table_name, table_name_length);
+ cmp_name= table_name;
+ }
}
else
{
@@ -25099,7 +25123,7 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from)
@retval REOPT_NEW_PLAN there is a new plan.
@retval REOPT_OLD_PLAN no new improved plan was produced, use the old one.
- @retval REOPT_ERROR an irrecovarable error occured during reoptimization.
+ @retval REOPT_ERROR an irrecovarable error occurred during reoptimization.
*/
JOIN::enum_reopt_result
@@ -25325,8 +25349,12 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
@param table Table if tab == NULL or tab->table
@param usable_keys Key map to find a cheaper key in
@param ref_key
- * 0 <= key < MAX_KEY - key number (hint) to start the search
- * -1 - no key number provided
+ 0 <= key < MAX_KEY - Key that is currently used for finding
+ row
+ MAX_KEY - means index_merge is used
+ -1 - means we're currently not using an
+ index to find rows.
+
@param select_limit LIMIT value
@param [out] new_key Key number if success, otherwise undefined
@param [out] new_key_direction Return -1 (reverse) or +1 if success,
@@ -25355,7 +25383,6 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
uint *saved_best_key_parts)
{
DBUG_ENTER("test_if_cheaper_ordering");
- DBUG_ASSERT(ref_key < int(MAX_KEY));
/*
Check whether there is an index compatible with the given order
usage of which is cheaper than usage of the ref_key index (ref_key>=0)
@@ -25420,7 +25447,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
Calculate the selectivity of the ref_key for REF_ACCESS. For
RANGE_ACCESS we use table->quick_condition_rows.
*/
- if (ref_key >= 0 && !is_hash_join_key_no(ref_key) && tab->type == JT_REF)
+ if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
{
if (table->quick_keys.is_set(ref_key))
refkey_rows_estimate= table->quick_rows[ref_key];
diff --git a/sql/sql_select.h b/sql/sql_select.h
index e85931dcc7c..f5b9cb4684d 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -35,7 +35,6 @@
#include "filesort.h"
typedef struct st_join_table JOIN_TAB;
-
/* Values in optimize */
#define KEY_OPTIMIZE_EXISTS 1
#define KEY_OPTIMIZE_REF_OR_NULL 2
@@ -423,6 +422,7 @@ typedef struct st_join_table {
/* Sorting related info */
Filesort *filesort;
+ SORT_INFO *filesort_result;
/*
Non-NULL value means this join_tab must do window function computation
@@ -1042,7 +1042,7 @@ protected:
enum enum_reopt_result {
REOPT_NEW_PLAN, /* there is a new reoptimized plan */
REOPT_OLD_PLAN, /* no new improved plan can be found, use the old one */
- REOPT_ERROR, /* an irrecovarable error occured during reoptimization */
+ REOPT_ERROR, /* an irrecovarable error occurred during reoptimization */
REOPT_NONE /* not yet reoptimized */
};
@@ -1708,8 +1708,8 @@ bool copy_funcs(Item **func_ptr, const THD *thd);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
Field* create_tmp_field_from_field(THD *thd, Field* org_field,
const char *name, TABLE *table,
- Item_field *item, uint convert_blob_length);
-
+ Item_field *item);
+
bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args);
/* functions from opt_sum.cc */
@@ -1967,8 +1967,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Field **def_field,
bool group, bool modify_item,
bool table_cant_handle_bit_fields,
- bool make_copy_field,
- uint convert_blob_length);
+ bool make_copy_field);
/*
General routine to change field->ptr of a NULL-terminated array of Field
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 0138c3e5a3b..196c138c04d 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -205,8 +205,8 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
free_root(&mem, MYF(0));
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
- if (init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0,
- FALSE))
+ if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL,
+ 1,0, FALSE))
DBUG_RETURN(1);
while (!(read_record_info.read_record(&read_record_info)))
{
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 0d2d07a4503..00881adff1d 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2009, 2015, MariaDB
+ Copyright (c) 2009, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,7 +39,6 @@
#include "tztime.h" // struct Time_zone
#include "sql_acl.h" // TABLE_ACLS, check_grant, DB_ACLS, acl_get,
// check_grant_db
-#include "filesort.h" // filesort_free_buffers
#include "sp.h"
#include "sp_head.h"
#include "sp_pcontext.h"
@@ -1092,39 +1091,29 @@ public:
/*
- Return CREATE command for table or view
+ Return metadata for CREATE command for table or view
@param thd Thread handler
@param table_list Table / view
+ @param field_list resulting list of fields
+ @param buffer resulting CREATE statement
@return
@retval 0 OK
@retval 1 Error
- @notes
- table_list->db and table_list->table_name are kept unchanged to
- not cause problems with SP.
*/
bool
-mysqld_show_create(THD *thd, TABLE_LIST *table_list)
+mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
+ List<Item> *field_list, String *buffer)
{
- Protocol *protocol= thd->protocol;
- char buff[2048];
- String buffer(buff, sizeof(buff), system_charset_info);
- List<Item> field_list;
bool error= TRUE;
MEM_ROOT *mem_root= thd->mem_root;
- DBUG_ENTER("mysqld_show_create");
+ DBUG_ENTER("mysqld_show_create_get_fields");
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->table_name));
- /*
- Metadata locks taken during SHOW CREATE should be released when
- the statmement completes as it is an information statement.
- */
- MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
-
/* We want to preserve the tree for views. */
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
@@ -1155,45 +1144,88 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
goto exit;
}
- buffer.length(0);
+ buffer->length(0);
if (table_list->view)
- buffer.set_charset(table_list->view_creation_ctx->get_client_cs());
+ buffer->set_charset(table_list->view_creation_ctx->get_client_cs());
if ((table_list->view ?
- show_create_view(thd, table_list, &buffer) :
- show_create_table(thd, table_list, &buffer, NULL, WITHOUT_DB_NAME)))
+ show_create_view(thd, table_list, buffer) :
+ show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME)))
goto exit;
if (table_list->view)
{
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "View", NAME_CHAR_LEN),
mem_root);
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "Create View",
- MY_MAX(buffer.length(),1024)),
+ MY_MAX(buffer->length(),1024)),
mem_root);
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "character_set_client",
MY_CS_NAME_SIZE),
mem_root);
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "collation_connection",
MY_CS_NAME_SIZE),
mem_root);
}
else
{
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "Table", NAME_CHAR_LEN),
mem_root);
// 1024 is for not to confuse old clients
- field_list.push_back(new (mem_root)
+ field_list->push_back(new (mem_root)
Item_empty_string(thd, "Create Table",
- MY_MAX(buffer.length(),1024)),
+ MY_MAX(buffer->length(),1024)),
mem_root);
}
+ error= FALSE;
+
+exit:
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Return CREATE command for table or view
+
+ @param thd Thread handler
+ @param table_list Table / view
+
+ @return
+ @retval 0 OK
+ @retval 1 Error
+
+ @notes
+ table_list->db and table_list->table_name are kept unchanged to
+ not cause problems with SP.
+*/
+
+bool
+mysqld_show_create(THD *thd, TABLE_LIST *table_list)
+{
+ Protocol *protocol= thd->protocol;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+ List<Item> field_list;
+ bool error= TRUE;
+ DBUG_ENTER("mysqld_show_create");
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
+ table_list->table_name));
+
+ /*
+ Metadata locks taken during SHOW CREATE should be released when
+ the statmement completes as it is an information statement.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
+
+ if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer))
+ goto exit;
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS |
@@ -1239,6 +1271,19 @@ exit:
DBUG_RETURN(error);
}
+
+void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Database", NAME_CHAR_LEN),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Create Database", 1024),
+ mem_root);
+}
+
+
bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
LEX_STRING *orig_dbname,
const DDL_options_st &options)
@@ -1251,7 +1296,7 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
#endif
Schema_specification_st create;
Protocol *protocol=thd->protocol;
- MEM_ROOT *mem_root= thd->mem_root;
+ List<Item> field_list;
DBUG_ENTER("mysql_show_create_db");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1285,13 +1330,8 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
load_db_opt_by_name(thd, dbname->str, &create);
}
- List<Item> field_list;
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Database", NAME_CHAR_LEN),
- mem_root);
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "Create Database", 1024),
- mem_root);
+
+ mysqld_show_create_db_get_fields(thd, &field_list);
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS |
@@ -1390,14 +1430,13 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
static const char *require_quotes(const char *name, uint name_length)
{
- uint length;
bool pure_digit= TRUE;
const char *end= name + name_length;
for (; name < end ; name++)
{
uchar chr= (uchar) *name;
- length= my_mbcharlen(system_charset_info, chr);
+ int length= my_charlen(system_charset_info, name, end);
if (length == 1 && !system_charset_info->ident_map[chr])
return name;
if (length == 1 && (chr < '0' || chr > '9'))
@@ -1409,20 +1448,17 @@ static const char *require_quotes(const char *name, uint name_length)
}
-/*
- Quote the given identifier if needed and append it to the target string.
- If the given identifier is empty, it will be quoted.
-
- SYNOPSIS
- append_identifier()
- thd thread handler
- packet target string
- name the identifier to be appended
- name_length length of the appending identifier
+/**
+ Convert and quote the given identifier if needed and append it to the
+ target string. If the given identifier is empty, it will be quoted.
+ @thd thread handler
+ @packet target string
+ @name the identifier to be appended
+ @length length of the appending identifier
- RETURN VALUES
- true Error
- false Ok
+ @return
+ 0 success
+ 1 error
*/
bool
@@ -1458,24 +1494,25 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
if (packet->append(&quote_char, 1, quote_charset))
return true;
- for (name_end= name+length ; name < name_end ; name+= length)
+ for (name_end= name+length ; name < name_end ; )
{
uchar chr= (uchar) *name;
- length= my_mbcharlen(system_charset_info, chr);
+ int char_length= my_charlen(system_charset_info, name, name_end);
/*
- my_mbcharlen can return 0 on a wrong multibyte
+ charlen can return 0 and negative numbers on a wrong multibyte
sequence. It is possible when upgrading from 4.0,
and identifier contains some accented characters.
The manual says it does not work. So we'll just
- change length to 1 not to hang in the endless loop.
+ change char_length to 1 not to hang in the endless loop.
*/
- if (!length)
- length= 1;
- if (length == 1 && chr == (uchar) quote_char &&
+ if (char_length <= 0)
+ char_length= 1;
+ if (char_length == 1 && chr == (uchar) quote_char &&
packet->append(&quote_char, 1, quote_charset))
return true;
- if (packet->append(name, length, system_charset_info))
+ if (packet->append(name, char_length, system_charset_info))
return true;
+ name+= char_length;
}
return packet->append(&quote_char, 1, quote_charset);
}
@@ -2314,7 +2351,8 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
We can't just use table->query, because our SQL_MODE may trigger
a different syntax, like when ANSI_QUOTES is defined.
*/
- table->view->unit.print(buff, QT_ORDINARY);
+ table->view->unit.print(buff, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
if (table->with_check != VIEW_CHECK_NONE)
{
@@ -4305,7 +4343,7 @@ uint get_table_open_method(TABLE_LIST *tables,
@retval FALSE No error, if lock was obtained TABLE_LIST::mdl_request::ticket
is set to non-NULL value.
- @retval TRUE Some error occured (probably thread was killed).
+ @retval TRUE Some error occurred (probably thread was killed).
*/
static bool
@@ -4413,7 +4451,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
if (try_acquire_high_prio_shared_mdl_lock(thd, &table_list, can_deadlock))
{
/*
- Some error occured (most probably we have been killed while
+ Some error occurred (most probably we have been killed while
waiting for conflicting locks to go away), let the caller to
handle the situation.
*/
@@ -4451,7 +4489,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
goto end;
}
- share= tdc_acquire_share_shortlived(thd, &table_list, GTS_TABLE | GTS_VIEW);
+ share= tdc_acquire_share(thd, &table_list, GTS_TABLE | GTS_VIEW);
if (!share)
{
res= 0;
@@ -4995,6 +5033,15 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
}
append_create_options(thd, &str, share->option_list, false, 0);
+ if (file)
+ {
+ HA_CREATE_INFO create_info;
+ memset(&create_info, 0, sizeof(create_info));
+ file->update_create_info(&create_info);
+ append_directory(thd, &str, "DATA", create_info.data_file_name);
+ append_directory(thd, &str, "INDEX", create_info.index_file_name);
+ }
+
if (str.length())
table->field[19]->store(str.ptr()+1, str.length()-1, cs);
@@ -5015,7 +5062,10 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
HA_STATUS_TIME |
HA_STATUS_VARIABLE_EXTRA |
HA_STATUS_AUTO)) != 0)
+ {
+ file->print_error(info_error, MYF(0));
goto err;
+ }
enum row_type row_type = file->get_row_type();
switch (row_type) {
@@ -7304,12 +7354,14 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin,
# pointer to 'schema_tables' element
*/
-ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name,
+ bool *in_plugin)
{
schema_table_ref schema_table_a;
ST_SCHEMA_TABLE *schema_table= schema_tables;
DBUG_ENTER("find_schema_table");
+ *in_plugin= false;
for (; schema_table->table_name; schema_table++)
{
if (!my_strcasecmp(system_charset_info,
@@ -7318,6 +7370,7 @@ ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
DBUG_RETURN(schema_table);
}
+ *in_plugin= true;
schema_table_a.table_name= table_name;
if (plugin_foreach(thd, find_schema_table_in_plugin,
MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a))
@@ -8016,8 +8069,6 @@ bool get_schema_tables_result(JOIN *join,
table_list->table->file->extra(HA_EXTRA_NO_CACHE);
table_list->table->file->extra(HA_EXTRA_RESET_STATE);
table_list->table->file->ha_delete_all_rows();
- free_io_cache(table_list->table);
- filesort_free_buffers(table_list->table,1);
table_list->table->null_row= 0;
}
else
@@ -8239,8 +8290,8 @@ ST_FIELD_INFO tables_fields_info[]=
OPEN_FRM_ONLY},
{"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
(MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", OPEN_FULL_TABLE},
- {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options",
- OPEN_FRM_ONLY},
+ {"CREATE_OPTIONS", 2048, MYSQL_TYPE_STRING, 0, 1, "Create_options",
+ OPEN_FULL_TABLE},
{"TABLE_COMMENT", TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
"Comment", OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 029249f4129..9dae78e7f0e 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2005, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -85,7 +86,10 @@ bool append_identifier(THD *thd, String *packet, const char *name,
uint length);
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_get_fields(THD *thd, TABLE_LIST *table_list,
+ List<Item> *field_list, String *buffer);
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
+void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list);
bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name,
LEX_STRING *orig_db_name,
const DDL_options_st &options);
@@ -114,7 +118,10 @@ bool schema_table_store_record(THD *thd, TABLE *table);
void initialize_information_schema_acl();
COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table);
-ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name, bool *in_plugin);
+static inline ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
+{ bool unused; return find_schema_table(thd, table_name, &unused); }
+
ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
int make_schema_select(THD *thd, SELECT_LEX *sel,
ST_SCHEMA_TABLE *schema_table);
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 1622d9df360..6c97ad7e9ab 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -16,8 +16,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "m_string.h" /* memset */
-#include "my_global.h" /* uchar */
#include "my_base.h" /* ha_rows */
#include "my_sys.h" /* qsort2_cmp */
#include "queues.h"
@@ -71,7 +69,6 @@ public:
uint rec_length; // Length of sorted records.
uint sort_length; // Length of sorted columns.
uint ref_length; // Length of record ref.
- uint addon_length; // Length of added packed fields.
uint res_length; // Length of records in final sorted file/buffer.
uint max_keys_per_buffer; // Max keys / buffer.
uint min_dupl_count;
@@ -81,6 +78,8 @@ public:
SORT_FIELD *local_sortorder;
SORT_FIELD *end;
SORT_ADDON_FIELD *addon_field; // Descriptors for companion fields.
+ LEX_STRING addon_buf; // Buffer & length of added packed fields.
+
uchar *unique_buff;
bool not_killable;
char* tmp_buffer;
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 324741fb55e..f6811b020eb 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -28,6 +28,7 @@
#include "key.h"
#include "sql_statistics.h"
#include "opt_range.h"
+#include "uniques.h"
#include "my_atomic.h"
/*
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index b14c3afca4b..20772adcb22 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -76,9 +76,9 @@ bool String::real_alloc(uint32 length)
@retval false Either the copy operation is complete or, if the size of the
new buffer is smaller than the currently allocated buffer (if one exists),
- no allocation occured.
+ no allocation occurred.
- @retval true An error occured when attempting to allocate memory.
+ @retval true An error occurred when attempting to allocate memory.
*/
bool String::realloc_raw(uint32 alloc_length)
{
@@ -789,12 +789,114 @@ int stringcmp(const String *s,const String *t)
}
+/**
+ Return a string which has the same value with "from" and
+ which is safe to modify, trying to avoid unnecessary allocation
+ and copying when possible.
+
+ @param to Buffer. Must not be a constant string.
+ @param from Some existing value. We'll try to reuse it.
+ Can be a constant or a variable string.
+ @param from_length The total size that will be possibly needed.
+ Note, can be 0.
+
+ Note, in some cases "from" and "to" can point to the same object.
+
+ If "from" is a variable string and its allocated memory is enough
+ to store "from_length" bytes, then "from" is returned as is.
+
+ If "from" is a variable string and its allocated memory is not enough
+ to store "from_length" bytes, then "from" is reallocated and returned.
+
+ Otherwise (if "from" is a constant string, or looks like a constant string),
+ then "to" is reallocated to fit "from_length" bytes, the value is copied
+ from "from" to "to", then "to" is returned.
+*/
String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
{
- if (from->Alloced_length >= from_length)
- return from;
- if ((from->alloced && (from->Alloced_length != 0)) || !to || from == to)
+ DBUG_ASSERT(to);
+ /*
+ If "from" is a constant string, e.g.:
+ SELECT INSERT('', <pos>, <length>, <replacement>);
+ we should not return it. See MDEV-9332.
+
+ The code below detects different string types:
+
+ a. All constant strings have Alloced_length==0 and alloced==false.
+ They point to a static memory array, or a mem_root memory,
+ and should stay untouched until the end of their life cycle.
+ Not safe to reuse.
+
+ b. Some variable string have Alloced_length==0 and alloced==false initially,
+ they are not bound to any char array and allocate space on the first use
+ (and become #d). A typical example of such String is Item::str_value.
+ This type of string could be reused, but there is no a way to distinguish
+ them from the true constant strings (#a).
+ Not safe to reuse.
+
+ c. Some variable strings have Alloced_length>0 and alloced==false.
+ They point to a fixed size writtable char array (typically on stack)
+ initially but can later allocate more space on the heap when the
+ fixed size array is too small (these strings become #d after allocation).
+ Safe to reuse.
+
+ d. Some variable strings have Alloced_length>0 and alloced==true.
+ They already store data on the heap.
+ Safe to reuse.
+
+ e. Some strings can have Alloced_length==0 and alloced==true.
+ This type of strings allocate space on the heap, but then are marked
+ as constant strings using String::mark_as_const().
+ A typical example - the result of a character set conversion
+ of a constant string.
+ Not safe to reuse.
+ */
+ if (from->Alloced_length > 0) // "from" is #c or #d (not a constant)
{
+ if (from->Alloced_length >= from_length)
+ return from; // #c or #d (large enough to store from_length bytes)
+
+ if (from->alloced)
+ {
+ (void) from->realloc(from_length);
+ return from; // #d (reallocated to fit from_length bytes)
+ }
+ /*
+ "from" is of type #c. It currently points to a writtable char array
+ (typically on stack), but is too small for "from_length" bytes.
+ We need to reallocate either "from" or "to".
+
+ "from" typically points to a temporary buffer inside Item_xxx::val_str(),
+ or to Item::str_value, and thus is "less permanent" than "to".
+
+ Reallocating "to" may give more benifits:
+ - "to" can point to a "more permanent" storage and can be reused
+ for multiple rows, e.g. str_buffer in Protocol::send_result_set_row(),
+ which is passed to val_str() for all string type rows.
+ - "from" can stay pointing to its original fixed size stack char array,
+ and thus reduce the total amount of my_alloc/my_free.
+ */
+ }
+
+ if (from == to)
+ {
+ /*
+ Possible string types:
+ #a not possible (constants should not be passed as "to")
+ #b possible (a fresh variable with no associated char buffer)
+ #c possible (a variable with a char buffer,
+ in case it's smaller than fixed_length)
+ #d not possible (handled earlier)
+ #e not possible (constants should not be passed as "to")
+
+ If a string of types #a or #e appears here, that means the caller made
+ something wrong. Otherwise, it's safe to reallocate and return "to".
+
+ Note, as we can't distinguish between #a and #b for sure,
+ so we can't assert "not #a", but we can at least assert "not #e".
+ */
+ DBUG_ASSERT(!from->alloced || from->Alloced_length > 0); // Not #e
+
(void) from->realloc(from_length);
return from;
}
@@ -803,7 +905,7 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
if ((to->str_length=MY_MIN(from->str_length,from_length)))
memcpy(to->Ptr,from->Ptr,to->str_length);
to->str_charset=from->str_charset;
- return to;
+ return to; // "from" was of types #a, #b, #e, or small #c.
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 08e47022b48..583058f80a5 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+ Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -90,7 +90,7 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
{
uint res;
uint errors;
- const char *conv_name;
+ const char *conv_name, *conv_name_end;
char tmp_name[FN_REFLEN];
char conv_string[FN_REFLEN];
int quote;
@@ -111,11 +111,13 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
{
DBUG_PRINT("error", ("strconvert of '%s' failed with %u (errors: %u)", conv_name, res, errors));
conv_name= name;
+ conv_name_end= name + name_len;
}
else
{
DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
conv_name= conv_string;
+ conv_name_end= conv_string + res;
}
quote = thd ? get_quote_char_for_identifier(thd, conv_name, res - 1) : '"';
@@ -125,8 +127,8 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
*(to_p++)= (char) quote;
while (*conv_name && (end_p - to_p - 1) > 0)
{
- uint length= my_mbcharlen(system_charset_info, *conv_name);
- if (!length)
+ int length= my_charlen(system_charset_info, conv_name, conv_name_end);
+ if (length <= 0)
length= 1;
if (length == 1 && *conv_name == (char) quote)
{
@@ -573,7 +575,7 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
DBUG_ENTER("build_tmptable_filename");
char *p= strnmov(buff, mysql_tmpdir, bufflen);
- my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x",
+ my_snprintf(p, bufflen - (p - buff), "/%s%lx_%llx_%x",
tmp_file_prefix, current_pid,
thd->thread_id, thd->tmp_table++);
@@ -2232,7 +2234,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
const char *comment_start;
uint32 comment_len;
- built_query.set_charset(system_charset_info);
+ built_query.set_charset(thd->charset());
if (if_exists)
built_query.append("DROP TABLE IF EXISTS ");
else
@@ -3448,8 +3450,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
else
{
/* Field redefined */
+
+ /*
+ If we are replacing a BIT field, revert the increment
+ of total_uneven_bit_length that was done above.
+ */
+ if (sql_field->sql_type == MYSQL_TYPE_BIT &&
+ file->ha_table_flags() & HA_CAN_BIT_FIELD)
+ total_uneven_bit_length-= sql_field->length & 7;
+
sql_field->def= dup_field->def;
sql_field->sql_type= dup_field->sql_type;
+
+ /*
+ If we are replacing a field with a BIT field, we need
+ to initialize pack_flag. Note that we do not need to
+ increment total_uneven_bit_length here as this dup_field
+ has already been processed.
+ */
+ if (sql_field->sql_type == MYSQL_TYPE_BIT)
+ {
+ sql_field->pack_flag= FIELDFLAG_NUMBER;
+ if (!(file->ha_table_flags() & HA_CAN_BIT_FIELD))
+ sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
+ }
+
sql_field->charset= (dup_field->charset ?
dup_field->charset :
create_info->default_table_charset);
@@ -6167,6 +6192,7 @@ static bool fill_alter_inplace_info(THD *thd,
c) flags passed to storage engine contain more detailed information
about nature of changes than those provided from parser.
*/
+ bool maybe_alter_vcol= false;
for (f_ptr= table->field; (field= *f_ptr); f_ptr++)
{
/* Clear marker for renamed or dropped field
@@ -6190,7 +6216,8 @@ static bool fill_alter_inplace_info(THD *thd,
/*
Check if type of column has changed to some incompatible type.
*/
- switch (field->is_equal(new_field))
+ uint is_equal= field->is_equal(new_field);
+ switch (is_equal)
{
case IS_EQUAL_NO:
/* New column type is incompatible with old one. */
@@ -6242,6 +6269,20 @@ static bool fill_alter_inplace_info(THD *thd,
ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE;
}
+ /*
+ Check if the column is computed and either
+ is stored or is used in the partitioning expression.
+ */
+ if (field->vcol_info &&
+ (field->stored_in_db() || field->vcol_info->is_in_partitioning_expr()))
+ {
+ if (is_equal == IS_EQUAL_NO ||
+ !field->vcol_info->is_equal(new_field->vcol_info))
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ else
+ maybe_alter_vcol= true;
+ }
+
/* Check if field was renamed */
if (my_strcasecmp(system_charset_info, field->field_name,
new_field->field_name))
@@ -6305,32 +6346,37 @@ static bool fill_alter_inplace_info(THD *thd,
}
}
+ if (maybe_alter_vcol)
+ {
+ /*
+ No virtual column was altered, but perhaps one of the other columns was,
+ and that column was part of the vcol expression?
+ We don't detect this correctly (FIXME), so let's just say that a vcol
+ *might* be affected if any other column was altered.
+ */
+ if (ha_alter_info->handler_flags &
+ ( Alter_inplace_info::ALTER_COLUMN_TYPE
+ | Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
+ | Alter_inplace_info::ALTER_COLUMN_OPTION ))
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ }
+
new_field_it.init(alter_info->create_list);
while ((new_field= new_field_it++))
{
- Virtual_column_info *vcol_info;
- if (new_field->field)
- vcol_info= new_field->field->vcol_info;
- else
+ if (! new_field->field)
{
- vcol_info= new_field->vcol_info;
/*
Field is not present in old version of table and therefore was added.
Again corresponding storage engine flag should be already set.
*/
DBUG_ASSERT(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
- }
- /*
- Check if the altered column is computed and either
- is stored or is used in the partitioning expression.
- TODO: Mark such a column with an alter flag only if
- the defining expression has changed.
- */
- if (vcol_info &&
- (vcol_info->stored_in_db || vcol_info->is_in_partitioning_expr()))
- {
- ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ if (new_field->vcol_info &&
+ (new_field->stored_in_db() || new_field->vcol_info->is_in_partitioning_expr()))
+ {
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ }
}
}
@@ -6917,7 +6963,7 @@ static bool mysql_inplace_alter_table(THD *thd,
MDL_request *target_mdl_request,
Alter_table_ctx *alter_ctx)
{
- Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED);
handlerton *db_type= table->s->db_type();
MDL_ticket *mdl_ticket= table->mdl_ticket;
HA_CREATE_INFO *create_info= ha_alter_info->create_info;
@@ -9143,13 +9189,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
error, but still worth reporting as it might indicate serious
problem with server.
*/
- goto err_with_mdl;
+ goto err_with_mdl_after_alter;
}
end_inplace:
if (thd->locked_tables_list.reopen_tables(thd))
- goto err_with_mdl;
+ goto err_with_mdl_after_alter;
THD_STAGE_INFO(thd, stage_end);
@@ -9230,6 +9276,10 @@ err_new_table_cleanup:
DBUG_RETURN(true);
+err_with_mdl_after_alter:
+ /* the table was altered. binlog the operation */
+ write_bin_log(thd, true, thd->query(), thd->query_length());
+
err_with_mdl:
/*
An error happened while we were holding exclusive name metadata lock
@@ -9301,13 +9351,13 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
int error= 1;
Copy_field *copy= NULL, *copy_end;
ha_rows found_count= 0, delete_count= 0;
+ SORT_INFO *file_sort= 0;
READ_RECORD info;
TABLE_LIST tables;
List<Item> fields;
List<Item> all_fields;
- ha_rows examined_rows;
- ha_rows found_rows;
bool auto_increment_field_copied= 0;
+ bool init_read_record_done= 0;
ulonglong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id, time_to_report_progress;
Field **dfield_ptr= to->default_field;
@@ -9390,9 +9440,6 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
}
else
{
- from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL |
- MY_THREAD_SPECIFIC));
bzero((char *) &tables, sizeof(tables));
tables.table= from;
tables.alias= tables.table_name= from->s->table_name.str;
@@ -9400,16 +9447,13 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
THD_STAGE_INFO(thd, stage_sorting);
Filesort_tracker dummy_tracker(false);
+ Filesort fsort(order, HA_POS_ERROR, NULL);
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order))
goto err;
- Filesort fsort(order, HA_POS_ERROR, NULL);
- if ((from->sort.found_records= filesort(thd, from, &fsort,
- true,
- &examined_rows, &found_rows,
- &dummy_tracker)) ==
- HA_POS_ERROR)
+
+ if (!(file_sort= filesort(thd, from, &fsort, true, &dummy_tracker)))
goto err;
}
thd_progress_next_stage(thd);
@@ -9419,8 +9463,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
to->mark_virtual_columns_for_write(TRUE);
- if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE))
+ if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, file_sort, 1, 1,
+ FALSE))
goto err;
+ init_read_record_done= 1;
if (ignore && !alter_ctx->fk_error_if_delete_row)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
@@ -9535,9 +9581,6 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
found_count++;
thd->get_stmt_da()->inc_current_row_for_warning();
}
- end_read_record(&info);
- free_io_cache(from);
- delete [] copy;
THD_STAGE_INFO(thd, stage_enabling_keys);
thd_progress_next_stage(thd);
@@ -9558,6 +9601,12 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
error= 1;
err:
+ /* Free resources */
+ if (init_read_record_done)
+ end_read_record(&info);
+ delete [] copy;
+ delete file_sort;
+
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= 0;
*copied= found_count;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 8e7525893eb..642cf208908 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -88,7 +88,7 @@ static my_bool print_cached_tables_callback(TDC_element *element,
THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, element->version,
- in_use ? in_use->thread_id : 0,
+ in_use ? (long) in_use->thread_id : (long) 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
diff --git a/sql/sql_time.h b/sql/sql_time.h
index 5a985710ee1..e0cab5cfa66 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 272e1445273..7a61279fc9c 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1101,6 +1101,7 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
table == (*fld)->table)))
return 1;
+ f->flags= (*fld)->flags;
f->null_ptr= null_ptr;
f->null_bit= null_bit;
if (null_bit == 128)
@@ -2305,7 +2306,7 @@ void Table_triggers_list::mark_fields_used(trg_event_type event)
/**
- Signals to the Table_triggers_list that a parse error has occured when
+ Signals to the Table_triggers_list that a parse error has occurred when
reading a trigger from file. This makes the Table_triggers_list enter an
error state flagged by m_has_unparseable_trigger == true. The error message
will be used whenever a statement invoking or manipulating triggers is
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 4e492e7099d..d85664568a1 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -433,10 +433,8 @@ Field *Type_handler_timestamp::make_conversion_table_field(TABLE *table,
const Field *target)
const
{
- // We assume TIMESTAMP(0)
- return new(table->in_use->mem_root)
- Field_timestamp(NULL, MAX_DATETIME_WIDTH, (uchar *) "", 1,
- Field::NONE, TMPNAME, table->s);
+ return new_Field_timestamp(table->in_use->mem_root, NULL, (uchar *) "", 1,
+ Field::NONE, TMPNAME, table->s, target->decimals());
}
@@ -476,9 +474,8 @@ Field *Type_handler_time::make_conversion_table_field(TABLE *table,
const Field *target)
const
{
- return new(table->in_use->mem_root)
- Field_time(NULL, MAX_TIME_WIDTH, (uchar *) "", 1,
- Field::NONE, TMPNAME);
+ return new_Field_time(table->in_use->mem_root, NULL, (uchar *) "", 1,
+ Field::NONE, TMPNAME, target->decimals());
}
@@ -497,9 +494,8 @@ Field *Type_handler_datetime::make_conversion_table_field(TABLE *table,
const Field *target)
const
{
- return new(table->in_use->mem_root)
- Field_datetime(NULL, MAX_DATETIME_WIDTH, (uchar *) "", 1,
- Field::NONE, TMPNAME);
+ return new_Field_datetime(table->in_use->mem_root, NULL, (uchar *) "", 1,
+ Field::NONE, TMPNAME, target->decimals());
}
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 0b294b5af8c..502bc88c489 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -180,7 +180,8 @@ void udf_init()
}
table= tables.table;
- if (init_read_record(&read_record_info, new_thd, table, NULL,1,0,FALSE))
+ if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
+ FALSE))
{
sql_print_error("Could not initialize init_read_record; udf's not "
"loaded");
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 4dac37eb563..87b836f40d9 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -173,7 +173,8 @@ select_union::create_result_table(THD *thd_arg, List<Item> *column_types,
/**
- Reset and empty the temporary table that stores the materialized query result.
+ Reset and empty the temporary table that stores the materialized query
+ result.
@note The cleanup performed here is exactly the same as for the two temp
tables of JOIN - exec_tmp_table_[1 | 2].
@@ -183,8 +184,6 @@ void select_union::cleanup()
{
table->file->extra(HA_EXTRA_RESET_STATE);
table->file->ha_delete_all_rows();
- free_io_cache(table);
- filesort_free_buffers(table,0);
}
@@ -1152,11 +1151,22 @@ List<Item> *st_select_lex_unit::get_unit_column_types()
return &sl->item_list;
}
+
+static void cleanup_order(ORDER *order)
+{
+ for (; order; order= order->next)
+ order->counter_used= 0;
+}
+
+
bool st_select_lex::cleanup()
{
bool error= FALSE;
DBUG_ENTER("st_select_lex::cleanup()");
+ cleanup_order(order_list.first);
+ cleanup_order(group_list.first);
+
if (join)
{
DBUG_ASSERT((st_select_lex*)join->select_lex == this);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index b9f7b53b2bf..739bef82ab2 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -270,6 +270,7 @@ int mysql_update(THD *thd,
key_map old_covering_keys;
TABLE *table;
SQL_SELECT *select= NULL;
+ SORT_INFO *file_sort= 0;
READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex;
ulonglong id;
@@ -369,6 +370,9 @@ int mysql_update(THD *thd,
if (check_unique_table(thd, table_list))
DBUG_RETURN(TRUE);
+ switch_to_nullable_trigger_fields(fields, table);
+ switch_to_nullable_trigger_fields(values, table);
+
/* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
if (select_lex->optimize_unflattened_subqueries(false))
DBUG_RETURN(TRUE);
@@ -418,7 +422,7 @@ int mysql_update(THD *thd,
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
set_statistics_for_table(thd, table);
- select= make_select(table, 0, 0, conds, 0, &error);
+ select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
if (error || !limit || thd->is_error() ||
(select && select->check_quick(thd, safe_update, limit)))
{
@@ -456,8 +460,6 @@ int mysql_update(THD *thd,
}
init_ftfuncs(thd, select_lex, 1);
- switch_to_nullable_trigger_fields(fields, table);
- switch_to_nullable_trigger_fields(values, table);
table->mark_columns_needed_for_update();
table->update_const_key_parts(conds);
@@ -556,24 +558,15 @@ int mysql_update(THD *thd,
to update
NOTE: filesort will call table->prepare_for_position()
*/
- ha_rows examined_rows;
- ha_rows found_rows;
Filesort fsort(order, limit, select);
- table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL |
- MY_THREAD_SPECIFIC));
Filesort_tracker *fs_tracker=
thd->lex->explain->get_upd_del_plan()->filesort_tracker;
- if ((table->sort.found_records= filesort(thd, table, &fsort, true,
- &examined_rows, &found_rows,
- fs_tracker))
- == HA_POS_ERROR)
- {
+ if (!(file_sort= filesort(thd, table, &fsort, true, fs_tracker)))
goto err;
- }
- thd->inc_examined_row_count(examined_rows);
+ thd->inc_examined_row_count(file_sort->examined_rows);
+
/*
Filesort has already found and selected the rows we want to update,
so we don't need the where clause
@@ -614,7 +607,7 @@ int mysql_update(THD *thd,
*/
if (query_plan.index == MAX_KEY || (select && select->quick))
- error= init_read_record(&info, thd, table, select, 0, 1, FALSE);
+ error= init_read_record(&info, thd, table, select, NULL, 0, 1, FALSE);
else
error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
reverse);
@@ -658,8 +651,9 @@ int mysql_update(THD *thd,
else
{
/*
- Don't try unlocking the row if skip_record reported an error since in
- this case the transaction might have been rolled back already.
+ Don't try unlocking the row if skip_record reported an
+ error since in this case the transaction might have been
+ rolled back already.
*/
if (error < 0)
{
@@ -708,7 +702,7 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->reset())
goto err;
table->file->try_semi_consistent_read(1);
- if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
+ if (init_read_record(&info, thd, table, select, file_sort, 0, 1, FALSE))
goto err;
updated= found= 0;
@@ -1016,6 +1010,7 @@ int mysql_update(THD *thd,
}
DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
+ delete file_sort;
/* If LAST_INSERT_ID(X) was used, report X */
id= thd->arg_of_last_insert_id_function ?
@@ -1049,6 +1044,7 @@ int mysql_update(THD *thd,
err:
delete select;
+ delete file_sort;
free_underlaid_joins(thd, select_lex);
table->set_keyread(false);
thd->abort_on_warning= 0;
@@ -2430,6 +2426,10 @@ int multi_update::do_updates()
int error;
if (table->default_field && (error= table->update_default_fields()))
goto err2;
+ if (table->vfield &&
+ update_virtual_fields(thd, table,
+ (table->triggers ? VCOL_UPDATE_ALL : VCOL_UPDATE_FOR_WRITE)))
+ goto err2;
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@@ -2586,7 +2586,7 @@ bool multi_update::send_eof()
if (local_error > 0) // if the above log write did not fail ...
{
/* Safety: If we haven't got an error before (can happen in do_updates) */
- my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update",
+ my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update",
MYF(0));
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 62e6790a142..41fd5b78f04 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -167,7 +167,7 @@ err:
@param item_list List of Items which should be checked
*/
-static void make_valid_column_names(THD *thd, List<Item> &item_list)
+void make_valid_column_names(THD *thd, List<Item> &item_list)
{
Item *item;
uint name_len;
@@ -215,7 +215,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
- if (tdc_open_view(thd, &decoy, decoy.alias, OPEN_VIEW_NO_PARSE))
+ if (tdc_open_view(thd, &decoy, OPEN_VIEW_NO_PARSE))
return TRUE;
if (!lex->definer)
@@ -244,7 +244,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
@param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
@retval FALSE Operation was a success.
- @retval TRUE An error occured.
+ @retval TRUE An error occurred.
*/
bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
@@ -387,7 +387,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
@note This function handles both create and alter view commands.
@retval FALSE Operation was a success.
- @retval TRUE An error occured.
+ @retval TRUE An error occurred.
*/
bool mysql_create_view(THD *thd, TABLE_LIST *views,
@@ -901,9 +901,11 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
- lex->unit.print(&view_query, QT_VIEW_INTERNAL);
- lex->unit.print(&is_query,
- enum_query_type(QT_TO_SYSTEM_CHARSET | QT_WITHOUT_INTRODUCERS));
+ lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+ lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |
+ QT_WITHOUT_INTRODUCERS |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
thd->variables.sql_mode|= sql_mode;
}
@@ -1535,8 +1537,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
/* Fields in this view can be used in upper select in case of merge. */
if (table->select_lex)
- table->select_lex->select_n_where_fields+=
- lex->select_lex.select_n_where_fields;
+ table->select_lex->add_where_field(&lex->select_lex);
}
/*
This method has a dependency on the proper lock type being set,
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 9c75643fd48..b9eb92198f8 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -56,8 +56,12 @@ bool check_duplicate_names(THD *thd, List<Item>& item_list,
bool mysql_rename_view(THD *thd, const char *new_db, const char *new_name,
TABLE_LIST *view);
+void make_valid_column_names(THD *thd, List<Item> &item_list);
+
#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL)
extern const LEX_STRING view_type;
+void make_valid_column_names(List<Item> &item_list);
+
#endif /* SQL_VIEW_INCLUDED */
diff --git a/sql/sql_window.cc b/sql/sql_window.cc
index 0352a0dfb5f..ef7f512f7fc 100644
--- a/sql/sql_window.cc
+++ b/sql/sql_window.cc
@@ -265,7 +265,7 @@ int rr_from_pointers(READ_RECORD *info);
*/
bool clone_read_record(const READ_RECORD *src, READ_RECORD *dst)
{
- DBUG_ASSERT(src->table->sort.record_pointers);
+ //DBUG_ASSERT(src->table->sort.record_pointers);
DBUG_ASSERT(src->read_record == rr_from_pointers);
memcpy(dst, src, sizeof(READ_RECORD));
return false;
@@ -1534,8 +1534,9 @@ bool Window_func_runner::setup(THD *thd)
bool Window_func_runner::exec(JOIN *join)
{
THD *thd= join->thd;
+ JOIN_TAB *join_tab= &join->join_tab[join->top_join_tab_count];
- if (create_sort_index(thd, join, &join->join_tab[join->top_join_tab_count],
+ if (create_sort_index(thd, join, join_tab,
filesort))
return true;
@@ -1545,19 +1546,18 @@ bool Window_func_runner::exec(JOIN *join)
Go through the sorted array and compute the window function
*/
READ_RECORD info;
- TABLE *tbl= join->join_tab[join->top_join_tab_count].table;
+ TABLE *tbl= join_tab->table;
- if (init_read_record(&info, thd, tbl, NULL/*select*/, 0, 1, FALSE))
+ if (init_read_record(&info, thd, tbl, NULL/*select*/, join_tab->filesort_result,
+ 0, 1, FALSE))
return true;
bool is_error= compute_func(win_func, tbl, &info);
/* This calls filesort_free_buffers(): */
end_read_record(&info);
-
- //TODO: should this be moved to cleanup: ?
- free_io_cache(tbl);
-
+ delete join_tab->filesort_result;
+ join_tab->filesort_result= NULL;
win_func->set_phase_to_retrieval();
return is_error;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index bb939bb6994..b8d0238b753 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -54,6 +54,7 @@
#include "sql_handler.h" // Sql_cmd_handler_*
#include "sql_signal.h"
#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics
+#include "sql_cte.h"
#include "sql_window.h"
#include "item_windowfunc.h"
#include "event_parse_data.h"
@@ -250,6 +251,35 @@ static bool maybe_start_compound_statement(THD *thd)
return 0;
}
+static bool push_sp_label(THD *thd, LEX_STRING label)
+{
+ sp_pcontext *ctx= thd->lex->spcont;
+ sp_label *lab= ctx->find_label(label);
+
+ if (lab)
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), label.str);
+ return 1;
+ }
+ else
+ {
+ lab= thd->lex->spcont->push_label(thd, label,
+ thd->lex->sphead->instructions());
+ lab->type= sp_label::ITERATION;
+ }
+ return 0;
+}
+
+static bool push_sp_empty_label(THD *thd)
+{
+ if (maybe_start_compound_statement(thd))
+ return 1;
+ /* Unlabeled controls get an empty label. */
+ thd->lex->spcont->push_label(thd, empty_lex_str,
+ thd->lex->sphead->instructions());
+ return 0;
+}
+
/**
Helper action for a case expression statement (the expr in 'CASE expr').
This helper is used for 'searched' cases only.
@@ -961,6 +991,8 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
class sp_label *splabel;
class sp_name *spname;
class sp_variable *spvar;
+ class With_clause *with_clause;
+
handlerton *db_type;
st_select_lex *select_lex;
struct p_elem_val *p_elem_value;
@@ -1472,6 +1504,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token REAL /* SQL-2003-R */
%token REBUILD_SYM
%token RECOVER_SYM
+%token RECURSIVE_SYM
%token REDOFILE_SYM
%token REDO_BUFFER_SIZE_SYM
%token REDUNDANT_SYM
@@ -1760,6 +1793,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
case_stmt_body opt_bin_mod
opt_if_exists_table_element opt_if_not_exists_table_element
opt_into opt_procedure_clause
+ opt_recursive
%type <object_ddl_options>
create_or_replace
@@ -1973,6 +2007,7 @@ END_OF_INPUT
%type <NONE> sp_proc_stmt_iterate
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
%type <NONE> case_stmt_specification
+%type <NONE> loop_body while_body repeat_body
%type <num> sp_decl_idents sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
@@ -2013,6 +2048,10 @@ END_OF_INPUT
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
ROLE_SYM
+%type <with_clause> opt_with_clause with_clause
+
+%type <lex_str_ptr> query_name
+
%%
@@ -2577,6 +2616,7 @@ create:
}
view_or_trigger_or_sp_or_event { }
| create_or_replace USER opt_if_not_exists clear_privileges grant_list
+ opt_require_clause opt_resource_options
{
if (Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))
MYSQL_YYABORT;
@@ -3783,7 +3823,7 @@ sp_proc_stmt_statement:
if (yychar == YYEMPTY)
i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
else
- i->m_query.length= lip->get_tok_end() - sp->m_tmp_query;
+ i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
if (!(i->m_query.str= strmake_root(thd->mem_root,
sp->m_tmp_query,
i->m_query.length)) ||
@@ -3825,20 +3865,6 @@ sp_proc_stmt_return:
}
;
-sp_unlabeled_control:
- {
- if (maybe_start_compound_statement(thd))
- MYSQL_YYABORT;
- /* Unlabeled controls get an empty label. */
- Lex->spcont->push_label(thd, empty_lex_str,
- Lex->sphead->instructions());
- }
- sp_control_content
- {
- Lex->sphead->backpatch(Lex->spcont->pop_label());
- }
- ;
-
sp_proc_stmt_leave:
LEAVE_SYM label_ident
{
@@ -4257,41 +4283,6 @@ else_clause_opt:
| ELSE sp_proc_stmts1
;
-sp_labeled_control:
- label_ident ':'
- {
- LEX *lex= Lex;
- sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($1);
-
- if (lab)
- {
- my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- else
- {
- lab= lex->spcont->push_label(thd, $1, lex->sphead->instructions());
- lab->type= sp_label::ITERATION;
- }
- }
- sp_control_content sp_opt_label
- {
- LEX *lex= Lex;
- sp_label *lab= lex->spcont->pop_label();
-
- if ($5.str)
- {
- if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
- {
- my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
- MYSQL_YYABORT;
- }
- }
- lex->sphead->backpatch(lab);
- }
- ;
-
sp_opt_label:
/* Empty */ { $$= null_lex_str; }
| label_ident { $$= $1; }
@@ -4384,8 +4375,7 @@ sp_block_content:
}
;
-sp_control_content:
- LOOP_SYM
+loop_body:
sp_proc_stmts1 END LOOP_SYM
{
LEX *lex= Lex;
@@ -4397,15 +4387,16 @@ sp_control_content:
lex->sphead->add_instr(i))
MYSQL_YYABORT;
}
- | WHILE_SYM
- { Lex->sphead->reset_lex(thd); }
+ ;
+
+while_body:
expr DO_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint ip= sp->instructions();
sp_instr_jump_if_not *i= new (lex->thd->mem_root)
- sp_instr_jump_if_not(ip, lex->spcont, $3, lex);
+ sp_instr_jump_if_not(ip, lex->spcont, $1, lex);
if (i == NULL ||
/* Jumping forward */
sp->push_backpatch(thd, i, lex->spcont->last_label()) ||
@@ -4427,7 +4418,10 @@ sp_control_content:
MYSQL_YYABORT;
lex->sphead->do_cont_backpatch();
}
- | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM
+ ;
+
+repeat_body:
+ sp_proc_stmts1 UNTIL_SYM
{ Lex->sphead->reset_lex(thd); }
expr END REPEAT_SYM
{
@@ -4435,7 +4429,7 @@ sp_control_content:
uint ip= lex->sphead->instructions();
sp_label *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump_if_not *i= new (lex->thd->mem_root)
- sp_instr_jump_if_not(ip, lex->spcont, $5, lab->ip, lex);
+ sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex);
if (i == NULL ||
lex->sphead->add_instr(i))
MYSQL_YYABORT;
@@ -4446,6 +4440,84 @@ sp_control_content:
}
;
+pop_sp_label:
+ sp_opt_label
+ {
+ sp_label *lab;
+ Lex->sphead->backpatch(lab= Lex->spcont->pop_label());
+ if ($1.str)
+ {
+ if (my_strcasecmp(system_charset_info, $1.str,
+ lab->name.str) != 0)
+ {
+ my_error(ER_SP_LABEL_MISMATCH, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ }
+ ;
+
+pop_sp_empty_label:
+ {
+ sp_label *lab;
+ Lex->sphead->backpatch(lab= Lex->spcont->pop_label());
+ DBUG_ASSERT(lab->name.length == 0);
+ }
+ ;
+
+sp_labeled_control:
+ label_ident ':' LOOP_SYM
+ {
+ if (push_sp_label(thd, $1))
+ MYSQL_YYABORT;
+ }
+ loop_body pop_sp_label
+ { }
+ | label_ident ':' WHILE_SYM
+ {
+ if (push_sp_label(thd, $1))
+ MYSQL_YYABORT;
+ Lex->sphead->reset_lex(thd);
+ }
+ while_body pop_sp_label
+ { }
+ | label_ident ':' REPEAT_SYM
+ {
+ if (push_sp_label(thd, $1))
+ MYSQL_YYABORT;
+ }
+ repeat_body pop_sp_label
+ { }
+ ;
+
+sp_unlabeled_control:
+ LOOP_SYM
+ {
+ if (push_sp_empty_label(thd))
+ MYSQL_YYABORT;
+ }
+ loop_body
+ pop_sp_empty_label
+ { }
+ | WHILE_SYM
+ {
+ if (push_sp_empty_label(thd))
+ MYSQL_YYABORT;
+ Lex->sphead->reset_lex(thd);
+ }
+ while_body
+ pop_sp_empty_label
+ { }
+ | REPEAT_SYM
+ {
+ if (push_sp_empty_label(thd))
+ MYSQL_YYABORT;
+ }
+ repeat_body
+ pop_sp_empty_label
+ { }
+ ;
+
trg_action_time:
BEFORE_SYM
{ Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; }
@@ -4912,7 +4984,7 @@ opt_create_partitioning:
/*
This part of the parser is about handling of the partition information.
- It's first version was written by Mikael Ronström with lots of answers to
+ It's first version was written by Mikael Ronstrm with lots of answers to
questions provided by Antony Curtis.
The partition grammar can be called from three places.
@@ -7311,6 +7383,13 @@ alter:
lex->sql_command= SQLCOM_ALTER_SERVER;
lex->server_options.reset($3);
} OPTIONS_SYM '(' server_options_list ')' { }
+ /* ALTER USER foo is allowed for MySQL compatibility. */
+ | ALTER opt_if_exists USER clear_privileges grant_list
+ opt_require_clause opt_resource_options
+ {
+ Lex->create_info.set($2);
+ Lex->sql_command= SQLCOM_ALTER_USER;
+ }
;
ev_alter_on_schedule_completion:
@@ -7368,7 +7447,7 @@ alter_commands:
| remove_partitioning
| partitioning
/*
- This part was added for release 5.1 by Mikael Ronström.
+ This part was added for release 5.1 by Mikael Ronstrm.
From here we insert a number of commands to manage the partitions of a
partitioned table such as adding partitions, dropping partitions,
reorganising partitions in various manners. In future releases the list
@@ -8419,10 +8498,11 @@ opt_ignore_leaves:
select:
- select_init
+ opt_with_clause select_init
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SELECT;
+ lex->current_select->set_with_clause($1);
}
;
@@ -10930,20 +11010,20 @@ table_factor:
and our parser. Possibly this rule could be replaced by our
query_expression_body.
*/
- | '(' get_select_lex select_derived_union ')' opt_table_alias
+ | '('opt_with_clause get_select_lex select_derived_union ')' opt_table_alias
{
- /* Use $2 instead of Lex->current_select as derived table will
+ /* Use $3 instead of Lex->current_select as derived table will
alter value of Lex->current_select. */
- if (!($3 || $5) && $2->embedding &&
- !$2->embedding->nested_join->join_list.elements)
+ if (!($4 || $6) && $3->embedding &&
+ !$3->embedding->nested_join->join_list.elements)
{
- /* we have a derived table ($3 == NULL) but no alias,
+ /* we have a derived table ($4 == NULL) but no alias,
Since we are nested in further parentheses so we
can pass NULL to the outer level parentheses
Permits parsing of "((((select ...))) as xyz)" */
$$= 0;
}
- else if (!$3)
+ else if (!$4)
{
/* Handle case of derived table, alias may be NULL if there
are no outer parentheses, add_table_to_list() will throw
@@ -10951,33 +11031,25 @@ table_factor:
LEX *lex=Lex;
SELECT_LEX *sel= lex->current_select;
SELECT_LEX_UNIT *unit= sel->master_unit();
+ unit->set_with_clause($2);
lex->current_select= sel= unit->outer_select();
Table_ident *ti= new (thd->mem_root) Table_ident(unit);
if (ti == NULL)
MYSQL_YYABORT;
if (!($$= sel->add_table_to_list(lex->thd,
- ti, $5, 0,
+ ti, $6, 0,
TL_READ, MDL_SHARED_READ)))
MYSQL_YYABORT;
sel->add_joined_table($$);
lex->pop_context();
lex->nest_level--;
- /*
- Fields in derived table can be used in upper select in
- case of merge. We do not add HAVING fields because we do
- not merge such derived. We do not add union because
- also do not merge them
- */
- if (!sel->next_select())
- $2->select_n_where_fields+=
- sel->select_n_where_fields;
}
- /*else if (($3->select_lex &&
- $3->select_lex->master_unit()->is_union() &&
- ($3->select_lex->master_unit()->first_select() ==
- $3->select_lex || !$3->lifted)) || $5)*/
- else if ($5 != NULL)
+ /*else if (($4->select_lex &&
+ $4->select_lex->master_unit()->is_union() &&
+ ($4->select_lex->master_unit()->first_select() ==
+ $4->select_lex || !$4->lifted)) || $6)*/
+ else if ($6 != NULL)
{
/*
Tables with or without joins within parentheses cannot
@@ -10990,8 +11062,17 @@ table_factor:
{
/* nested join: FROM (t1 JOIN t2 ...),
nest_level is the same as in the outer query */
- $$= $3;
+ $$= $4;
}
+ /*
+ Fields in derived table can be used in upper select in
+ case of merge. We do not add HAVING fields because we do
+ not merge such derived. We do not add union because
+ also do not merge them
+ */
+ if ($$ && $$->derived &&
+ !$$->derived->first_select()->next_select())
+ $$->select_lex->add_where_field($$->derived->first_select());
}
;
@@ -12968,6 +13049,18 @@ show_param:
lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER;
lex->spname= $3;
}
+ | CREATE USER
+ {
+ Lex->sql_command= SQLCOM_SHOW_CREATE_USER;
+ if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER))))
+ MYSQL_YYABORT;
+ Lex->grant_user->user= current_user;
+ }
+ | CREATE USER user
+ {
+ Lex->sql_command= SQLCOM_SHOW_CREATE_USER;
+ Lex->grant_user= $3;
+ }
| PROCEDURE_SYM STATUS_SYM wild_and_where
{
LEX *lex= Lex;
@@ -13007,9 +13100,10 @@ show_param:
| IDENT_sys remember_tok_start wild_and_where
{
LEX *lex= Lex;
+ bool in_plugin;
lex->sql_command= SQLCOM_SHOW_GENERIC;
- ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str);
- if (!table || !table->old_format)
+ ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin);
+ if (!table || !table->old_format || !in_plugin)
{
my_parse_error(thd, ER_SYNTAX_ERROR, $2);
MYSQL_YYABORT;
@@ -13916,8 +14010,93 @@ temporal_literal:
;
+opt_with_clause:
+ /*empty */ { $$= 0; }
+ | with_clause
+ {
+ $$= $1;
+ Lex->derived_tables|= DERIVED_WITH;
+ }
+ ;
+
+
+with_clause:
+ WITH opt_recursive
+ {
+ With_clause *with_clause=
+ new With_clause($2, Lex->curr_with_clause);
+ if (with_clause == NULL)
+ MYSQL_YYABORT;
+ Lex->curr_with_clause= with_clause;
+ with_clause->add_to_list(Lex->with_clauses_list_last_next);
+ }
+ with_list
+ {
+ $$= Lex->curr_with_clause;
+ Lex->curr_with_clause= Lex->curr_with_clause->pop();
+ }
+ ;
+
+
+opt_recursive:
+ /*empty*/ { $$= 0; }
+ | RECURSIVE_SYM { $$= 1; }
+ ;
+
+
+with_list:
+ with_list_element
+ | with_list ',' with_list_element
+ ;
+
+
+with_list_element:
+ query_name
+ opt_with_column_list
+ AS '(' remember_name subselect remember_end ')'
+ {
+ With_element *elem= new With_element($1, Lex->with_column_list, $6->master_unit());
+ if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
+ MYSQL_YYABORT;
+ Lex->with_column_list.empty();
+ if (elem->set_unparsed_spec(thd, $5+1, $7))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+opt_with_column_list:
+ /* empty */
+ {}
+ | '(' with_column_list ')'
+ ;
+
+
+with_column_list:
+ ident
+ {
+ Lex->with_column_list.push_back((LEX_STRING*)
+ thd->memdup(&$1, sizeof(LEX_STRING)));
+ }
+ | with_column_list ',' ident
+ {
+ Lex->with_column_list.push_back((LEX_STRING*)
+ thd->memdup(&$3, sizeof(LEX_STRING)));
+ }
+ ;
+
+
+query_name:
+ ident
+ {
+ $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING));
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
/**********************************************************************
** Creating different items.
**********************************************************************/
@@ -14343,9 +14522,7 @@ user_maybe_role:
MYSQL_YYABORT;
$$->user = $1;
$$->host= null_lex_str; // User or Role, see get_current_user()
- $$->password= null_lex_str;
- $$->plugin= empty_lex_str;
- $$->auth= empty_lex_str;
+ $$->reset_auth();
if (check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -14357,9 +14534,7 @@ user_maybe_role:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
- $$->password= null_lex_str;
- $$->plugin= empty_lex_str;
- $$->auth= empty_lex_str;
+ $$->reset_auth();
if (check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15275,14 +15450,14 @@ opt_for_user:
;
text_or_password:
- TEXT_STRING { Lex->definer->auth= $1;}
- | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->password= $3; }
+ TEXT_STRING { Lex->definer->pwhash= $1;}
+ | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; }
| OLD_PASSWORD_SYM '(' TEXT_STRING ')'
{
- Lex->definer->password= $3;
- Lex->definer->auth.str= Item_func_password::alloc(thd,
+ Lex->definer->pwtext= $3;
+ Lex->definer->pwhash.str= Item_func_password::alloc(thd,
$3.str, $3.length, Item_func_password::OLD);
- Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
;
@@ -15546,14 +15721,14 @@ grant:
grant_command:
grant_privileges ON opt_table grant_ident TO_SYM grant_list
- require_clause grant_options
+ opt_require_clause opt_grant_options
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_GRANT;
lex->type= 0;
}
| grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
- require_clause grant_options
+ opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -15565,7 +15740,7 @@ grant_command:
lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
- require_clause grant_options
+ opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -15621,9 +15796,7 @@ current_role:
if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
$$->user= current_role;
- $$->password= null_lex_str;
- $$->plugin= empty_lex_str;
- $$->auth= empty_lex_str;
+ $$->reset_auth();
}
;
@@ -15642,9 +15815,7 @@ grant_role:
MYSQL_YYABORT;
$$->user = $1;
$$->host= empty_lex_str;
- $$->password= null_lex_str;
- $$->plugin= empty_lex_str;
- $$->auth= empty_lex_str;
+ $$->reset_auth();
if (check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15860,14 +16031,15 @@ using_or_as: USING | AS ;
grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
- $$=$1; $1->password=$4;
+ $$= $1;
+ $1->pwtext= $4;
if (Lex->sql_command == SQLCOM_REVOKE)
MYSQL_YYABORT;
}
| user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
{
$$= $1;
- $1->auth= $5;
+ $1->pwhash= $5;
}
| user IDENTIFIED_SYM via_or_with ident_or_text
{
@@ -15928,7 +16100,7 @@ column_list_id:
}
;
-require_clause:
+opt_require_clause:
/* empty */
| REQUIRE_SYM require_list
{
@@ -15948,24 +16120,8 @@ require_clause:
}
;
-grant_options:
- /* empty */ {}
- | WITH grant_option_list
- ;
-
-opt_grant_option:
- /* empty */ {}
- | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
- ;
-
-grant_option_list:
- grant_option_list grant_option {}
- | grant_option {}
- ;
-
-grant_option:
- GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | MAX_QUERIES_PER_HOUR ulong_num
+resource_option:
+ MAX_QUERIES_PER_HOUR ulong_num
{
LEX *lex=Lex;
lex->mqh.questions=$2;
@@ -15997,6 +16153,37 @@ grant_option:
}
;
+resource_option_list:
+ resource_option_list resource_option {}
+ | resource_option {}
+ ;
+
+opt_resource_options:
+ /* empty */ {}
+ | WITH resource_option_list
+ ;
+
+
+opt_grant_options:
+ /* empty */ {}
+ | WITH grant_option_list {}
+ ;
+
+opt_grant_option:
+ /* empty */ {}
+ | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ ;
+
+grant_option_list:
+ grant_option_list grant_option {}
+ | grant_option {}
+ ;
+
+grant_option:
+ GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ | resource_option {}
+ ;
+
begin:
BEGIN_SYM
{
@@ -16192,9 +16379,10 @@ query_expression_body:
/* Corresponds to <query expression> in the SQL:2003 standard. */
subselect:
- subselect_start query_expression_body subselect_end
+ subselect_start opt_with_clause query_expression_body subselect_end
{
- $$= $2;
+ $3->set_with_clause($2);
+ $$= $3;
}
;
@@ -16421,7 +16609,7 @@ view_select:
lex->parsing_options.allows_derived= FALSE;
lex->create_view_select.str= (char *) YYLIP->get_cpp_ptr();
}
- view_select_aux view_check_option
+ opt_with_clause view_select_aux view_check_option
{
LEX *lex= Lex;
uint len= YYLIP->get_cpp_ptr() - lex->create_view_select.str;
@@ -16433,6 +16621,7 @@ view_select:
lex->parsing_options.allows_select_into= TRUE;
lex->parsing_options.allows_select_procedure= TRUE;
lex->parsing_options.allows_derived= TRUE;
+ lex->current_select->set_with_clause($2);
}
;
diff --git a/sql/structs.h b/sql/structs.h
index 8dc6323bde0..e51f3e0fe3a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -202,7 +202,8 @@ extern const char *show_comp_option_name[];
typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user {
- LEX_STRING user, host, password, plugin, auth;
+ LEX_STRING user, host, plugin, auth;
+ LEX_STRING pwtext, pwhash;
bool is_role() { return user.str[0] && !host.str[0]; }
void set_lex_string(LEX_STRING *l, char *buf)
{
@@ -211,6 +212,12 @@ typedef struct st_lex_user {
else
l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf;
}
+ void reset_auth()
+ {
+ pwtext.length= pwhash.length= plugin.length= auth.length= 0;
+ pwtext.str= pwhash.str= 0;
+ plugin.str= auth.str= const_cast<char*>("");
+ }
} LEX_USER;
/*
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index cdd2366ca29..9d871703bfe 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -283,7 +283,7 @@ static Sys_var_long Sys_pfs_events_stages_history_size(
/**
Variable performance_schema_max_statement_classes.
The default number of statement classes is the sum of:
- - COM_END for all regular "statement/com/...",
+ - (COM_END - mariadb gap) for all regular "statement/com/...",
- 1 for "statement/com/new_packet", for unknown enum_server_command
- 1 for "statement/com/Error", for invalid enum_server_command
- SQLCOM_END for all regular "statement/sql/...",
@@ -295,7 +295,8 @@ static Sys_var_ulong Sys_pfs_max_statement_classes(
"Maximum number of statement instruments.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_statement_class_sizing),
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
- DEFAULT((ulong) SQLCOM_END + (ulong) COM_END + 4),
+ DEFAULT((ulong) SQLCOM_END +
+ (ulong) (COM_END -(COM_MDB_GAP_END - COM_MDB_GAP_BEG + 1)) + 4),
BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_events_statements_history_long_size(
@@ -1183,6 +1184,19 @@ static Sys_var_mybool Sys_log_queries_not_using_indexes(
GLOBAL_VAR(opt_log_queries_not_using_indexes),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+static Sys_var_mybool Sys_log_slow_admin_statements(
+ "log_slow_admin_statements",
+ "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to "
+ "the slow log if it is open.",
+ GLOBAL_VAR(opt_log_slow_admin_statements),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_log_slow_slave_statements(
+ "log_slow_slave_statements",
+ "Log slow statements executed by slave thread to the slow log if it is open.",
+ GLOBAL_VAR(opt_log_slow_slave_statements),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
static Sys_var_ulong Sys_log_warnings(
"log_warnings",
"Log some not critical warnings to the general log file."
@@ -1437,16 +1451,11 @@ static Sys_var_ulong Sys_metadata_locks_hash_instances(
VALID_RANGE(1, 1024), DEFAULT(8),
BLOCK_SIZE(1));
-/*
- "pseudo_thread_id" variable used in the test suite to detect 32/64bit
- systems. If you change it to something else then ulong then fix the tests
- in mysql-test/include/have_32bit.inc and have_64bit.inc.
-*/
-static Sys_var_ulong Sys_pseudo_thread_id(
+static Sys_var_ulonglong Sys_pseudo_thread_id(
"pseudo_thread_id",
"This variable is for internal server use",
SESSION_ONLY(pseudo_thread_id),
- NO_CMD_LINE, VALID_RANGE(0, ULONG_MAX), DEFAULT(0),
+ NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
ON_CHECK(check_has_super));
@@ -1848,6 +1857,15 @@ static Sys_var_ulong Sys_slave_parallel_threads(
NOT_IN_BINLOG, ON_CHECK(check_slave_parallel_threads),
ON_UPDATE(fix_slave_parallel_threads));
+/* Alias for @@slave_parallel_threads to match what MySQL 5.7 uses. */
+static Sys_var_ulong Sys_slave_parallel_workers(
+ "slave_parallel_workers",
+ "Alias for slave_parallel_threads",
+ 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),
+ ON_UPDATE(fix_slave_parallel_threads));
+
static bool
check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var)
@@ -3189,9 +3207,9 @@ static Sys_var_ulong Sys_table_cache_size(
static Sys_var_ulong Sys_thread_cache_size(
"thread_cache_size",
- "How many threads we should keep in a cache for reuse",
+ "How many threads we should keep in a cache for reuse. These are freed after 5 minutes of idle time",
GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1));
+ VALID_RANGE(0, 16384), DEFAULT(256), BLOCK_SIZE(1));
#ifdef HAVE_POOL_OF_THREADS
static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type)
@@ -3516,7 +3534,8 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
{
thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
thd->mdl_context.release_transactional_locks();
- WSREP_DEBUG("autocommit, MDL TRX lock released: %lu", thd->thread_id);
+ WSREP_DEBUG("autocommit, MDL TRX lock released: %lld",
+ (longlong) thd->thread_id);
return true;
}
/*
@@ -4474,6 +4493,28 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi)
mi->connection_name.str);
return true;
}
+ if (mi->using_gtid != Master_info::USE_GTID_NO && mi->using_parallel())
+ {
+ ulong domain_count;
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ domain_count= rpl_global_gtid_slave_state->count();
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (domain_count > 1)
+ {
+ /*
+ With domain-based parallel replication, the slave position is
+ multi-dimensional, so the relay log position is not very meaningful.
+ It might not even correspond to the next GTID to execute in _any_
+ domain (the case after error stop). So slave_skip_counter will most
+ likely not do what the user intends. Instead give an error, with a
+ suggestion to instead set @@gtid_slave_pos past the point of error;
+ this works reliably also in the case of multiple domains.
+ */
+ my_error(ER_SLAVE_SKIP_NOT_IN_GTID, MYF(0));
+ return true;
+ }
+ }
+
/* The value was stored temporarily in thd */
mi->rli.slave_skip_counter= thd->variables.slave_skip_counter;
return false;
@@ -5032,7 +5073,7 @@ static Sys_var_set Sys_log_slow_filter(
"Log only certain types of queries",
SESSION_VAR(log_slow_filter), CMD_LINE(REQUIRED_ARG),
log_slow_filter_names,
- DEFAULT(MAX_SET(array_elements(log_slow_filter_names)-1)));
+ DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1)));
static const char *default_regex_flags_names[]=
{
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index 2badd5d997a..373f5834838 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -1094,9 +1094,6 @@ public:
}
};
-// overflow-safe (1 << X)-1
-#define MAX_SET(X) ((((1UL << ((X)-1))-1) << 1) | 1)
-
/**
The class for flagset variables - a variant of SET that allows in-place
editing (turning on/off individual bits). String representations looks like
@@ -1131,7 +1128,7 @@ public:
global_var(ulonglong)= def_val;
SYSVAR_ASSERT(typelib.count > 1);
SYSVAR_ASSERT(typelib.count <= 65);
- SYSVAR_ASSERT(def_val < MAX_SET(typelib.count));
+ SYSVAR_ASSERT(def_val < my_set_bits(typelib.count));
SYSVAR_ASSERT(strcmp(values[typelib.count-1], "default") == 0);
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
@@ -1179,7 +1176,7 @@ public:
{
longlong tmp=var->value->val_int();
if ((tmp < 0 && ! var->value->unsigned_flag)
- || (ulonglong)tmp > MAX_SET(typelib.count))
+ || (ulonglong)tmp > my_set_bits(typelib.count))
return true;
else
var->save_result.ulonglong_value= tmp;
@@ -1240,7 +1237,7 @@ public:
global_var(ulonglong)= def_val;
SYSVAR_ASSERT(typelib.count > 0);
SYSVAR_ASSERT(typelib.count <= 64);
- SYSVAR_ASSERT(def_val <= MAX_SET(typelib.count));
+ SYSVAR_ASSERT(def_val <= my_set_bits(typelib.count));
SYSVAR_ASSERT(size == sizeof(ulonglong));
}
bool do_check(THD *thd, set_var *var)
@@ -1278,7 +1275,7 @@ public:
{
longlong tmp=var->value->val_int();
if ((tmp < 0 && ! var->value->unsigned_flag)
- || (ulonglong)tmp > MAX_SET(typelib.count))
+ || (ulonglong)tmp > my_set_bits(typelib.count))
return true;
else
var->save_result.ulonglong_value= tmp;
diff --git a/sql/table.cc b/sql/table.cc
index 2bcc10dec39..dc1730b5b6f 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -40,6 +40,7 @@
#include "discover.h"
#include "mdl.h" // MDL_wait_for_graph_visitor
#include "sql_view.h"
+#include "rpl_filter.h"
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
@@ -62,6 +63,8 @@ LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
*/
LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+static int64 last_table_id;
+
/* Functions defined in this file */
static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
@@ -316,7 +319,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
share->normalized_path.length= path_length;
share->table_category= get_table_category(& share->db, & share->table_name);
share->open_errno= ENOENT;
- share->cached_row_logging_check= -1;
+ /* The following will be fixed in open_table_from_share */
+ share->cached_row_logging_check= 1;
init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
@@ -325,7 +329,16 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
&share->LOCK_share, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
&share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
- tdc_assign_new_table_id(share);
+
+ /*
+ There is one reserved number that cannot be used. Remember to
+ change this when 6-byte global table id's are introduced.
+ */
+ do
+ {
+ share->table_map_id= my_atomic_add64_explicit(&last_table_id, 1,
+ MY_MEMORY_ORDER_RELAXED);
+ } while (unlikely(share->table_map_id == ~0UL));
}
DBUG_RETURN(share);
}
@@ -381,7 +394,7 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
share->path.length= share->normalized_path.length= strlen(path);
share->frm_version= FRM_VER_TRUE_VARCHAR;
- share->cached_row_logging_check= -1;
+ share->cached_row_logging_check= 0; // No row logging
/*
table_map_id is also used for MERGE tables to suppress repeated
@@ -2974,6 +2987,9 @@ partititon_err:
outparam->no_replicate= FALSE;
}
+ if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str))
+ outparam->s->cached_row_logging_check= 0; // No row based replication
+
/* Increment the opened_tables counter, only when open flags set. */
if (db_stat)
thd->status_var.opened_tables++;
@@ -7265,7 +7281,9 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
*/
if (is_merged_derived())
{
- if (is_view() || unit->prepared)
+ if (is_view() ||
+ (unit->prepared &&
+ !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)))
create_field_translation(thd);
}
@@ -7407,6 +7425,11 @@ void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock)
}
}
+bool TABLE_LIST::is_with_table()
+{
+ return derived && derived->with_element;
+}
+
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
diff --git a/sql/table.h b/sql/table.h
index f646fc5ad50..aa7c11ba3d8 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -48,6 +48,7 @@ class ACL_internal_schema_access;
class ACL_internal_table_access;
class Field;
class Table_statistics;
+class With_element;
class TDC_element;
/*
@@ -326,56 +327,6 @@ enum enum_vcol_update_mode
VCOL_UPDATE_ALL
};
-class Filesort_info
-{
- /// Buffer for sorting keys.
- Filesort_buffer filesort_buffer;
-
-public:
- IO_CACHE *io_cache; /* If sorted through filesort */
- uchar *buffpek; /* Buffer for buffpek structures */
- uint buffpek_len; /* Max number of buffpeks in the buffer */
- uchar *addon_buf; /* Pointer to a buffer if sorted with fields */
- size_t addon_length; /* Length of the buffer */
- struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
- void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); /* To unpack back */
- uchar *record_pointers; /* If sorted in memory */
- ha_rows found_records; /* How many records in sort */
-
- Filesort_info(): record_pointers(0) {};
- /** Sort filesort_buffer */
- void sort_buffer(Sort_param *param, uint count)
- { filesort_buffer.sort_buffer(param, count); }
-
- /**
- Accessors for Filesort_buffer (which @c).
- */
- uchar *get_record_buffer(uint idx)
- { return filesort_buffer.get_record_buffer(idx); }
-
- uchar **get_sort_keys()
- { return filesort_buffer.get_sort_keys(); }
-
- uchar **alloc_sort_buffer(uint num_records, uint record_length)
- { return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
-
- bool check_sort_buffer_properties(uint num_records, uint record_length)
- {
- return filesort_buffer.check_sort_buffer_properties(num_records,
- record_length);
- }
-
- void free_sort_buffer()
- { filesort_buffer.free_sort_buffer(); }
-
- void init_record_pointers()
- { filesort_buffer.init_record_pointers(); }
-
- size_t sort_buffer_size() const
- { return filesort_buffer.sort_buffer_size(); }
-};
-
-
class Field_blob;
class Table_triggers_list;
@@ -496,9 +447,6 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db,
const LEX_STRING *name);
-struct TABLE_share;
-struct All_share_tables;
-
typedef struct st_table_field_type
{
LEX_STRING name;
@@ -983,6 +931,57 @@ struct TABLE_SHARE
};
+/**
+ Class is used as a BLOB field value storage for
+ intermediate GROUP_CONCAT results. Used only for
+ GROUP_CONCAT with DISTINCT or ORDER BY options.
+ */
+
+class Blob_mem_storage: public Sql_alloc
+{
+private:
+ MEM_ROOT storage;
+ /**
+ Sign that some values were cut
+ during saving into the storage.
+ */
+ bool truncated_value;
+public:
+ Blob_mem_storage() :truncated_value(false)
+ {
+ init_alloc_root(&storage, MAX_FIELD_VARCHARLENGTH, 0, MYF(0));
+ }
+ ~ Blob_mem_storage()
+ {
+ free_root(&storage, MYF(0));
+ }
+ void reset()
+ {
+ free_root(&storage, MYF(MY_MARK_BLOCKS_FREE));
+ truncated_value= false;
+ }
+ /**
+ Fuction creates duplicate of 'from'
+ string in 'storage' MEM_ROOT.
+
+ @param from string to copy
+ @param length string length
+
+ @retval Pointer to the copied string.
+ @retval 0 if an error occured.
+ */
+ char *store(const char *from, uint length)
+ {
+ return (char*) memdup_root(&storage, from, length);
+ }
+ void set_truncated_value(bool is_truncated_value)
+ {
+ truncated_value= is_truncated_value;
+ }
+ bool is_truncated_value() { return truncated_value; }
+};
+
+
/* Information for one open table */
enum index_hint_type
{
@@ -1014,7 +1013,7 @@ private:
One should use methods of I_P_List template instead.
*/
TABLE *share_all_next, **share_all_prev;
- friend struct All_share_tables;
+ friend class TDC_element;
public:
@@ -1255,8 +1254,13 @@ public:
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
+ /**
+ Initialized in Item_func_group_concat::setup for appropriate
+ temporary table if GROUP_CONCAT is used with ORDER BY | DISTINCT
+ and BLOB field count > 0.
+ */
+ Blob_mem_storage *blob_storage;
GRANT_INFO grant;
- Filesort_info sort;
/*
The arena which the items for expressions from the table definition
are associated with.
@@ -1433,19 +1437,6 @@ struct TABLE_share
};
-struct All_share_tables
-{
- static inline TABLE **next_ptr(TABLE *l)
- {
- return &l->share_all_next;
- }
- static inline TABLE ***prev_ptr(TABLE *l)
- {
- return &l->share_all_prev;
- }
-};
-
-
enum enum_schema_table_state
{
NOT_PROCESSED= 0,
@@ -1864,6 +1855,7 @@ struct TABLE_LIST
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
*/
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
+ With_element *with; /* With element of with_table */
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
/*
@@ -2233,6 +2225,7 @@ struct TABLE_LIST
{
return (derived_type & DTYPE_TABLE);
}
+ bool is_with_table();
inline void set_view()
{
derived_type= DTYPE_VIEW;
@@ -2273,6 +2266,7 @@ struct TABLE_LIST
{
derived_type|= DTYPE_MULTITABLE;
}
+ bool set_as_with_table(THD *thd, With_element *with_elem);
void reset_const_table();
bool handle_derived(LEX *lex, uint phases);
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 2dd368a1945..b6c1e32b350 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -67,7 +67,6 @@ I_P_List <TDC_element,
I_P_List_fast_push_back<TDC_element> > unused_shares;
static int64 tdc_version; /* Increments on each reload */
-static int64 last_table_id;
static bool tdc_inited;
static int32 tc_count; /**< Number of TABLE objects in table cache. */
@@ -599,13 +598,14 @@ void tdc_unlock_share(TDC_element *element)
# Share for table
*/
-TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
- const char *key, uint key_length,
- my_hash_value_type hash_value, uint flags,
+TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
TABLE **out_table)
{
TABLE_SHARE *share;
TDC_element *element;
+ const char *key;
+ uint key_length= get_table_def_key(tl, &key);
+ my_hash_value_type hash_value= tl->mdl_request.key.tc_hash_value();
bool was_unused;
DBUG_ENTER("tdc_acquire_share");
@@ -629,7 +629,7 @@ retry:
lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(element);
- if (!(share= alloc_table_share(db, table_name, key, key_length)))
+ if (!(share= alloc_table_share(tl->db, tl->table_name, key, key_length)))
{
lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);
DBUG_RETURN(0);
@@ -841,7 +841,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name,
bool kill_delayed_threads)
{
- I_P_List <TABLE, TABLE_share> purge_tables;
+ TDC_element::TABLE_list purge_tables;
TABLE *table;
TDC_element *element;
uint my_refs= 1;
@@ -1091,56 +1091,3 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
}
return res;
}
-
-
-/*
- Function to assign a new table map id to a table share.
-
- PARAMETERS
-
- share - Pointer to table share structure
-
- DESCRIPTION
-
- We are intentionally not checking that share->mutex is locked
- since this function should only be called when opening a table
- share and before it is entered into the table definition cache
- (meaning that it cannot be fetched by another thread, even
- accidentally).
-
- PRE-CONDITION(S)
-
- share is non-NULL
- last_table_id_lock initialized (tdc_inited)
-
- POST-CONDITION(S)
-
- share->table_map_id is given a value that with a high certainty is
- not used by any other table (the only case where a table id can be
- reused is on wrap-around, which means more than 4 billion table
- share opens have been executed while one table was open all the
- time).
-
- share->table_map_id is not ~0UL.
-*/
-
-void tdc_assign_new_table_id(TABLE_SHARE *share)
-{
- ulong tid;
- DBUG_ENTER("assign_new_table_id");
- DBUG_ASSERT(share);
- DBUG_ASSERT(tdc_inited);
-
- /*
- There is one reserved number that cannot be used. Remember to
- change this when 6-byte global table id's are introduced.
- */
- do
- {
- tid= my_atomic_add64_explicit(&last_table_id, 1, MY_MEMORY_ORDER_RELAXED);
- } while (unlikely(tid == ~0UL));
-
- share->table_map_id= tid;
- DBUG_PRINT("info", ("table_id= %lu", share->table_map_id));
- DBUG_VOID_RETURN;
-}
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 2c5b0fc45a2..2efc535c425 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -31,7 +31,9 @@ public:
TABLE_SHARE *share;
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
- typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
+ typedef I_P_List <TABLE, I_P_List_adapter<TABLE, &TABLE::share_all_next,
+ &TABLE::share_all_prev> >
+ All_share_tables_list;
/**
Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
all_tables_refs.
@@ -205,11 +207,8 @@ extern void tdc_purge(bool all);
extern TDC_element *tdc_lock_share(THD *thd, const char *db,
const char *table_name);
extern void tdc_unlock_share(TDC_element *element);
-extern TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
- const char *table_name,
- const char *key, uint key_length,
- my_hash_value_type hash_value,
- uint flags, TABLE **out_table);
+extern TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
+ TABLE **out_table= 0);
extern void tdc_release_share(TABLE_SHARE *share);
extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name,
@@ -220,7 +219,6 @@ extern int tdc_wait_for_old_version(THD *thd, const char *db,
ulong refresh_version= ULONG_MAX);
extern ulong tdc_refresh_version(void);
extern ulong tdc_increment_refresh_version(void);
-extern void tdc_assign_new_table_id(TABLE_SHARE *share);
extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
@@ -249,50 +247,3 @@ inline uint tdc_create_key(char *key, const char *db, const char *table_name)
return (uint) (strmake(strmake(key, db, NAME_LEN) + 1, table_name,
NAME_LEN) - key + 1);
}
-
-/**
- Convenience helper: call tdc_acquire_share() without out_table.
-*/
-
-static inline TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
- const char *table_name,
- const char *key,
- uint key_length, uint flags)
-{
- return tdc_acquire_share(thd, db, table_name, key, key_length,
- my_hash_sort(&my_charset_bin, (uchar*) key,
- key_length), flags, 0);
-}
-
-
-/**
- Convenience helper: call tdc_acquire_share() without precomputed cache key.
-*/
-
-static inline TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
- const char *table_name, uint flags)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- key_length= tdc_create_key(key, db, table_name);
- return tdc_acquire_share(thd, db, table_name, key, key_length, flags);
-}
-
-
-/**
- Convenience helper: call tdc_acquire_share() reusing the MDL cache key.
-
- @note lifetime of the returned TABLE_SHARE is limited by the
- lifetime of the TABLE_LIST object!!!
-*/
-
-uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
-
-static inline TABLE_SHARE *tdc_acquire_share_shortlived(THD *thd, TABLE_LIST *tl,
- uint flags)
-{
- const char *key;
- uint key_length= get_table_def_key(tl, &key);
- return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length,
- tl->mdl_request.key.tc_hash_value(), flags, 0);
-}
diff --git a/sql/threadpool.h b/sql/threadpool.h
index 7b62d2cb70a..7ddc661565f 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -27,10 +27,9 @@ extern uint threadpool_oversubscribe; /* Maximum active threads in group */
/* Common thread pool routines, suitable for different implementations */
-extern void threadpool_cleanup_connection(THD *thd);
extern void threadpool_remove_connection(THD *thd);
extern int threadpool_process_request(THD *thd);
-extern int threadpool_add_connection(THD *thd);
+extern THD* threadpool_add_connection(CONNECT *connect, void *scheduled_data);
/*
Functions used by scheduler.
@@ -38,7 +37,7 @@ extern int threadpool_add_connection(THD *thd);
threadpool_unix.cc or threadpool_win.cc
*/
extern bool tp_init();
-extern void tp_add_connection(THD*);
+extern void tp_add_connection(CONNECT *);
extern void tp_wait_begin(THD *, int);
extern void tp_wait_end(THD*);
extern void tp_post_kill_notification(THD *thd);
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index 9fb38319096..2cfa3473222 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -94,7 +94,7 @@ struct Worker_thread_context
/*
Attach/associate the connection with the OS thread,
*/
-static bool thread_attach(THD* thd)
+static void thread_attach(THD* thd)
{
pthread_setspecific(THR_KEY_mysys,thd->mysys_var);
thd->thread_stack=(char*)&thd;
@@ -103,13 +103,14 @@ static bool thread_attach(THD* thd)
if (PSI_server)
PSI_server->set_thread(thd->event_scheduler.m_psi);
#endif
- return 0;
}
-int threadpool_add_connection(THD *thd)
+THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
{
- int retval=1;
+ THD *thd= NULL;
+ int error=1;
+
Worker_thread_context worker_context;
worker_context.save();
@@ -120,13 +121,29 @@ int threadpool_add_connection(THD *thd)
pthread_setspecific(THR_KEY_mysys, 0);
my_thread_init();
- thd->mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys);
- if (!thd->mysys_var)
+ st_my_thread_var* mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys);
+ if (!mysys_var ||!(thd= connect->create_thd()))
{
/* Out of memory? */
+ connect->close_and_delete();
+ if (mysys_var)
+ {
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ current PSI is still from worker thread.
+ Set to 0, to avoid premature cleanup by my_thread_end
+ */
+ if (PSI_server) PSI_server->set_thread(0);
+#endif
+ my_thread_end();
+ }
worker_context.restore();
- return 1;
+ return NULL;
}
+ delete connect;
+ add_to_active_threads(thd);
+ thd->mysys_var= mysys_var;
+ thd->event_scheduler.data= scheduler_data;
/* Create new PSI thread for use with the THD. */
#ifdef HAVE_PSI_INTERFACE
@@ -157,28 +174,19 @@ int threadpool_add_connection(THD *thd)
*/
if (thd_is_connection_alive(thd))
{
- retval= 0;
+ error= 0;
thd->net.reading_or_writing= 1;
thd->skip_wait_timeout= true;
}
}
}
+ if (error)
+ {
+ threadpool_remove_connection(thd);
+ thd= NULL;
+ }
worker_context.restore();
- return retval;
-}
-
-/*
- threadpool_cleanup_connection() does the bulk of connection shutdown work.
- Usually called from threadpool_remove_connection(), but rarely it might
- be called also in the main polling thread if connection initialization fails.
-*/
-void threadpool_cleanup_connection(THD *thd)
-{
- thd->net.reading_or_writing = 0;
- end_connection(thd);
- close_connection(thd, 0);
- unlink_thd(thd);
- mysql_cond_broadcast(&COND_thread_count);
+ return thd;
}
@@ -188,7 +196,12 @@ void threadpool_remove_connection(THD *thd)
worker_context.save();
thread_attach(thd);
- threadpool_cleanup_connection(thd);
+ thd->net.reading_or_writing = 0;
+ end_connection(thd);
+ close_connection(thd, 0);
+ unlink_thd(thd);
+ mysql_cond_broadcast(&COND_thread_count);
+
/*
Free resources associated with this connection:
mysys thread_var and PSI thread.
@@ -221,7 +234,8 @@ int threadpool_process_request(THD *thd)
/*
- In the loop below, the flow is essentially the copy of thead-per-connections
+ In the loop below, the flow is essentially the copy of
+ thead-per-connections
logic, see do_handle_one_connection() in sql_connect.c
The goal is to execute a single query, thus the loop is normally executed
@@ -260,18 +274,31 @@ end:
}
+
+/* Dummy functions, do nothing */
+
+static bool tp_init_new_connection_thread()
+{
+ return 0;
+}
+
+static bool tp_end_thread(THD *, bool)
+{
+ return 0;
+}
+
static scheduler_functions tp_scheduler_functions=
{
0, // max_threads
NULL,
NULL,
tp_init, // init
- NULL, // init_new_connection_thread
+ tp_init_new_connection_thread, // init_new_connection_thread
tp_add_connection, // add_connection
tp_wait_begin, // thd_wait_begin
tp_wait_end, // thd_wait_end
post_kill_notification, // post_kill_notification
- NULL, // end_thread
+ tp_end_thread, // Dummy function
tp_end // end
};
diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc
index 89a2036cb10..91b392eb766 100644
--- a/sql/threadpool_unix.cc
+++ b/sql/threadpool_unix.cc
@@ -116,6 +116,7 @@ struct connection_t
connection_t *next_in_queue;
connection_t **prev_in_queue;
ulonglong abs_wait_timeout;
+ CONNECT* connect;
bool logged_in;
bool bound_to_poll_descriptor;
bool waiting;
@@ -809,7 +810,7 @@ static int create_worker(thread_group_t *thread_group)
if (!err)
{
thread_group->last_thread_creation_time=microsecond_interval_timer();
- thread_created++;
+ statistic_increment(thread_created,&LOCK_status);
add_thread_count(thread_group, 1);
}
else
@@ -1203,18 +1204,19 @@ void wait_end(thread_group_t *thread_group)
Allocate/initialize a new connection structure.
*/
-connection_t *alloc_connection(THD *thd)
+connection_t *alloc_connection()
{
+ connection_t* connection;
DBUG_ENTER("alloc_connection");
+ DBUG_EXECUTE_IF("simulate_failed_connection_1", DBUG_RETURN(0); );
- connection_t* connection = (connection_t *)my_malloc(sizeof(connection_t),0);
- if (connection)
+ if ((connection = (connection_t *)my_malloc(sizeof(connection_t),0)))
{
- connection->thd = thd;
connection->waiting= false;
connection->logged_in= false;
connection->bound_to_poll_descriptor= false;
connection->abs_wait_timeout= ULONGLONG_MAX;
+ connection->thd= 0;
}
DBUG_RETURN(connection);
}
@@ -1225,38 +1227,34 @@ connection_t *alloc_connection(THD *thd)
Add a new connection to thread pool..
*/
-void tp_add_connection(THD *thd)
+void tp_add_connection(CONNECT *connect)
{
+ connection_t *connection;
DBUG_ENTER("tp_add_connection");
-
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
- connection_t *connection= alloc_connection(thd);
- if (connection)
+
+ connection= alloc_connection();
+ if (!connection)
{
- thd->event_scheduler.data= connection;
-
- /* Assign connection to a group. */
- thread_group_t *group=
- &all_groups[thd->thread_id%group_count];
-
- connection->thread_group=group;
+ connect->close_and_delete();
+ DBUG_VOID_RETURN;
+ }
+ connection->connect= connect;
+
+ /* Assign connection to a group. */
+ thread_group_t *group=
+ &all_groups[connect->thread_id%group_count];
+
+ connection->thread_group=group;
- mysql_mutex_lock(&group->mutex);
- group->connection_count++;
- mysql_mutex_unlock(&group->mutex);
+ mysql_mutex_lock(&group->mutex);
+ group->connection_count++;
+ mysql_mutex_unlock(&group->mutex);
- /*
- Add connection to the work queue.Actual logon
- will be done by a worker thread.
- */
- queue_put(group, connection);
- }
- else
- {
- /* Allocation failed */
- threadpool_cleanup_connection(thd);
- }
+ /*
+ Add connection to the work queue.Actual logon
+ will be done by a worker thread.
+ */
+ queue_put(group, connection);
DBUG_VOID_RETURN;
}
@@ -1269,9 +1267,12 @@ static void connection_abort(connection_t *connection)
{
DBUG_ENTER("connection_abort");
thread_group_t *group= connection->thread_group;
-
- threadpool_remove_connection(connection->thd);
-
+
+ if (connection->thd)
+ {
+ threadpool_remove_connection(connection->thd);
+ }
+
mysql_mutex_lock(&group->mutex);
group->connection_count--;
mysql_mutex_unlock(&group->mutex);
@@ -1440,7 +1441,8 @@ static void handle_event(connection_t *connection)
if (!connection->logged_in)
{
- err= threadpool_add_connection(connection->thd);
+ connection->thd = threadpool_add_connection(connection->connect, connection);
+ err= (connection->thd == NULL);
connection->logged_in= true;
}
else
@@ -1548,7 +1550,6 @@ bool tp_init()
DBUG_RETURN(0);
}
-
void tp_end()
{
DBUG_ENTER("tp_end");
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index 4be51f3d6e9..2ca815a6062 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -226,11 +226,12 @@ struct connection_t
PTP_WAIT shm_read;
/* Callback instance, used to inform treadpool about long callbacks */
PTP_CALLBACK_INSTANCE callback_instance;
+ CONNECT* connect;
bool logged_in;
};
-void init_connection(connection_t *connection)
+void init_connection(connection_t *connection, CONNECT *connect)
{
connection->logged_in = false;
connection->handle= 0;
@@ -240,10 +241,11 @@ void init_connection(connection_t *connection)
connection->logged_in = false;
connection->timeout= ULONGLONG_MAX;
connection->callback_instance= 0;
+ connection->thd= 0;
memset(&connection->overlapped, 0, sizeof(OVERLAPPED));
InitializeThreadpoolEnvironment(&connection->callback_environ);
SetThreadpoolCallbackPool(&connection->callback_environ, pool);
- connection->thd = 0;
+ connection->connect= connect;
}
@@ -388,7 +390,7 @@ int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
return 0;
}
- /* Some error occured */
+ /* Some error occurred */
CancelThreadpoolIo(io);
return -1;
}
@@ -396,8 +398,8 @@ int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
int login(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
{
- if (threadpool_add_connection(connection->thd) == 0
- && init_io(connection, connection->thd) == 0
+ if ((connection->thd= threadpool_add_connection(connection->connect, connection))
+ && init_io(connection, connection->thd) == 0
&& start_io(connection, instance) == 0)
{
return 0;
@@ -465,7 +467,7 @@ static void check_thread_init()
if (FlsGetValue(fls) == NULL)
{
FlsSetValue(fls, (void *)1);
- thread_created++;
+ statistic_increment(thread_created, &LOCK_status);
InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads);
}
}
@@ -544,7 +546,6 @@ void tp_end(void)
}
}
-
/*
Handle read completion/notification.
*/
@@ -575,7 +576,7 @@ static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
return;
error:
- /* Some error has occured. */
+ /* Some error has occurred. */
destroy_connection(connection, instance);
free(connection);
@@ -656,24 +657,21 @@ static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance,
/*
Notify the thread pool about a new connection.
- NOTE: LOCK_thread_count is locked on entry. This function must unlock it.
*/
-void tp_add_connection(THD *thd)
-{
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
- connection_t *con = (connection_t *)malloc(sizeof(connection_t));
- if(!con)
+void tp_add_connection(CONNECT *connect)
+{
+ connection_t *con;
+ con= (connection_t *)malloc(sizeof(connection_t));
+ DBUG_EXECUTE_IF("simulate_failed_connection_1", free(con);con= 0; );
+ if (!con)
{
tp_log_warning("Allocation failed", "tp_add_connection");
- threadpool_cleanup_connection(thd);
+ connect->close_and_delete();
return;
}
- init_connection(con);
- con->thd= thd;
- thd->event_scheduler.data= con;
+ init_connection(con, connect);
/* Try to login asynchronously, using threads in the pool */
PTP_WORK wrk = CreateThreadpoolWork(login_callback,con, &con->callback_environ);
@@ -685,7 +683,7 @@ void tp_add_connection(THD *thd)
else
{
/* Likely memory pressure */
- threadpool_cleanup_connection(thd);
+ connect->close_and_delete();
}
}
diff --git a/sql/tztime.cc b/sql/tztime.cc
index f94e10c4877..ce0272d996d 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -309,7 +309,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
Note: See description of TIME_to_gmt_sec() function first.
In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
which defines "shifted by tz offset and leap seconds my_time_t" ->
- my_time_t function wich is almost the same (except ranges of ambiguity)
+ my_time_t function which is almost the same (except ranges of ambiguity)
as reverse function to piecewise linear function used for my_time_t ->
"shifted my_time_t" conversion and which is also specified as table in
zoneinfo file or in our db (It is specified as start of time type ranges
@@ -612,7 +612,7 @@ sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
/*
- Find time range wich contains given my_time_t value
+ Find time range which contains given my_time_t value
SYNOPSIS
find_time_range()
@@ -708,7 +708,7 @@ find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
TODO
We can improve this function by creating joined array of transitions and
leap corrections. This will require adding extra field to TRAN_TYPE_INFO
- for storing number of "extra" seconds to minute occured due to correction
+ for storing number of "extra" seconds to minute occurred due to correction
(60th and 61st second, look how we calculate them as "hit" in this
function).
Under realistic assumptions about frequency of transitions the same array
@@ -2769,7 +2769,7 @@ main(int argc, char **argv)
#ifdef TESTTIME
/*
- Some simple brute-force test wich allowed to catch a pair of bugs.
+ Some simple brute-force test which allowed to catch a pair of bugs.
Also can provide interesting facts about system's time zone support
implementation.
*/
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 63eb6e0eb90..f2fa0bf7b1a 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -37,7 +37,9 @@
#include "sql_sort.h"
#include "queues.h" // QUEUE
#include "my_tree.h" // element_count
-#include "sql_class.h" // Unique
+#include "uniques.h" // Unique
+#include "sql_sort.h"
+#include "myisamchk.h" // BUFFPEK
int unique_write_to_file(uchar* key, element_count count, Unique *unique)
{
@@ -58,8 +60,8 @@ int unique_write_to_file_with_count(uchar* key, element_count count, Unique *uni
int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique)
{
- memcpy(unique->record_pointers, key, unique->size);
- unique->record_pointers+=unique->size;
+ memcpy(unique->sort.record_pointers, key, unique->size);
+ unique->sort.record_pointers+=unique->size;
return 0;
}
@@ -67,8 +69,8 @@ int unique_intersect_write_to_ptrs(uchar* key, element_count count, Unique *uniq
{
if (count >= unique->min_dupl_count)
{
- memcpy(unique->record_pointers, key, unique->size);
- unique->record_pointers+=unique->size;
+ memcpy(unique->sort.record_pointers, key, unique->size);
+ unique->sort.record_pointers+=unique->size;
}
else
unique->filtered_out_elems++;
@@ -80,16 +82,15 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
uint size_arg, ulonglong max_in_memory_size_arg,
uint min_dupl_count_arg)
:max_in_memory_size(max_in_memory_size_arg),
- record_pointers(NULL),
size(size_arg),
elements(0)
{
+ my_b_clear(&file);
min_dupl_count= min_dupl_count_arg;
full_size= size;
if (min_dupl_count_arg)
full_size+= sizeof(element_count);
with_counters= MY_TEST(min_dupl_count_arg);
- my_b_clear(&file);
init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func,
NULL, comp_func_fixed_arg, MYF(MY_THREAD_SPECIFIC));
/* If the following fail's the next add will also fail */
@@ -408,8 +409,10 @@ Unique::reset()
reset_dynamic(&file_ptrs);
reinit_io_cache(&file, WRITE_CACHE, 0L, 0, 1);
}
+ my_free(sort.record_pointers);
elements= 0;
tree.flag= 0;
+ sort.record_pointers= 0;
}
/*
@@ -636,7 +639,7 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
if (elements == 0) /* the whole tree is in memory */
return tree_walk(&tree, action, walk_action_arg, left_root_right);
- table->sort.found_records=elements+tree.elements_in_tree;
+ sort.return_rows= elements+tree.elements_in_tree;
/* flush current tree to the file to have some memory for merge buffer */
if (flush())
return 1;
@@ -663,9 +666,11 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
/*
DESCRIPTION
- Perform multi-pass sort merge of the elements accessed through table->sort,
- using the buffer buff as the merge buffer. The last pass is not performed
- if without_last_merge is TRUE.
+
+ Perform multi-pass sort merge of the elements using the buffer buff as
+ the merge buffer. The last pass is not performed if without_last_merge is
+ TRUE.
+
SYNOPSIS
Unique:merge()
All params are 'IN':
@@ -679,23 +684,19 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
{
- IO_CACHE *outfile= table->sort.io_cache;
+ IO_CACHE *outfile= &sort.io_cache;
BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
uint maxbuffer= file_ptrs.elements - 1;
my_off_t save_pos;
bool error= 1;
+ Sort_param sort_param;
- /* Open cached file if it isn't open */
- if (!outfile)
- outfile= table->sort.io_cache= (IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_THREAD_SPECIFIC|MY_ZEROFILL));
- if (!outfile ||
- (! my_b_inited(outfile) &&
- open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
- MYF(MY_WME))))
+ /* Open cached file for table records if it isn't open */
+ if (! my_b_inited(outfile) &&
+ open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
+ MYF(MY_WME)))
return 1;
- Sort_param sort_param;
bzero((char*) &sort_param,sizeof(sort_param));
sort_param.max_rows= elements;
sort_param.sort_form= table;
@@ -744,44 +745,49 @@ err:
/*
- Modify the TABLE element so that when one calls init_records()
- the rows will be read in priority order.
+ Allocate memory that can be used with init_records() so that
+ rows will be read in priority order.
*/
bool Unique::get(TABLE *table)
{
bool rc= 1;
uchar *sort_buffer= NULL;
- table->sort.found_records= elements+tree.elements_in_tree;
+ sort.return_rows= elements+tree.elements_in_tree;
+ DBUG_ENTER("Unique::get");
if (my_b_tell(&file) == 0)
{
/* Whole tree is in memory; Don't use disk if you don't need to */
- if ((record_pointers=table->sort.record_pointers= (uchar*)
+ if ((sort.record_pointers= (uchar*)
my_malloc(size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
{
+ uchar *save_record_pointers= sort.record_pointers;
tree_walk_action action= min_dupl_count ?
(tree_walk_action) unique_intersect_write_to_ptrs :
(tree_walk_action) unique_write_to_ptrs;
filtered_out_elems= 0;
(void) tree_walk(&tree, action,
this, left_root_right);
- table->sort.found_records-= filtered_out_elems;
- return 0;
+ /* Restore record_pointers that was changed in by 'action' above */
+ sort.record_pointers= save_record_pointers;
+ sort.return_rows-= filtered_out_elems;
+ DBUG_RETURN(0);
}
}
/* Not enough memory; Save the result to file && free memory used by tree */
if (flush())
- return 1;
+ DBUG_RETURN(1);
size_t buff_sz= (max_in_memory_size / full_size + 1) * full_size;
- if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
- return 1;
+ if (!(sort_buffer= (uchar*) my_malloc(buff_sz,
+ MYF(MY_THREAD_SPECIFIC|MY_WME))))
+ DBUG_RETURN(1);
if (merge(table, sort_buffer, FALSE))
- goto err;
+ goto err;
rc= 0;
err:
my_free(sort_buffer);
- return rc;
+ DBUG_RETURN(rc);
}
diff --git a/sql/uniques.h b/sql/uniques.h
new file mode 100644
index 00000000000..0210e879788
--- /dev/null
+++ b/sql/uniques.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2016 MariaDB corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef UNIQUE_INCLUDED
+#define UNIQUE_INCLUDED
+
+#include "filesort.h"
+
+/*
+ Unique -- class for unique (removing of duplicates).
+ Puts all values to the TREE. If the tree becomes too big,
+ it's dumped to the file. User can request sorted values, or
+ just iterate through them. In the last case tree merging is performed in
+ memory simultaneously with iteration, so it should be ~2-3x faster.
+ */
+
+class Unique :public Sql_alloc
+{
+ DYNAMIC_ARRAY file_ptrs;
+ ulong max_elements;
+ ulonglong max_in_memory_size;
+ IO_CACHE file;
+ TREE tree;
+ ulong filtered_out_elems;
+ uint size;
+ uint full_size;
+ uint min_dupl_count; /* always 0 for unions, > 0 for intersections */
+ bool with_counters;
+
+ bool merge(TABLE *table, uchar *buff, bool without_last_merge);
+ bool flush();
+
+public:
+ ulong elements;
+ SORT_INFO sort;
+ Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
+ uint size_arg, ulonglong max_in_memory_size_arg,
+ uint min_dupl_count_arg= 0);
+ ~Unique();
+ ulong elements_in_tree() { return tree.elements_in_tree; }
+ inline bool unique_add(void *ptr)
+ {
+ DBUG_ENTER("unique_add");
+ DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements));
+ if (!(tree.flag & TREE_ONLY_DUPS) &&
+ tree.elements_in_tree >= max_elements && flush())
+ DBUG_RETURN(1);
+ DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg));
+ }
+
+ bool is_in_memory() { return (my_b_tell(&file) == 0); }
+ void close_for_expansion() { tree.flag= TREE_ONLY_DUPS; }
+
+ bool get(TABLE *table);
+
+ /* Cost of searching for an element in the tree */
+ inline static double get_search_cost(ulonglong tree_elems, uint compare_factor)
+ {
+ return log((double) tree_elems) / (compare_factor * M_LN2);
+ }
+
+ static double get_use_cost(uint *buffer, size_t nkeys, uint key_size,
+ ulonglong max_in_memory_size, uint compare_factor,
+ bool intersect_fl, bool *in_memory);
+ inline static int get_cost_calc_buff_size(size_t nkeys, uint key_size,
+ ulonglong max_in_memory_size)
+ {
+ register ulonglong max_elems_in_tree=
+ max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size);
+ return (int) (sizeof(uint)*(1 + nkeys/max_elems_in_tree));
+ }
+
+ void reset();
+ bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg);
+
+ uint get_size() const { return size; }
+ ulonglong get_max_in_memory_size() const { return max_in_memory_size; }
+
+ friend int unique_write_to_file(uchar* key, element_count count, Unique *unique);
+ friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique);
+
+ friend int unique_write_to_file_with_count(uchar* key, element_count count,
+ Unique *unique);
+ friend int unique_intersect_write_to_ptrs(uchar* key, element_count count,
+ Unique *unique);
+};
+
+#endif /* UNIQUE_INCLUDED */
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index c1c6a90e614..cf1feb49f41 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -19,6 +19,7 @@
#include "log_event.h" // class THD, EVENT_LEN_OFFSET, etc.
#include "wsrep_applier.h"
+#include "debug_sync.h"
/*
read the first event from (*buf). The size of the (*buf) is (*buf_len).
@@ -220,6 +221,16 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
{
THD* const thd((THD*)ctx);
+ // Allow tests to block the applier thread using the DBUG facilities.
+ DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
+ {
+ const char act[]=
+ "now "
+ "wait_for signal.wsrep_apply_cb";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+
thd->wsrep_trx_meta = *meta;
#ifdef WSREP_PROC_INFO
@@ -269,8 +280,8 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
TABLE *tmp;
while ((tmp = thd->temporary_tables))
{
- WSREP_DEBUG("Applier %lu, has temporary tables: %s.%s",
- thd->thread_id,
+ WSREP_DEBUG("Applier %lld, has temporary tables: %s.%s",
+ (longlong) thd->thread_id,
(tmp->s) ? tmp->s->db.str : "void",
(tmp->s) ? tmp->s->table_name.str : "void");
close_temporary_table(thd, tmp, 1, 1);
@@ -279,8 +290,7 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
return rcode;
}
-static wsrep_cb_status_t wsrep_commit(THD* const thd,
- wsrep_seqno_t const global_seqno)
+static wsrep_cb_status_t wsrep_commit(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
@@ -299,7 +309,11 @@ static wsrep_cb_status_t wsrep_commit(THD* const thd,
#ifdef GTID_SUPPORT
thd->variables.gtid_next.set_automatic();
#endif /* GTID_SUPPORT */
- // TODO: mark snapshot with global_seqno.
+ if (thd->wsrep_apply_toi)
+ {
+ wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid,
+ thd->wsrep_trx_meta.gtid.seqno);
+ }
}
#ifdef WSREP_PROC_INFO
@@ -313,8 +327,7 @@ static wsrep_cb_status_t wsrep_commit(THD* const thd,
return rcode;
}
-static wsrep_cb_status_t wsrep_rollback(THD* const thd,
- wsrep_seqno_t const global_seqno)
+static wsrep_cb_status_t wsrep_rollback(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
@@ -351,9 +364,9 @@ wsrep_cb_status_t wsrep_commit_cb(void* const ctx,
wsrep_cb_status_t rcode;
if (commit)
- rcode = wsrep_commit(thd, meta->gtid.seqno);
+ rcode = wsrep_commit(thd);
else
- rcode = wsrep_rollback(thd, meta->gtid.seqno);
+ rcode = wsrep_rollback(thd);
wsrep_set_apply_format(thd, NULL);
thd->mdl_context.release_transactional_locks();
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 36917674128..7884df586a1 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -319,9 +319,9 @@ int wsrep_write_cache(wsrep_t* const wsrep,
void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
{
char filename[PATH_MAX]= {0};
- int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
- wsrep_data_home_dir, thd->thread_id,
- (long long)wsrep_thd_trx_seqno(thd));
+ int len= snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld.log",
+ wsrep_data_home_dir, (longlong) thd->thread_id,
+ (longlong) wsrep_thd_trx_seqno(thd));
if (len >= PATH_MAX)
{
WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
@@ -374,9 +374,9 @@ int wsrep_binlog_savepoint_rollback(THD *thd, void *sv)
void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
{
char filename[PATH_MAX]= {0};
- int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
- wsrep_data_home_dir, thd->thread_id,
- (long long)wsrep_thd_trx_seqno(thd));
+ int len= snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld.log",
+ wsrep_data_home_dir, (longlong) thd->thread_id,
+ (longlong) wsrep_thd_trx_seqno(thd));
size_t bytes_in_cache = 0;
// check path
if (len >= PATH_MAX)
@@ -448,8 +448,8 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
Log_event_writer writer(&cache);
Format_description_log_event *ev= wsrep_get_apply_format(thd);
- int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld_v2.log",
- wsrep_data_home_dir, thd->thread_id,
+ int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld_v2.log",
+ wsrep_data_home_dir, (longlong) thd->thread_id,
(long long) wsrep_thd_trx_seqno(thd));
if (len >= PATH_MAX)
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index 603cd8ec5ea..0aa7f9b0aad 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -116,7 +116,7 @@ int wsrep_thd_retry_counter(THD *)
void wsrep_thd_set_conflict_state(THD *, enum wsrep_conflict_state)
{ }
-bool wsrep_thd_skip_append_keys(THD *)
+bool wsrep_thd_ignore_table(THD *)
{ return 0; }
longlong wsrep_thd_trx_seqno(THD *)
diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
index 5bec467708c..fa34a5bbc55 100644
--- a/sql/wsrep_hton.cc
+++ b/sql/wsrep_hton.cc
@@ -22,6 +22,7 @@
#include "wsrep_xid.h"
#include <cstdio>
#include <cstdlib>
+#include "debug_sync.h"
extern ulonglong thd_to_trx_id(THD *thd);
@@ -67,6 +68,17 @@ void wsrep_register_hton(THD* thd, bool all)
if (WSREP(thd) && thd->wsrep_exec_mode != TOTAL_ORDER &&
!thd->wsrep_apply_toi)
{
+ if (thd->wsrep_exec_mode == LOCAL_STATE &&
+ (thd_sql_command(thd) == SQLCOM_OPTIMIZE ||
+ thd_sql_command(thd) == SQLCOM_ANALYZE ||
+ thd_sql_command(thd) == SQLCOM_REPAIR) &&
+ thd->lex->no_write_to_binlog == 1)
+ {
+ WSREP_DEBUG("Skipping wsrep_register_hton for LOCAL sql admin command : %s",
+ thd->query());
+ return;
+ }
+
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
for (Ha_trx_info *i= trans->ha_list; i; i = i->next())
{
@@ -317,6 +329,8 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
}
+ DEBUG_SYNC(thd, "wsrep_before_replication");
+
if (thd->slave_thread && !opt_log_slave_updates) DBUG_RETURN(WSREP_TRX_OK);
if (thd->wsrep_exec_mode == REPL_RECV) {
@@ -383,9 +397,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
&wtime);
if (replay_round++ % 100000 == 0)
- WSREP_DEBUG("commit waiting for replaying: replayers %d, thd: (%lu) "
+ WSREP_DEBUG("commit waiting for replaying: replayers %d, thd: %lld "
"conflict: %d (round: %d)",
- wsrep_replaying, thd->thread_id,
+ wsrep_replaying, (longlong) thd->thread_id,
thd->wsrep_conflict_state, replay_round);
mysql_mutex_unlock(&LOCK_wsrep_replaying);
@@ -449,11 +463,11 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
if (WSREP_UNDEFINED_TRX_ID == thd->wsrep_ws_handle.trx_id)
{
- WSREP_WARN("SQL statement was ineffective, THD: %lu, buf: %zu\n"
+ WSREP_WARN("SQL statement was ineffective thd: %lld buf: %zu\n"
"schema: %s \n"
"QUERY: %s\n"
" => Skipping replication",
- thd->thread_id, data_len,
+ (longlong) thd->thread_id, data_len,
(thd->db ? thd->db : "(null)"), thd->query());
rcode = WSREP_TRX_FAIL;
}
@@ -469,20 +483,22 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
&thd->wsrep_trx_meta);
if (rcode == WSREP_TRX_MISSING) {
- WSREP_WARN("Transaction missing in provider, thd: %ld, schema: %s, SQL: %s",
- thd->thread_id, (thd->db ? thd->db : "(null)"), thd->query());
+ WSREP_WARN("Transaction missing in provider, thd: %lld schema: %s SQL: %s",
+ (longlong) thd->thread_id,
+ (thd->db ? thd->db : "(null)"), thd->query());
rcode = WSREP_TRX_FAIL;
} else if (rcode == WSREP_BF_ABORT) {
- WSREP_DEBUG("thd %lu seqno %lld BF aborted by provider, will replay",
- thd->thread_id, (long long)thd->wsrep_trx_meta.gtid.seqno);
+ WSREP_DEBUG("thd: %lld seqno: %lld BF aborted by provider, will replay",
+ (longlong) thd->thread_id,
+ (longlong) thd->wsrep_trx_meta.gtid.seqno);
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
thd->wsrep_conflict_state = MUST_REPLAY;
DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0);
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying++;
- WSREP_DEBUG("replaying increased: %d, thd: %lu",
- wsrep_replaying, thd->thread_id);
+ WSREP_DEBUG("replaying increased: %d, thd: %lld",
+ wsrep_replaying, (longlong) thd->thread_id);
mysql_mutex_unlock(&LOCK_wsrep_replaying);
}
} else {
@@ -506,9 +522,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all)
if (thd->wsrep_conflict_state != NO_CONFLICT)
{
- WSREP_WARN("thd %lu seqno %lld: conflict state %d after post commit",
- thd->thread_id,
- (long long)thd->wsrep_trx_meta.gtid.seqno,
+ WSREP_WARN("thd: %llu seqno: %lld conflict state %d after post commit",
+ (longlong) thd->thread_id,
+ (longlong) thd->wsrep_trx_meta.gtid.seqno,
thd->wsrep_conflict_state);
}
thd->wsrep_exec_mode= LOCAL_COMMIT;
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index b922d2b2857..f84ebe4dbb9 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -91,12 +91,6 @@ my_bool wsrep_slave_UK_checks = 0; // slave thread does UK checks
my_bool wsrep_slave_FK_checks = 0; // slave thread does FK checks
bool wsrep_new_cluster = false; // Bootstrap the cluster ?
-/*
- Set during the creation of first wsrep applier and rollback threads.
- Since these threads are critical, abort if the thread creation fails.
-*/
-my_bool wsrep_creating_startup_threads = 0;
-
// Use wsrep_gtid_domain_id for galera transactions?
bool wsrep_gtid_mode = 0;
// gtid_domain_id for galera transactions.
@@ -798,7 +792,6 @@ void wsrep_init_startup (bool first)
if (!wsrep_start_replication()) unireg_abort(1);
- wsrep_creating_startup_threads= 1;
wsrep_create_rollbacker();
wsrep_create_appliers(1);
@@ -947,19 +940,24 @@ bool wsrep_start_replication()
return true;
}
+bool wsrep_must_sync_wait (THD* thd, uint mask)
+{
+ return (thd->variables.wsrep_sync_wait & mask) &&
+ thd->variables.wsrep_on &&
+ !thd->in_active_multi_stmt_transaction() &&
+ thd->wsrep_conflict_state != REPLAYING &&
+ thd->wsrep_sync_wait_gtid.seqno == WSREP_SEQNO_UNDEFINED;
+}
+
bool wsrep_sync_wait (THD* thd, uint mask)
{
- if ((thd->variables.wsrep_sync_wait & mask) &&
- thd->variables.wsrep_on &&
- !thd->in_active_multi_stmt_transaction() &&
- thd->wsrep_conflict_state != REPLAYING)
+ if (wsrep_must_sync_wait(thd, mask))
{
WSREP_DEBUG("wsrep_sync_wait: thd->variables.wsrep_sync_wait = %u, mask = %u",
thd->variables.wsrep_sync_wait, mask);
// This allows autocommit SELECTs and a first SELECT after SET AUTOCOMMIT=0
// TODO: modify to check if thd has locked any rows.
- wsrep_gtid_t gtid;
- wsrep_status_t ret= wsrep->causal_read (wsrep, &gtid);
+ wsrep_status_t ret= wsrep->causal_read (wsrep, &thd->wsrep_sync_wait_gtid);
if (unlikely(WSREP_OK != ret))
{
@@ -1467,16 +1465,19 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
thd->wsrep_exec_mode, thd->query() );
- ret = wsrep->desync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
- ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- return(ret);
+ ret = wsrep->desync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return(ret);
+ }
}
+ else
+ WSREP_DEBUG("RSU desync skipped: %d", wsrep_desync);
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying++;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
@@ -1491,13 +1492,14 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
wsrep_replaying--;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("resync failed %d for schema: %s, query: %s",
- ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("resync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
+ }
}
my_error(ER_LOCK_DEADLOCK, MYF(0));
return(1);
@@ -1534,14 +1536,18 @@ static void wsrep_RSU_end(THD *thd)
(thd->db ? thd->db : "(null)"),
thd->query());
}
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
- (thd->db ? thd->db : "(null)"),
- thd->query());
- return;
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"), thd->query());
+ return;
+ }
}
+ else
+ WSREP_DEBUG("RSU resync skipped: %d", wsrep_desync);
thd->variables.wsrep_on = 1;
}
@@ -1560,8 +1566,8 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
if (thd->wsrep_conflict_state == MUST_ABORT)
{
- WSREP_INFO("thread: %lu, schema: %s, query: %s has been aborted due to multi-master conflict",
- thd->thread_id,
+ WSREP_INFO("thread: %lld schema: %s query: %s has been aborted due to multi-master conflict",
+ (longlong) thd->thread_id,
(thd->db ? thd->db : "(null)"),
thd->query());
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
@@ -1574,15 +1580,15 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
if (thd->global_read_lock.can_acquire_protection())
{
- WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %lu",
- thd->query(), thd->thread_id);
+ WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %lld",
+ thd->query(), (longlong) thd->thread_id);
return -1;
}
if (wsrep_debug && thd->mdl_context.has_locks())
{
- WSREP_DEBUG("thread holds MDL locks at TI begin: %s %lu",
- thd->query(), thd->thread_id);
+ WSREP_DEBUG("thread holds MDL locks at TI begin: %s %lld",
+ thd->query(), (longlong) thd->thread_id);
}
/*
@@ -1645,13 +1651,13 @@ void wsrep_to_isolation_end(THD *thd)
WSREP_##severity( \
"%s\n" \
"schema: %.*s\n" \
- "request: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \
- "granted: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \
+ "request: (%lld \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \
+ "granted: (%lld \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \
msg, schema_len, schema, \
- req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
+ (longlong) req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
req->wsrep_exec_mode, req->wsrep_query_state, req->wsrep_conflict_state, \
req->get_command(), req->lex->sql_command, req->query(), \
- gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
+ (longlong) gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
gra->wsrep_exec_mode, gra->wsrep_query_state, gra->wsrep_conflict_state, \
gra->get_command(), gra->lex->sql_command, gra->query());
@@ -1751,8 +1757,9 @@ pthread_handler_t start_wsrep_THD(void *arg)
goto error;
}
+ thd->thread_id= next_thread_id();
+
mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id=thread_id++;
if (wsrep_gtid_mode)
{
@@ -1820,21 +1827,11 @@ pthread_handler_t start_wsrep_THD(void *arg)
//thd->version= refresh_version;
thd->proc_info= 0;
thd->set_command(COM_SLEEP);
-
- if (wsrep_creating_startup_threads == 0)
- {
- thd->init_for_queries();
- }
+ thd->init_for_queries();
mysql_mutex_lock(&LOCK_thread_count);
wsrep_running_threads++;
mysql_cond_broadcast(&COND_thread_count);
-
- if (wsrep_running_threads > 2)
- {
- wsrep_creating_startup_threads= 0;
- }
-
mysql_mutex_unlock(&LOCK_thread_count);
processor(thd);
@@ -1863,21 +1860,22 @@ pthread_handler_t start_wsrep_THD(void *arg)
// at server shutdown
}
- my_thread_end();
if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION)
{
mysql_mutex_lock(&LOCK_thread_count);
- delete thd;
- thread_count--;
+ thd->unlink();
mysql_mutex_unlock(&LOCK_thread_count);
+ delete thd;
+ dec_thread_count();
}
+ my_thread_end();
return(NULL);
error:
WSREP_ERROR("Failed to create/initialize system thread");
/* Abort if its the first applier/rollbacker thread. */
- if (wsrep_creating_startup_threads == 1)
+ if (!mysqld_server_initialized)
unireg_abort(1);
else
return NULL;
@@ -1937,8 +1935,8 @@ static bool have_client_connections()
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
- DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- tmp->thread_id));
+ DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
+ (longlong) tmp->thread_id));
if (is_client_connection(tmp) && tmp->killed == KILL_CONNECTION)
{
(void)abort_replicated(tmp);
@@ -2022,8 +2020,8 @@ void wsrep_close_client_connections(my_bool wait_to_end)
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
- DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- tmp->thread_id));
+ DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
+ (longlong) tmp->thread_id));
/* We skip slave threads & scheduler on this first loop through. */
if (!is_client_connection(tmp))
continue;
@@ -2038,7 +2036,7 @@ void wsrep_close_client_connections(my_bool wait_to_end)
if (abort_replicated(tmp))
continue;
- WSREP_DEBUG("closing connection %ld", tmp->thread_id);
+ WSREP_DEBUG("closing connection %lld", (longlong) tmp->thread_id);
wsrep_close_thread(tmp);
}
mysql_mutex_unlock(&LOCK_thread_count);
@@ -2059,7 +2057,7 @@ void wsrep_close_client_connections(my_bool wait_to_end)
!abort_replicated(tmp) &&
!is_replaying_connection(tmp))
{
- WSREP_INFO("killing local connection: %ld",tmp->thread_id);
+ WSREP_INFO("killing local connection: %lld", (longlong) tmp->thread_id);
close_connection(tmp,0);
}
#endif
@@ -2084,7 +2082,7 @@ void wsrep_close_client_connections(my_bool wait_to_end)
void wsrep_close_applier(THD *thd)
{
- WSREP_DEBUG("closing applier %ld", thd->thread_id);
+ WSREP_DEBUG("closing applier %lld", (longlong) thd->thread_id);
wsrep_close_thread(thd);
}
@@ -2097,12 +2095,12 @@ void wsrep_close_threads(THD *thd)
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
- DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- tmp->thread_id));
+ DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
+ (longlong) tmp->thread_id));
/* We skip slave threads & scheduler on this first loop through. */
if (tmp->wsrep_applier && tmp != thd)
{
- WSREP_DEBUG("closing wsrep thread %ld", tmp->thread_id);
+ WSREP_DEBUG("closing wsrep thread %lld", (longlong) tmp->thread_id);
wsrep_close_thread (tmp);
}
}
@@ -2198,9 +2196,9 @@ int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
&(thd->lex->definer->host),
saved_mode))
{
- WSREP_WARN("SP create string failed: schema: %s, query: %s",
- (thd->db ? thd->db : "(null)"), thd->query());
- return 1;
+ WSREP_WARN("SP create string failed: schema: %s, query: %s",
+ (thd->db ? thd->db : "(null)"), thd->query());
+ return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
@@ -2381,9 +2379,9 @@ int wsrep_thd_retry_counter(THD *thd)
}
-extern "C" bool wsrep_thd_skip_append_keys(THD *thd)
+extern "C" bool wsrep_thd_ignore_table(THD *thd)
{
- return thd->wsrep_skip_append_keys;
+ return thd->wsrep_ignore_table;
}
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 26d3484b3b4..e91ed2302a2 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -86,7 +86,6 @@ extern my_bool wsrep_slave_FK_checks;
extern my_bool wsrep_slave_UK_checks;
extern ulong wsrep_running_threads;
extern bool wsrep_new_cluster;
-extern my_bool wsrep_creating_startup_threads;
extern bool wsrep_gtid_mode;
extern uint32 wsrep_gtid_domain_id;
@@ -161,6 +160,7 @@ extern void wsrep_kill_mysql(THD *thd);
/* new defines */
extern void wsrep_stop_replication(THD *thd);
extern bool wsrep_start_replication();
+extern bool wsrep_must_sync_wait(THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
extern bool wsrep_sync_wait(THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
extern int wsrep_check_opts();
extern void wsrep_prepend_PATH (const char* path);
@@ -328,6 +328,7 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
#define wsrep_prepend_PATH(X)
#define wsrep_before_SE() (0)
#define wsrep_init_startup(X)
+#define wsrep_must_sync_wait(...) (0)
#define wsrep_sync_wait(...) (0)
#define wsrep_to_isolation_begin(...) (0)
#define wsrep_register_hton(...) do { } while(0)
@@ -341,7 +342,6 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
#define wsrep_thr_init() do {} while(0)
#define wsrep_thr_deinit() do {} while(0)
#define wsrep_running_threads (0)
-#define wsrep_creating_startup_threads (0)
#endif /* WITH_WSREP */
#endif /* WSREP_MYSQLD_H */
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index c75f2c116ec..6d04527cbcb 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -875,7 +875,7 @@ static int sst_donate_mysqldump (const char* addr,
host, port, mysqld_port, mysqld_unix_port,
wsrep_defaults_file, uuid_str,
(long long)seqno, wsrep_gtid_domain_id,
- bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ bypass ? " " WSREP_SST_OPT_BYPASS : "");
if (ret < 0 || ret >= cmd_len)
{
@@ -896,6 +896,56 @@ static int sst_donate_mysqldump (const char* addr,
wsrep_seqno_t wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
+
+/*
+ Create a file under data directory.
+*/
+static int sst_create_file(const char *name, const char *content)
+{
+ int err= 0;
+ char *real_name;
+ char *tmp_name;
+ ssize_t len;
+ FILE *file;
+
+ len= strlen(mysql_real_data_home) + strlen(name) + 2;
+ real_name= (char *) alloca(len);
+
+ snprintf(real_name, (size_t) len, "%s/%s", mysql_real_data_home, name);
+
+ tmp_name= (char *) alloca(len + 4);
+ snprintf(tmp_name, (size_t) len + 4, "%s.tmp", real_name);
+
+ file= fopen(tmp_name, "w+");
+
+ if (0 == file)
+ {
+ err= errno;
+ WSREP_ERROR("Failed to open '%s': %d (%s)", tmp_name, err, strerror(err));
+ }
+ else
+ {
+ // Write the specified content into the file.
+ if (content != NULL)
+ {
+ fprintf(file, "%s\n", content);
+ fsync(fileno(file));
+ }
+
+ fclose(file);
+
+ if (rename(tmp_name, real_name) == -1)
+ {
+ err= errno;
+ WSREP_ERROR("Failed to rename '%s' to '%s': %d (%s)", tmp_name,
+ real_name, err, strerror(err));
+ }
+ }
+
+ return err;
+}
+
+
static int run_sql_command(THD *thd, const char *query)
{
thd->set_query((char *)query, strlen(query));
@@ -907,11 +957,11 @@ static int run_sql_command(THD *thd, const char *query)
return -1;
}
- mysql_parse(thd, thd->query(), thd->query_length(), &ps);
+ mysql_parse(thd, thd->query(), thd->query_length(), &ps, FALSE);
if (thd->is_error())
{
int const err= thd->get_stmt_da()->sql_errno();
- WSREP_WARN ("error executing '%s': %d (%s)%s",
+ WSREP_WARN ("Error executing '%s': %d (%s)%s",
query, err, thd->get_stmt_da()->message(),
err == ER_UNKNOWN_SYSTEM_VARIABLE ?
". Was mysqld built with --with-innodb-disallow-writes ?" : "");
@@ -921,15 +971,21 @@ static int run_sql_command(THD *thd, const char *query)
return 0;
}
+
static int sst_flush_tables(THD* thd)
{
WSREP_INFO("Flushing tables for SST...");
- int err;
+ int err= 0;
int not_used;
- CHARSET_INFO *current_charset;
+ /*
+ Files created to notify the SST script about the outcome of table flush
+ operation.
+ */
+ const char *flush_success= "tables_flushed";
+ const char *flush_error= "sst_error";
- current_charset = thd->variables.character_set_client;
+ CHARSET_INFO *current_charset= thd->variables.character_set_client;
if (!is_supported_parser_charset(current_charset))
{
@@ -942,61 +998,55 @@ static int sst_flush_tables(THD* thd)
if (run_sql_command(thd, "FLUSH TABLES WITH READ LOCK"))
{
- WSREP_ERROR("Failed to flush and lock tables");
- err = -1;
+ err= -1;
}
else
{
- /* make sure logs are flushed after global read lock acquired */
- err= reload_acl_and_cache(thd, REFRESH_ENGINE_LOG | REFRESH_BINARY_LOG,
- (TABLE_LIST*) 0, &not_used);
+ /*
+ Make sure logs are flushed after global read lock acquired. In case
+ reload fails, we must also release the acquired FTWRL.
+ */
+ if (reload_acl_and_cache(thd, REFRESH_ENGINE_LOG | REFRESH_BINARY_LOG,
+ (TABLE_LIST*) 0, &not_used))
+ {
+ thd->global_read_lock.unlock_global_read_lock(thd);
+ err= -1;
+ }
}
thd->variables.character_set_client = current_charset;
-
if (err)
{
- WSREP_ERROR("Failed to flush tables: %d (%s)", err, strerror(err));
+ WSREP_ERROR("Failed to flush and lock tables");
+
+ /*
+ The SST must be aborted as the flush tables failed. Notify this to SST
+ script by creating the error file.
+ */
+ int tmp;
+ if ((tmp= sst_create_file(flush_error, NULL))) {
+ err= tmp;
+ }
}
else
{
WSREP_INFO("Tables flushed.");
- const char base_name[]= "tables_flushed";
-
- ssize_t const full_len= strlen(mysql_real_data_home) + strlen(base_name)+2;
- char *real_name= (char *) alloca(full_len);
- snprintf(real_name, (size_t) full_len, "%s/%s", mysql_real_data_home,
- base_name);
- char *tmp_name= (char *) alloca(full_len + 4);
- snprintf(tmp_name, (size_t) full_len + 4, "%s.tmp", real_name);
- FILE* file= fopen(tmp_name, "w+");
- if (0 == file)
- {
- err= errno;
- WSREP_ERROR("Failed to open '%s': %d (%s)", tmp_name, err,strerror(err));
- }
- else
- {
- // Write cluster state ID and wsrep_gtid_domain_id.
- fprintf(file, "%s:%lld %d\n",
- wsrep_cluster_state_uuid, (long long)wsrep_locked_seqno,
- wsrep_gtid_domain_id);
- fsync(fileno(file));
- fclose(file);
- if (rename(tmp_name, real_name) == -1)
- {
- err= errno;
- WSREP_ERROR("Failed to rename '%s' to '%s': %d (%s)",
- tmp_name, real_name, err,strerror(err));
- }
- }
+ /*
+ Tables have been flushed. Create a file with cluster state ID and
+ wsrep_gtid_domain_id.
+ */
+ char content[100];
+ snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid,
+ (long long)wsrep_locked_seqno, wsrep_gtid_domain_id);
+ err= sst_create_file(flush_success, content);
}
return err;
}
+
static void sst_disallow_writes (THD* thd, bool yes)
{
char query_str[64] = { 0, };
@@ -1170,7 +1220,7 @@ static int sst_donate_other (const char* method,
wsrep_defaults_file,
binlog_opt, binlog_opt_val,
uuid, (long long) seqno, wsrep_gtid_domain_id,
- bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ bypass ? " " WSREP_SST_OPT_BYPASS : "");
my_free(binlog_opt_val);
if (ret < 0 || ret >= cmd_len)
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index ab09a9e3a99..3835c925745 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -50,8 +50,8 @@ int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
/* must have (&thd->LOCK_wsrep_thd) */
void wsrep_client_rollback(THD *thd)
{
- WSREP_DEBUG("client rollback due to BF abort for (%ld), query: %s",
- thd->thread_id, thd->query());
+ WSREP_DEBUG("client rollback due to BF abort for (%lld), query: %s",
+ (longlong) thd->thread_id, thd->query());
WSREP_ATOMIC_ADD_LONG(&wsrep_bf_aborts_counter, 1);
@@ -61,14 +61,16 @@ void wsrep_client_rollback(THD *thd)
if (thd->locked_tables_mode && thd->lock)
{
- WSREP_DEBUG("unlocking tables for BF abort (%ld)", thd->thread_id);
+ WSREP_DEBUG("unlocking tables for BF abort (%lld)",
+ (longlong) thd->thread_id);
thd->locked_tables_list.unlock_locked_tables(thd);
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock.is_acquired())
{
- WSREP_DEBUG("unlocking GRL for BF abort (%ld)", thd->thread_id);
+ WSREP_DEBUG("unlocking GRL for BF abort (%lld)",
+ (longlong) thd->thread_id);
thd->global_read_lock.unlock_global_read_lock(thd);
}
@@ -80,7 +82,8 @@ void wsrep_client_rollback(THD *thd)
if (thd->get_binlog_table_maps())
{
- WSREP_DEBUG("clearing binlog table map for BF abort (%ld)", thd->thread_id);
+ WSREP_DEBUG("clearing binlog table map for BF abort (%lld)",
+ (longlong) thd->thread_id);
thd->clear_binlog_table_maps();
}
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
@@ -202,8 +205,8 @@ void wsrep_replay_transaction(THD *thd)
close_thread_tables(thd);
if (thd->locked_tables_mode && thd->lock)
{
- WSREP_DEBUG("releasing table lock for replaying (%ld)",
- thd->thread_id);
+ WSREP_DEBUG("releasing table lock for replaying (%lld)",
+ (longlong) thd->thread_id);
thd->locked_tables_list.unlock_locked_tables(thd);
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
@@ -242,8 +245,8 @@ void wsrep_replay_transaction(THD *thd)
case WSREP_OK:
thd->wsrep_conflict_state= NO_CONFLICT;
wsrep->post_commit(wsrep, &thd->wsrep_ws_handle);
- WSREP_DEBUG("trx_replay successful for: %ld %llu",
- thd->thread_id, (long long)thd->real_id);
+ WSREP_DEBUG("trx_replay successful for: %lld %lld",
+ (longlong) thd->thread_id, (longlong) thd->real_id);
if (thd->get_stmt_da()->is_sent())
{
WSREP_WARN("replay ok, thd has reported status");
@@ -292,8 +295,8 @@ void wsrep_replay_transaction(THD *thd)
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying--;
- WSREP_DEBUG("replaying decreased: %d, thd: %lu",
- wsrep_replaying, thd->thread_id);
+ WSREP_DEBUG("replaying decreased: %d, thd: %lld",
+ wsrep_replaying, (longlong) thd->thread_id);
mysql_cond_broadcast(&COND_wsrep_replaying);
mysql_mutex_unlock(&LOCK_wsrep_replaying);
}
@@ -360,15 +363,34 @@ static void wsrep_replication_process(THD *thd)
TABLE *tmp;
while ((tmp = thd->temporary_tables))
{
- WSREP_WARN("Applier %lu, has temporary tables at exit: %s.%s",
- thd->thread_id,
- (tmp->s) ? tmp->s->db.str : "void",
- (tmp->s) ? tmp->s->table_name.str : "void");
+ WSREP_WARN("Applier %lld, has temporary tables at exit: %s.%s",
+ (longlong) thd->thread_id,
+ (tmp->s) ? tmp->s->db.str : "void",
+ (tmp->s) ? tmp->s->table_name.str : "void");
}
wsrep_return_from_bf_mode(thd, &shadow);
DBUG_VOID_RETURN;
}
+static bool create_wsrep_THD(wsrep_thd_processor_fun processor)
+{
+ ulong old_wsrep_running_threads= wsrep_running_threads;
+ pthread_t unused;
+ mysql_mutex_lock(&LOCK_thread_count);
+ bool res= pthread_create(&unused, &connection_attrib, start_wsrep_THD,
+ (void*)processor);
+ /*
+ if starting a thread on server startup, wait until the this thread's THD
+ is fully initialized (otherwise a THD initialization code might
+ try to access a partially initialized server data structure - MDEV-8208).
+ */
+ if (!mysqld_server_initialized)
+ while (old_wsrep_running_threads == wsrep_running_threads)
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ return res;
+}
+
void wsrep_create_appliers(long threads)
{
if (!wsrep_connected)
@@ -385,11 +407,8 @@ void wsrep_create_appliers(long threads)
}
long wsrep_threads=0;
- pthread_t hThread;
while (wsrep_threads++ < threads) {
- if (pthread_create(
- &hThread, &connection_attrib,
- start_wsrep_THD, (void*)wsrep_replication_process))
+ if (create_wsrep_THD(wsrep_replication_process))
WSREP_WARN("Can't create thread to manage wsrep replication");
}
}
@@ -454,8 +473,9 @@ static void wsrep_rollback_process(THD *thd)
mysql_mutex_lock(&aborting->LOCK_wsrep_thd);
wsrep_client_rollback(aborting);
- WSREP_DEBUG("WSREP rollbacker aborted thd: (%lu %llu)",
- aborting->thread_id, (long long)aborting->real_id);
+ WSREP_DEBUG("WSREP rollbacker aborted thd: (%lld %lld)",
+ (longlong) aborting->thread_id,
+ (longlong) aborting->real_id);
mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
set_current_thd(thd);
@@ -476,10 +496,8 @@ void wsrep_create_rollbacker()
{
if (wsrep_provider && strcasecmp(wsrep_provider, "none"))
{
- pthread_t hThread;
/* create rollbacker */
- if (pthread_create( &hThread, &connection_attrib,
- start_wsrep_THD, (void*)wsrep_rollback_process))
+ if (create_wsrep_THD(wsrep_rollback_process))
WSREP_WARN("Can't create thread to manage wsrep rollback");
}
}