summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt7
-rw-r--r--sql/filesort.cc9
-rw-r--r--sql/filesort.h13
-rw-r--r--sql/handle_connections_win.cc19
-rw-r--r--sql/handler.h2
-rw-r--r--sql/log_event_server.cc3
-rw-r--r--sql/mysql_install_db.cc419
-rw-r--r--sql/mysqld.cc479
-rw-r--r--sql/mysqld.h11
-rw-r--r--sql/nt_servc.cc555
-rw-r--r--sql/nt_servc.h113
-rw-r--r--sql/opt_subselect.cc17
-rw-r--r--sql/protocol.cc293
-rw-r--r--sql/protocol.h6
-rw-r--r--sql/records.cc29
-rw-r--r--sql/records.h1
-rw-r--r--sql/share/errmsg-utf8.txt15
-rw-r--r--sql/slave.cc3
-rw-r--r--sql/sql_class.cc11
-rw-r--r--sql/sql_class.h48
-rw-r--r--sql/sql_db.cc176
-rw-r--r--sql/sql_delete.cc10
-rw-r--r--sql/sql_error.cc1
-rw-r--r--sql/sql_error.h11
-rw-r--r--sql/sql_parse.cc221
-rw-r--r--sql/sql_parse.h6
-rw-r--r--sql/sql_prepare.cc25
-rw-r--r--sql/sql_select.cc113
-rw-r--r--sql/sql_select.h4
-rw-r--r--sql/sql_sort.h3
-rw-r--r--sql/sql_yacc.yy10
-rw-r--r--sql/table.h3
-rw-r--r--sql/threadpool.h5
-rw-r--r--sql/threadpool_common.cc19
-rw-r--r--sql/threadpool_generic.cc72
-rw-r--r--sql/threadpool_generic.h19
-rw-r--r--sql/threadpool_win.cc192
-rw-r--r--sql/threadpool_winsockets.cc259
-rw-r--r--sql/threadpool_winsockets.h80
-rw-r--r--sql/upgrade_conf_file.cc22
-rw-r--r--sql/winmain.cc371
-rw-r--r--sql/winservice.c23
-rw-r--r--sql/wsrep_schema.cc2
-rw-r--r--sql/wsrep_sst.cc2
-rw-r--r--sql/wsrep_var.cc19
45 files changed, 2030 insertions, 1691 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index b9cd418f295..39307ab8183 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -146,7 +146,6 @@ SET (SQL_SOURCE
opt_index_cond_pushdown.cc opt_subselect.cc
opt_table_elimination.cc sql_expression_cache.cc
gcalc_slicescan.cc gcalc_tools.cc
- ../sql-common/mysql_async.c
my_apc.cc mf_iocache_encr.cc item_jsonfunc.cc
my_json_writer.cc
rpl_gtid.cc rpl_parallel.cc
@@ -179,7 +178,7 @@ IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR
AND (NOT DISABLE_THREADPOOL))
ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS)
IF(WIN32)
- SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc)
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc threadpool_winsockets.cc threadpool_winsockets.h)
ENDIF()
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_generic.cc)
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_common.cc)
@@ -187,7 +186,7 @@ IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR
ENDIF()
IF(WIN32)
- SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc nt_servc.cc)
+ SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc)
ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
@@ -217,7 +216,7 @@ FOREACH(se aria partition perfschema sql_sequence wsrep)
ENDFOREACH()
IF(WIN32)
- SET(MYSQLD_SOURCE main.cc message.rc)
+ SET(MYSQLD_SOURCE winmain.cc message.rc)
ELSE()
SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL})
ENDIF()
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 4eea588007e..0337325b544 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -251,6 +251,9 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
param.init_for_filesort(sort_len, table, max_rows, filesort->sort_positions);
+ param.set_all_read_bits= filesort->set_all_read_bits;
+ param.unpack= filesort->unpack;
+
sort->addon_fields= param.addon_fields;
sort->sort_keys= param.sort_keys;
@@ -884,6 +887,8 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
goto err;
}
+ if (param->set_all_read_bits)
+ sort_form->column_bitmaps_set(save_read_set, save_write_set);
DEBUG_SYNC(thd, "after_index_merge_phase1");
for (;;)
@@ -891,7 +896,11 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
if (quick_select)
error= select->quick->get_next();
else /* Not quick-select */
+ {
error= file->ha_rnd_next(sort_form->record[0]);
+ if (param->unpack)
+ param->unpack(sort_form);
+ }
if (unlikely(error))
break;
file->position(sort_form->record[0]);
diff --git a/sql/filesort.h b/sql/filesort.h
index 9f71da02c96..29ae5e20cc6 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -62,6 +62,13 @@ public:
Filesort_tracker *tracker;
Sort_keys *sort_keys;
+ /*
+ TRUE means all the fields of table of whose bitmap read_set is set
+ need to be read while reading records in the sort buffer.
+ FALSE otherwise
+ */
+ bool set_all_read_bits;
+
Filesort(ORDER *order_arg, ha_rows limit_arg, bool sort_positions_arg,
SQL_SELECT *select_arg):
order(order_arg),
@@ -71,7 +78,9 @@ public:
own_select(false),
using_pq(false),
sort_positions(sort_positions_arg),
- sort_keys(NULL)
+ sort_keys(NULL),
+ set_all_read_bits(FALSE),
+ unpack(NULL)
{
DBUG_ASSERT(order);
};
@@ -79,6 +88,8 @@ public:
~Filesort() { cleanup(); }
/* Prepare ORDER BY list for sorting. */
Sort_keys* make_sortorder(THD *thd, JOIN *join, table_map first_table_bit);
+ /* Unpack temp table columns to base table columns*/
+ void (*unpack)(TABLE *);
private:
void cleanup();
diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc
index b61130dd6e9..debbce998fa 100644
--- a/sql/handle_connections_win.cc
+++ b/sql/handle_connections_win.cc
@@ -26,7 +26,6 @@
#include <handle_connections_win.h>
/* From mysqld.cc */
-extern HANDLE hEventShutdown;
extern MYSQL_SOCKET base_ip_sock, extra_ip_sock;
#ifdef HAVE_POOL_OF_THREADS
extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ();
@@ -510,6 +509,18 @@ struct Pipe_Listener : public Listener
}
};
+ /* The shutdown event, which is set whenever*/
+static void create_shutdown_event()
+{
+ char shutdown_event_name[40];
+ sprintf_s(shutdown_event_name, "MySQLShutdown%u", GetCurrentProcessId());
+ if (!(hEventShutdown= CreateEvent(0, FALSE, FALSE, shutdown_event_name)))
+ {
+ sql_print_error("Can't create shutdown event, Windows error %u", GetLastError());
+ unireg_abort(1);
+ }
+}
+
/**
Accept new client connections on Windows.
@@ -572,9 +583,9 @@ void network_init_win()
void handle_connections_win()
{
- DBUG_ASSERT(hEventShutdown);
int n_waits;
+ create_shutdown_event();
wait_events[SHUTDOWN_IDX]= hEventShutdown;
n_waits= 1;
@@ -589,6 +600,8 @@ void handle_connections_win()
all_listeners[i]->begin_accept();
}
+ mysqld_win_set_startup_complete();
+
for (;;)
{
DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE);
@@ -600,6 +613,8 @@ void handle_connections_win()
all_listeners[idx - LISTENER_START_IDX]->completion_callback();
}
+ mysqld_win_initiate_shutdown();
+
/* Cleanup */
for (int i= 0; i < n_listeners; i++)
{
diff --git a/sql/handler.h b/sql/handler.h
index 4e1e3f0413f..66ff4db8050 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -535,7 +535,7 @@ enum legacy_db_type
DB_TYPE_PERFORMANCE_SCHEMA=28,
DB_TYPE_S3=41,
DB_TYPE_ARIA=42,
- DB_TYPE_TOKUDB=43,
+ DB_TYPE_TOKUDB=43, /* disabled in MariaDB Server 10.5, removed in 10.6 */
DB_TYPE_SEQUENCE=44,
DB_TYPE_FIRST_DYNAMIC=45,
DB_TYPE_DEFAULT=127 // Must be last
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index aa3e48ee873..cd35c6d6d23 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -1903,8 +1903,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE);
}
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
- FALSE, FALSE);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
log_slow_statement(thd);
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 651eea33304..04f3789bad4 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -26,9 +26,13 @@
#include <shellapi.h>
#include <accctrl.h>
#include <aclapi.h>
+#include <ntsecapi.h>
+#include <sddl.h>
struct IUnknown;
#include <shlwapi.h>
+#include <string>
+
#define USAGETEXT \
"mysql_install_db.exe Ver 1.00 for Windows\n" \
"Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub\n" \
@@ -39,9 +43,8 @@ struct IUnknown;
extern "C" const char* mysql_bootstrap_sql[];
-static char default_os_user[]= "NT AUTHORITY\\NetworkService";
static char default_datadir[MAX_PATH];
-static int create_db_instance();
+static int create_db_instance(const char *datadir);
static uint opt_silent;
static char datadir_buffer[FN_REFLEN];
static char mysqld_path[FN_REFLEN];
@@ -51,13 +54,13 @@ static char *opt_password;
static int opt_port;
static int opt_innodb_page_size;
static char *opt_socket;
-static char *opt_os_user;
-static char *opt_os_password;
static my_bool opt_default_user;
static my_bool opt_allow_remote_root_access;
static my_bool opt_skip_networking;
static my_bool opt_verbose_bootstrap;
static my_bool verbose_errors;
+static my_bool opt_large_pages;
+static char *opt_config;
#define DEFAULT_INNODB_PAGE_SIZE 16*1024
@@ -73,14 +76,14 @@ static struct my_option my_long_options[]=
&opt_password, &opt_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "mysql port",
&opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"socket", 'W',
+ {"socket", 'W',
"named pipe name (if missing, it will be set the same as service)",
&opt_socket, &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"default-user", 'D', "Create default user",
&opt_default_user, &opt_default_user, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"allow-remote-root-access", 'R',
+ {"allow-remote-root-access", 'R',
"Allows remote access from network for user root",
- &opt_allow_remote_root_access, &opt_allow_remote_root_access, 0 , GET_BOOL,
+ &opt_allow_remote_root_access, &opt_allow_remote_root_access, 0 , GET_BOOL,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"skip-networking", 'N', "Do not use TCP connections, use pipe instead",
&opt_skip_networking, &opt_skip_networking, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0,
@@ -91,6 +94,10 @@ static struct my_option my_long_options[]=
&opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"verbose-bootstrap", 'o', "Include mysqld bootstrap output",&opt_verbose_bootstrap,
&opt_verbose_bootstrap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ { "large-pages",'l', "Use large pages", &opt_large_pages,
+ &opt_large_pages, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"config",'c', "my.ini config template file", &opt_config,
+ &opt_config, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -135,7 +142,7 @@ ATTRIBUTE_NORETURN static void die(const char *fmt, ...)
}
-static void verbose(const char *fmt, ...)
+static void verbose( const char *fmt, ...)
{
va_list args;
@@ -150,15 +157,16 @@ static void verbose(const char *fmt, ...)
va_end(args);
}
+static char full_config_path[MAX_PATH];
int main(int argc, char **argv)
{
int error;
- char self_name[FN_REFLEN];
+ char self_name[MAX_PATH];
char *p;
-
+ char *datadir = NULL;
MY_INIT(argv[0]);
- GetModuleFileName(NULL, self_name, FN_REFLEN);
+ GetModuleFileName(NULL, self_name, MAX_PATH);
strcpy(mysqld_path,self_name);
p= strrchr(mysqld_path, FN_LIBCHAR);
if (p)
@@ -168,7 +176,56 @@ int main(int argc, char **argv)
if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
exit(error);
- if (!opt_datadir)
+
+ if (opt_config != 0 && _access(opt_config, 04) != 0)
+ {
+ int err= errno;
+ switch(err)
+ {
+ case EACCES:
+ die("File %s can't be read", opt_config);
+ break;
+ case ENOENT:
+ die("File %s does not exist", opt_config);
+ break;
+ default:
+ die("Can't access file %s, errno %d",opt_config, err);
+ break;
+ }
+ }
+ if (opt_config)
+ {
+ DWORD dwret = GetFullPathName(opt_config, sizeof(full_config_path), full_config_path, NULL);
+ if (dwret == 0)
+ {
+ die("GetFullPathName failed, last error %u", GetLastError());
+ }
+ else if (dwret > sizeof(full_config_path))
+ {
+ die("Can't resolve the config file name, path too large");
+ }
+ opt_config= full_config_path;
+ }
+
+ if(opt_datadir)
+ datadir = opt_datadir;
+
+ if (!datadir && opt_config)
+ {
+ for(auto section : {"server","mysqld"})
+ {
+ auto ret = GetPrivateProfileStringA(section,"datadir", NULL, default_datadir,
+ sizeof(default_datadir)-1, opt_config);
+ if (ret)
+ {
+ datadir= default_datadir;
+ printf("Data directory (from config file) is %s\n",datadir);
+ break;
+ }
+ }
+ }
+
+ if (!datadir)
{
/*
Figure out default data directory. It "data" directory, next to "bin" directory, where
@@ -189,31 +246,32 @@ int main(int argc, char **argv)
my_print_help(my_long_options);
}
strcat_s(default_datadir, "\\data");
- opt_datadir= default_datadir;
- printf("Default data directory is %s\n",opt_datadir);
+ datadir= default_datadir;
+ printf("Default data directory is %s\n",datadir);
}
+ DBUG_ASSERT(datadir);
+
/* Print some help on errors */
verbose_errors= TRUE;
- if (!opt_os_user)
- {
- opt_os_user= default_os_user;
- opt_os_password= NULL;
- }
/* Workaround WiX bug (strip possible quote character at the end of path) */
- size_t len= strlen(opt_datadir);
+ size_t len= strlen(datadir);
if (len > 0)
{
- if (opt_datadir[len-1] == '"')
+ if (datadir[len-1] == '"')
+ {
+ datadir[len-1]= 0;
+ }
+ if (datadir[0] == '"')
{
- opt_datadir[len-1]= 0;
+ datadir++;
}
}
- GetFullPathName(opt_datadir, FN_REFLEN, datadir_buffer, NULL);
- opt_datadir= datadir_buffer;
+ GetFullPathName(datadir, FN_REFLEN, datadir_buffer, NULL);
+ datadir= datadir_buffer;
- if (create_db_instance())
+ if (create_db_instance(datadir))
{
die("database creation failed");
}
@@ -279,19 +337,37 @@ static char *get_plugindir()
static char *init_bootstrap_command_line(char *cmdline, size_t size)
{
- char basedir[MAX_PATH];
- get_basedir(basedir, sizeof(basedir), mysqld_path);
-
- my_snprintf(cmdline, size - 1,
- "\"\"%s\" --no-defaults %s --innodb-page-size=%d --bootstrap"
- " \"--lc-messages-dir=%s/share\""
- " --basedir=. --datadir=. --default-storage-engine=myisam"
- " --max_allowed_packet=9M "
- " --net-buffer-length=16k\"", mysqld_path,
- opt_verbose_bootstrap ? "--console" : "", opt_innodb_page_size, basedir);
+ snprintf(cmdline, size - 1,
+ "\"\"%s\""
+ " --defaults-file=my.ini"
+ " %s"
+ " --bootstrap"
+ " --datadir=."
+ " --loose-innodb-buffer-pool-size=10M"
+ "\""
+ , mysqld_path, opt_verbose_bootstrap ? "--console" : "");
return cmdline;
}
+static char my_ini_path[MAX_PATH];
+
+static void write_myini_str(const char *key, const char* val, const char *section="mysqld")
+{
+ DBUG_ASSERT(my_ini_path[0]);
+ if (!WritePrivateProfileString(section, key, val, my_ini_path))
+ {
+ die("Can't write to ini file key=%s, val=%s, section=%s, Windows error %u",key,val,section,
+ GetLastError());
+ }
+}
+
+
+static void write_myini_int(const char* key, int val, const char* section = "mysqld")
+{
+ char buf[10];
+ itoa(val, buf, 10);
+ write_myini_str(key, buf, section);
+}
/**
Create my.ini in current directory (this is assumed to be
@@ -305,59 +381,63 @@ static int create_myini()
char path_buf[MAX_PATH];
GetCurrentDirectory(MAX_PATH, path_buf);
-
- /* Create ini file. */
- FILE *myini= fopen("my.ini","wt");
- if (!myini)
+ snprintf(my_ini_path,sizeof(my_ini_path), "%s\\my.ini", path_buf);
+ if (opt_config)
{
- die("Can't create my.ini in data directory");
+ if (!CopyFile(opt_config, my_ini_path,TRUE))
+ {
+ die("Can't copy %s to my.ini , last error %lu", opt_config, GetLastError());
+ }
}
/* Write out server settings. */
- fprintf(myini, "[mysqld]\n");
convert_slashes(path_buf);
- fprintf(myini, "datadir=%s\n", path_buf);
+ write_myini_str("datadir",path_buf);
+
if (opt_skip_networking)
{
- fprintf(myini,"skip-networking\n");
+ write_myini_str("skip-networking","ON");
if (!opt_socket)
opt_socket= opt_service;
}
- enable_named_pipe= (my_bool)
+ enable_named_pipe= (my_bool)
((opt_socket && opt_socket[0]) || opt_skip_networking);
if (enable_named_pipe)
{
- fprintf(myini,"named-pipe=ON\n");
+ write_myini_str("named-pipe","ON");
}
if (opt_socket && opt_socket[0])
{
- fprintf(myini, "socket=%s\n", opt_socket);
+ write_myini_str("socket", opt_socket);
}
if (opt_port)
{
- fprintf(myini,"port=%d\n", opt_port);
+ write_myini_int("port", opt_port);
}
if (opt_innodb_page_size != DEFAULT_INNODB_PAGE_SIZE)
{
- fprintf(myini, "innodb-page-size=%d\n", opt_innodb_page_size);
+ write_myini_int("innodb-page-size", opt_innodb_page_size);
+ }
+ if (opt_large_pages)
+ {
+ write_myini_str("large-pages","ON");
}
+
/* Write out client settings. */
- fprintf(myini, "[client]\n");
/* Used for named pipes */
if (opt_socket && opt_socket[0])
- fprintf(myini,"socket=%s\n",opt_socket);
+ write_myini_str("socket",opt_socket,"client");
if (opt_skip_networking)
- fprintf(myini,"protocol=pipe\n");
+ write_myini_str("protocol", "pipe", "client");
else if (opt_port)
- fprintf(myini,"port=%d\n",opt_port);
+ write_myini_int("port",opt_port,"client");
char *plugin_dir = get_plugindir();
if (plugin_dir)
- fprintf(myini, "plugin-dir=%s\n", plugin_dir);
- fclose(myini);
+ write_myini_str("plugin-dir", plugin_dir, "client");
return 0;
}
@@ -380,22 +460,92 @@ static const char allow_remote_root_access_cmd[]=
"DROP TABLE tmp_user;\n";
static const char end_of_script[]="-- end.";
+/*
+Add or remove privilege for a user
+@param[in] account_name - user name, Windows style, e.g "NT SERVICE\mariadb", or ".\joe"
+@param[in] privilege name - standard Windows privilege name, e.g "SeLockMemoryPrivilege"
+@param[in] add - when true, add privilege, otherwise remove it
+
+In special case where privilege name is NULL, and add is false
+all privileges for the user are removed.
+*/
+static int handle_user_privileges(const char *account_name, const wchar_t *privilege_name, bool add)
+{
+ LSA_OBJECT_ATTRIBUTES attr{};
+ LSA_HANDLE lsa_handle;
+ auto status= LsaOpenPolicy(
+ 0, &attr, POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, &lsa_handle);
+ if (status)
+ {
+ verbose("LsaOpenPolicy returned %lu", LsaNtStatusToWinError(status));
+ return 1;
+ }
+ BYTE sidbuf[SECURITY_MAX_SID_SIZE];
+ PSID sid= (PSID) sidbuf;
+ SID_NAME_USE name_use;
+ char domain_name[256];
+ DWORD cbSid= sizeof(sidbuf);
+ DWORD cbDomain= sizeof(domain_name);
+ BOOL ok= LookupAccountNameA(0, account_name, sid, &cbSid, domain_name,
+ &cbDomain, &name_use);
+ if (!ok)
+ {
+ verbose("LsaOpenPolicy returned %lu", LsaNtStatusToWinError(status));
+ return 1;
+ }
+
+ if (privilege_name)
+ {
+ LSA_UNICODE_STRING priv{};
+ priv.Buffer= (PWSTR) privilege_name;
+ priv.Length= (USHORT) wcslen(privilege_name) * sizeof(wchar_t);
+ priv.MaximumLength= priv.Length;
+ if (add)
+ {
+ status= LsaAddAccountRights(lsa_handle, sid, &priv, 1);
+ if (status)
+ {
+ verbose("LsaAddAccountRights returned %lu/%lu", status,
+ LsaNtStatusToWinError(status));
+ return 1;
+ }
+ }
+ else
+ {
+ status= LsaRemoveAccountRights(lsa_handle, sid, FALSE, &priv, 1);
+ if (status)
+ {
+ verbose("LsaRemoveRights returned %lu/%lu",
+ LsaNtStatusToWinError(status));
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(!add);
+ status= LsaRemoveAccountRights(lsa_handle, sid, TRUE, 0, 0);
+ }
+ LsaClose(lsa_handle);
+ return 0;
+}
+
/* Register service. Assume my.ini is in datadir */
-static int register_service()
+static int register_service(const char *datadir, const char *user, const char *passwd)
{
char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */
SC_HANDLE sc_manager, sc_service;
- size_t datadir_len= strlen(opt_datadir);
+ size_t datadir_len= strlen(datadir);
const char *backslash_after_datadir= "\\";
- if (datadir_len && opt_datadir[datadir_len-1] == '\\')
+ if (datadir_len && datadir[datadir_len-1] == '\\')
backslash_after_datadir= "";
verbose("Registering service '%s'", opt_service);
my_snprintf(buf, sizeof(buf)-1,
- "\"%s\" \"--defaults-file=%s%smy.ini\" \"%s\"" , mysqld_path, opt_datadir,
+ "\"%s\" \"--defaults-file=%s%smy.ini\" \"%s\"" , mysqld_path, datadir,
backslash_after_datadir, opt_service);
/* Get a handle to the SCM database. */
@@ -408,7 +558,7 @@ static int register_service()
/* Create the service. */
sc_service= CreateService(sc_manager, opt_service, opt_service,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
- SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, opt_os_user, opt_os_password);
+ SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, user, passwd);
if (!sc_service)
{
@@ -549,7 +699,7 @@ static int set_directory_permissions(const char *dir, const char *os_user)
/* Create database instance (including registering as service etc) .*/
-static int create_db_instance()
+static int create_db_instance(const char *datadir)
{
int ret= 0;
char cwd[MAX_PATH];
@@ -558,6 +708,8 @@ static int create_db_instance()
FILE *in;
bool created_datadir= false;
DWORD last_error;
+ bool service_created= false;
+ std::string mysql_db_dir;
verbose("Running bootstrap");
@@ -565,7 +717,7 @@ static int create_db_instance()
/* Create datadir and datadir/mysql, if they do not already exist. */
- if (CreateDirectory(opt_datadir, NULL))
+ if (CreateDirectory(datadir, NULL))
{
created_datadir= true;
}
@@ -576,71 +728,91 @@ static int create_db_instance()
{
case ERROR_ACCESS_DENIED:
die("Can't create data directory '%s' (access denied)\n",
- opt_datadir);
+ datadir);
break;
case ERROR_PATH_NOT_FOUND:
die("Can't create data directory '%s' "
"(one or more intermediate directories do not exist)\n",
- opt_datadir);
+ datadir);
break;
default:
die("Can't create data directory '%s', last error %u\n",
- opt_datadir, last_error);
+ datadir, last_error);
break;
}
}
- if (!SetCurrentDirectory(opt_datadir))
+ if (!SetCurrentDirectory(datadir))
{
last_error = GetLastError();
switch (last_error)
{
case ERROR_DIRECTORY:
die("Can't set current directory to '%s', the path is not a valid directory \n",
- opt_datadir);
+ datadir);
break;
default:
die("Can' set current directory to '%s', last error %u\n",
- opt_datadir, last_error);
+ datadir, last_error);
break;
}
}
- if (!PathIsDirectoryEmpty(opt_datadir))
+ if (!PathIsDirectoryEmpty(datadir))
{
- fprintf(stderr,"ERROR : Data directory %s is not empty."
- " Only new or empty existing directories are accepted for --datadir\n",opt_datadir);
+ fprintf(stderr, "ERROR : Data directory %s is not empty."
+ " Only new or empty existing directories are accepted for --datadir\n", datadir);
exit(1);
}
- if (!CreateDirectory("mysql",NULL))
+ std::string service_user;
+ /* Register service if requested. */
+ if (opt_service && opt_service[0])
{
- last_error = GetLastError();
- DWORD attributes;
- switch(last_error)
- {
- case ERROR_ACCESS_DENIED:
- die("Can't create subdirectory 'mysql' in '%s' (access denied)\n",opt_datadir);
- break;
- case ERROR_ALREADY_EXISTS:
- attributes = GetFileAttributes("mysql");
-
- if (attributes == INVALID_FILE_ATTRIBUTES)
- die("GetFileAttributes() failed for existing file '%s\\mysql', last error %u",
- opt_datadir, GetLastError());
- else if (!(attributes & FILE_ATTRIBUTE_DIRECTORY))
- die("File '%s\\mysql' exists, but it is not a directory", opt_datadir);
-
- break;
- }
+ /* Run service under virtual account NT SERVICE\service_name.*/
+ service_user.append("NT SERVICE\\").append(opt_service);
+ ret = register_service(datadir, service_user.c_str(), NULL);
+ if (ret)
+ goto end;
+ service_created = true;
+ }
+ if (opt_large_pages)
+ {
+ handle_user_privileges(service_user.c_str(), L"SeLockMemoryPrivilege", true);
+ }
+ /*
+ Set data directory permissions for both current user and
+ the one who who runs services.
+ */
+ set_directory_permissions(datadir, NULL);
+ if (!service_user.empty())
+ {
+ set_directory_permissions(datadir, service_user.c_str());
}
/*
- Set data directory permissions for both current user and
- default_os_user (the one who runs services).
+ Get security descriptor for the data directory.
+ It will be passed, as SDDL text, to the mysqld bootstrap subprocess,
+ to allow for correct subdirectory permissions.
*/
- set_directory_permissions(opt_datadir, NULL);
- set_directory_permissions(opt_datadir, default_os_user);
+ PSECURITY_DESCRIPTOR pSD;
+ if (GetNamedSecurityInfoA(datadir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ 0, 0, 0, 0, &pSD) == ERROR_SUCCESS)
+ {
+ char* string_sd = NULL;
+ if (ConvertSecurityDescriptorToStringSecurityDescriptor(pSD, SDDL_REVISION_1,
+ DACL_SECURITY_INFORMATION, &string_sd, 0))
+ {
+ _putenv_s("MARIADB_NEW_DIRECTORY_SDDL", string_sd);
+ LocalFree(string_sd);
+ }
+ LocalFree(pSD);
+ }
+
+ /* Create my.ini file in data directory.*/
+ ret = create_myini();
+ if (ret)
+ goto end;
/* Do mysqld --bootstrap. */
init_bootstrap_command_line(cmdline, sizeof(cmdline));
@@ -656,18 +828,23 @@ static int create_db_instance()
{
verbose("WARNING: Can't disable buffering on mysqld's stdin");
}
- if (fwrite("use mysql;\n",11,1, in) != 1)
+ static const char *pre_bootstrap_sql[] = { "create database mysql;\n","use mysql;\n"};
+ for (auto cmd : pre_bootstrap_sql)
{
- verbose("ERROR: Can't write to mysqld's stdin");
- ret= 1;
- goto end;
+ /* Write the bootstrap script to stdin. */
+ if (fwrite(cmd, strlen(cmd), 1, in) != 1)
+ {
+ verbose("ERROR: Can't write to mysqld's stdin");
+ ret= 1;
+ goto end;
+ }
}
- int i;
- for (i=0; mysql_bootstrap_sql[i]; i++)
+ for (int i= 0; mysql_bootstrap_sql[i]; i++)
{
+ auto cmd = mysql_bootstrap_sql[i];
/* Write the bootstrap script to stdin. */
- if (fwrite(mysql_bootstrap_sql[i], strlen(mysql_bootstrap_sql[i]), 1, in) != 1)
+ if (fwrite(cmd, strlen(cmd), 1, in) != 1)
{
verbose("ERROR: Can't write to mysqld's stdin");
ret= 1;
@@ -709,7 +886,7 @@ static int create_db_instance()
}
/*
- On some reason, bootstrap chokes if last command sent via stdin ends with
+ On some reason, bootstrap chokes if last command sent via stdin ends with
newline, so we supply a dummy comment, that does not end with newline.
*/
fputs(end_of_script, in);
@@ -723,25 +900,37 @@ static int create_db_instance()
goto end;
}
+end:
+ if (!ret)
+ return ret;
- /* Create my.ini file in data directory.*/
- ret= create_myini();
- if (ret)
- goto end;
-
- /* Register service if requested. */
- if (opt_service && opt_service[0])
+ /* Cleanup after error.*/
+ if (created_datadir)
{
- ret= register_service();
- if (ret)
- goto end;
+ SetCurrentDirectory(cwd);
+ clean_directory(datadir);
}
-end:
- if (ret)
+ if (service_created)
{
- SetCurrentDirectory(cwd);
- clean_directory(opt_datadir);
+ auto sc_manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (sc_manager)
+ {
+ auto sc_handle= OpenServiceA(sc_manager,opt_service, DELETE);
+ if (sc_handle)
+ {
+ DeleteService(sc_handle);
+ CloseServiceHandle(sc_handle);
+ }
+ CloseServiceHandle(sc_manager);
+ }
+
+ /*Remove all service user privileges for the user.*/
+ if(strncmp(service_user.c_str(), "NT SERVICE\\",
+ sizeof("NT SERVICE\\")-1))
+ {
+ handle_user_privileges(service_user.c_str(), 0, false);
+ }
if (created_datadir)
RemoveDirectory(opt_datadir);
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 9834d331474..a045dfbe2d0 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -117,12 +117,15 @@
#include "sql_reload.h" // reload_acl_and_cache
#include "sp_head.h" // init_sp_psi_keys
+#include <mysqld_default_groups.h>
+
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef _WIN32
#include <handle_connections_win.h>
+#include <sddl.h>
#endif
#include <my_service_manager.h>
@@ -369,7 +372,7 @@ my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
uint volatile global_disable_checkpoint;
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
+#if defined(_WIN32)
ulong slow_start_timeout;
#endif
/**
@@ -1400,21 +1403,10 @@ static pthread_t select_thread;
/* OS specific variables */
-#ifdef __WIN__
-#undef getpid
-#include <process.h>
-
-static bool start_mode=0, use_opt_args;
-static int opt_argc;
-static char **opt_argv;
-
-#if !defined(EMBEDDED_LIBRARY)
+#ifdef _WIN32
HANDLE hEventShutdown;
-static char shutdown_event_name[40];
-#include "nt_servc.h"
-static NTService Service; ///< Service object for WinNT
-#endif /* EMBEDDED_LIBRARY */
-#endif /* __WIN__ */
+#endif
+
#ifndef EMBEDDED_LIBRARY
bool mysqld_embedded=0;
@@ -1631,9 +1623,8 @@ static void break_connect_loop()
abort_loop= 1;
-#if defined(__WIN__)
- if (!SetEvent(hEventShutdown))
- DBUG_PRINT("error", ("Got error: %ld from SetEvent", GetLastError()));
+#if defined(_WIN32)
+ mysqld_win_initiate_shutdown();
#else
/* Avoid waiting for ourselves when thread-handling=no-threads. */
if (pthread_equal(pthread_self(), select_thread))
@@ -1905,6 +1896,12 @@ extern "C" void unireg_abort(int exit_code)
mysqld_exit(exit_code);
}
+#ifdef _WIN32
+typedef void (*report_svc_status_t)(DWORD current_state, DWORD win32_exit_code,
+ DWORD wait_hint);
+static void dummy_svc_status(DWORD, DWORD, DWORD) {}
+static report_svc_status_t my_report_svc_status= dummy_svc_status;
+#endif
static void mysqld_exit(int exit_code)
{
@@ -1935,6 +1932,9 @@ static void mysqld_exit(int exit_code)
SAFEMALLOC_REPORT_MEMORY(0);
}
DBUG_LEAVE;
+#ifdef _WIN32
+ my_report_svc_status(SERVICE_STOPPED, exit_code, 0);
+#endif
sd_notify(0, "STATUS=MariaDB server is down");
exit(exit_code); /* purecov: inspected */
}
@@ -2617,14 +2617,60 @@ void unlink_thd(THD *thd)
}
-/******************************************************************************
- Setup a signal thread with handles all signals.
- Because Linux doesn't support schemas use a mutex to check that
- the signal thread is ready before continuing
-******************************************************************************/
+#if defined(_WIN32)
+/*
+ If server is started as service, the service routine will set
+ the callback function.
+*/
+void mysqld_set_service_status_callback(void (*r)(DWORD, DWORD, DWORD))
+{
+ my_report_svc_status= r;
+}
-#if defined(__WIN__)
+static bool startup_complete()
+{
+ return hEventShutdown != NULL;
+}
+/**
+ Initiates shutdown on Windows by setting shutdown event.
+ Reports windows service status.
+
+ If startup was not finished, terminates process (no good
+ cleanup possible)
+*/
+void mysqld_win_initiate_shutdown()
+{
+ if (startup_complete())
+ {
+ my_report_svc_status(SERVICE_STOP_PENDING, 0, 0);
+ abort_loop= 1;
+ if (!SetEvent(hEventShutdown))
+ /* This should never fail.*/
+ abort();
+ }
+ else
+ {
+ my_report_svc_status(SERVICE_STOPPED, 1, 0);
+ TerminateProcess(GetCurrentProcess(), 1);
+ }
+}
+
+/*
+ Signal when server has started and can accept connections.
+*/
+void mysqld_win_set_startup_complete()
+{
+ my_report_svc_status(SERVICE_RUNNING, 0, 0);
+ DBUG_ASSERT(startup_complete());
+}
+
+
+void mysqld_win_set_service_name(const char *name)
+{
+ if (stricmp(name, "mysql"))
+ load_default_groups[array_elements(load_default_groups) - 2]= name;
+}
/*
On Windows, we use native SetConsoleCtrlHandler for handle events like Ctrl-C
@@ -2635,33 +2681,30 @@ void unlink_thd(THD *thd)
callstack.
*/
-static BOOL WINAPI console_event_handler( DWORD type )
+static BOOL WINAPI console_event_handler( DWORD type )
{
- DBUG_ENTER("console_event_handler");
-#ifndef EMBEDDED_LIBRARY
- if(type == CTRL_C_EVENT)
+ static const char *names[]= {
+ "CTRL_C_EVENT","CTRL_BREAK_EVENT", "CTRL_CLOSE_EVENT", "", "",
+ "CTRL_LOGOFF_EVENT", "CTRL_SHUTDOWN_EVENT"};
+
+ switch (type)
{
- /*
- Do not shutdown before startup is finished and shutdown
- thread is initialized. Otherwise there is a race condition
- between main thread doing initialization and CTRL-C thread doing
- cleanup, which can result into crash.
- */
-#ifndef EMBEDDED_LIBRARY
- if(hEventShutdown)
- break_connect_loop();
- else
-#endif
- sql_print_warning("CTRL-C ignored during startup");
- DBUG_RETURN(TRUE);
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ sql_print_information("console_event_handler: received %s event, shutting down",
+ names[type]);
+ mysqld_win_initiate_shutdown();
+ return TRUE;
+ case CTRL_CLOSE_EVENT:
+ sql_print_information("console_event_handler: received CTRL_CLOSE_EVENT event, terminating");
+ TerminateProcess(GetCurrentProcess(), 1);
+ return TRUE;
+ default:
+ return FALSE;
}
-#endif
- DBUG_RETURN(FALSE);
}
-
-
#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
#define DEBUGGER_ATTACH_TIMEOUT 120
/*
@@ -2692,7 +2735,7 @@ static void wait_for_debugger(int timeout_sec)
}
#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
-LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
+static LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
{
static BOOL first_time= TRUE;
if(!first_time)
@@ -2739,10 +2782,9 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
void init_signals(void)
{
- if(opt_console)
- SetConsoleCtrlHandler(console_event_handler,TRUE);
+ SetConsoleCtrlHandler(console_event_handler,TRUE);
- /* Avoid MessageBox()es*/
+ /* Avoid MessageBox()es*/
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
@@ -2759,7 +2801,8 @@ void init_signals(void)
*/
SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS
| SEM_NOOPENFILEERRORBOX);
- SetUnhandledExceptionFilter(my_unhandler_exception_filter);
+ if(!opt_debugging)
+ SetUnhandledExceptionFilter(my_unhandler_exception_filter);
}
@@ -3137,12 +3180,7 @@ void *my_str_realloc_mysqld(void *ptr, size_t size)
}
#endif
-#include <mysqld_default_groups.h>
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
-static const int load_default_groups_sz=
-sizeof(load_default_groups)/sizeof(load_default_groups[0]);
-#endif
/**
@@ -3293,7 +3331,6 @@ 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)},
@@ -3806,7 +3843,7 @@ static int init_common_variables()
of SQLCOM_ constants.
*/
compile_time_assert(sizeof(com_status_vars)/sizeof(com_status_vars[0]) - 1 ==
- SQLCOM_END + 11);
+ SQLCOM_END + 10);
#endif
if (get_options(&remaining_argc, &remaining_argv))
@@ -4991,6 +5028,31 @@ static int init_server_components()
/* The following options were added after 5.6.10 */
MYSQL_TO_BE_IMPLEMENTED_OPTION("rpl-stop-slave-timeout"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("validate-user-plugins"), // NO_EMBEDDED_ACCESS_CHECKS
+
+ /* The following options were deprecated in 10.5 or earlier */
+ MARIADB_REMOVED_OPTION("innodb-adaptive-max-sleep-delay"),
+ MARIADB_REMOVED_OPTION("innodb-background-scrub-data-check-interval"),
+ MARIADB_REMOVED_OPTION("innodb-background-scrub-data-compressed"),
+ MARIADB_REMOVED_OPTION("innodb-background-scrub-data-interval"),
+ MARIADB_REMOVED_OPTION("innodb-background-scrub-data-uncompressed"),
+ MARIADB_REMOVED_OPTION("innodb-buffer-pool-instances"),
+ MARIADB_REMOVED_OPTION("innodb-commit-concurrency"),
+ MARIADB_REMOVED_OPTION("innodb-concurrency-tickets"),
+ MARIADB_REMOVED_OPTION("innodb-file-format"),
+ MARIADB_REMOVED_OPTION("innodb-large-prefix"),
+ MARIADB_REMOVED_OPTION("innodb-lock-schedule-algorithm"),
+ MARIADB_REMOVED_OPTION("innodb-log-checksums"),
+ MARIADB_REMOVED_OPTION("innodb-log-compressed-pages"),
+ MARIADB_REMOVED_OPTION("innodb-log-files-in-group"),
+ MARIADB_REMOVED_OPTION("innodb-log-optimize-ddl"),
+ MARIADB_REMOVED_OPTION("innodb-page-cleaners"),
+ MARIADB_REMOVED_OPTION("innodb-replication-delay"),
+ MARIADB_REMOVED_OPTION("innodb-scrub-log"),
+ MARIADB_REMOVED_OPTION("innodb-scrub-log-speed"),
+ MARIADB_REMOVED_OPTION("innodb-sync-array-size"),
+ MARIADB_REMOVED_OPTION("innodb-thread-concurrency"),
+ MARIADB_REMOVED_OPTION("innodb-thread-sleep-delay"),
+ MARIADB_REMOVED_OPTION("innodb-undo-logs"),
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/*
@@ -5194,19 +5256,6 @@ static int init_server_components()
#ifndef EMBEDDED_LIBRARY
-#ifdef _WIN32
-static void create_shutdown_event()
-{
- hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
- // On "Stop Service" we have to do regular shutdown
- Service.SetShutdownEvent(hEventShutdown);
-}
-#else /*_WIN32*/
-#define create_shutdown_event()
-#endif
-#endif /* EMBEDDED_LIBRARY */
-
-#ifndef EMBEDDED_LIBRARY
#ifndef DBUG_OFF
/*
@@ -5246,11 +5295,7 @@ static void test_lc_time_sz()
#endif//DBUG_OFF
-#ifdef __WIN__
-int win_main(int argc, char **argv)
-#else
int mysqld_main(int argc, char **argv)
-#endif
{
#ifndef _WIN32
/* We can't close stdin just now, because it may be booststrap mode. */
@@ -5268,7 +5313,6 @@ int mysqld_main(int argc, char **argv)
if (init_early_variables())
exit(1);
-#ifndef _WIN32
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
pre_initialize_performance_schema();
#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
@@ -5278,7 +5322,6 @@ int mysqld_main(int argc, char **argv)
fprintf(stderr, "my_init() failed.");
return 1;
}
-#endif
orig_argc= argc;
orig_argv= argv;
@@ -5485,12 +5528,12 @@ int mysqld_main(int argc, char **argv)
if (WSREP_ON && wsrep_check_opts()) unireg_abort(1);
#endif
+#ifdef _WIN32
/*
The subsequent calls may take a long time : e.g. innodb log read.
Thus set the long running service control manager timeout
*/
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
- Service.SetSlowStarting(slow_start_timeout);
+ my_report_svc_status(SERVICE_START_PENDING, NO_ERROR, slow_start_timeout);
#endif
if (init_server_components())
@@ -5499,13 +5542,6 @@ int mysqld_main(int argc, char **argv)
init_ssl();
network_init();
-#ifdef _WIN32
- if (!opt_console)
- {
- FreeConsole(); // Remove window
- }
-#endif
-
#ifdef WITH_WSREP
// Recover and exit.
if (wsrep_recovery)
@@ -5593,7 +5629,6 @@ int mysqld_main(int argc, char **argv)
}
}
- create_shutdown_event();
start_handle_manager();
/* Copy default global rpl_filter to global_rpl_filter */
@@ -5647,9 +5682,6 @@ int mysqld_main(int argc, char **argv)
}
#endif
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
- Service.SetRunning();
-#endif
/* Signal threads waiting for server to be started */
mysql_mutex_lock(&LOCK_server_started);
@@ -5705,16 +5737,6 @@ int mysqld_main(int argc, char **argv)
*/
PSI_CALL_delete_current_thread();
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
- if (start_mode)
- Service.Stop();
- else
- {
- Service.SetShutdownEvent(0);
- if (hEventShutdown)
- CloseHandle(hEventShutdown);
- }
-#endif
#if (defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY))
ERR_remove_state(0);
#endif
@@ -5725,245 +5747,6 @@ int mysqld_main(int argc, char **argv)
#endif /* !EMBEDDED_LIBRARY */
-/****************************************************************************
- Main and thread entry function for Win32
- (all this is needed only to run mysqld as a service on WinNT)
-****************************************************************************/
-
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
-void mysql_service(void *p)
-{
- if (my_thread_init())
- abort();
-
- if (use_opt_args)
- win_main(opt_argc, opt_argv);
- else
- win_main(Service.my_argc, Service.my_argv);
-
- my_thread_end();
-}
-
-
-/* Quote string if it contains space, else copy */
-
-static char *add_quoted_string(char *to, const char *from, char *to_end)
-{
- uint length= (uint) (to_end-to);
-
- if (!strchr(from, ' '))
- return strmake(to, from, length-1);
- return strxnmov(to, length-1, "\"", from, "\"", NullS);
-}
-
-
-/**
- Handle basic handling of services, like installation and removal.
-
- @param argv Pointer to argument list
- @param servicename Internal name of service
- @param displayname Display name of service (in taskbar ?)
- @param file_path Path to this program
- @param startup_option Startup option to mysqld
-
- @retval 0 option handled
- @retval 1 Could not handle option
-*/
-
-static bool
-default_service_handling(char **argv,
- const char *servicename,
- const char *displayname,
- const char *file_path,
- const char *extra_opt,
- const char *account_name)
-{
- char path_and_service[FN_REFLEN+FN_REFLEN+32], *pos, *end;
- const char *opt_delim;
- end= path_and_service + sizeof(path_and_service)-3;
-
- /* We have to quote filename if it contains spaces */
- pos= add_quoted_string(path_and_service, file_path, end);
- if (extra_opt && *extra_opt)
- {
- /*
- Add option after file_path. There will be zero or one extra option. It's
- assumed to be --defaults-file=file but isn't checked. The variable (not
- the option name) should be quoted if it contains a string.
- */
- *pos++= ' ';
- if ((opt_delim= strchr(extra_opt, '=')))
- {
- size_t length= ++opt_delim - extra_opt;
- pos= strnmov(pos, extra_opt, length);
- }
- else
- opt_delim= extra_opt;
-
- pos= add_quoted_string(pos, opt_delim, end);
- }
- /* We must have servicename last */
- *pos++= ' ';
- (void) add_quoted_string(pos, servicename, end);
-
- if (Service.got_service_option(argv, "install"))
- {
- Service.Install(1, servicename, displayname, path_and_service,
- account_name);
- return 0;
- }
- if (Service.got_service_option(argv, "install-manual"))
- {
- Service.Install(0, servicename, displayname, path_and_service,
- account_name);
- return 0;
- }
- if (Service.got_service_option(argv, "remove"))
- {
- Service.Remove(servicename);
- return 0;
- }
- return 1;
-}
-
-/* Remove service name from the command line arguments, and pass
-resulting command line to the service via opt_args.*/
-#include <vector>
-static void service_init_cmdline_args(int argc, char **argv)
-{
- start_mode= 1;
- use_opt_args= 1;
-
- if(argc == 1)
- {
- opt_argc= argc;
- opt_argv= argv;
- }
- else
- {
- static std::vector<char *> argv_no_service;
- for (int i= 0; argv[i]; i++)
- argv_no_service.push_back(argv[i]);
- // Remove the last argument, service name
- argv_no_service[argv_no_service.size() - 1]= 0;
- opt_argc= (int)argv_no_service.size() - 1;
- opt_argv= &argv_no_service[0];
- }
- DBUG_ASSERT(!opt_argv[opt_argc]);
-}
-
-int mysqld_main(int argc, char **argv)
-{
- my_progname= argv[0];
-
- /*
- When several instances are running on the same machine, we
- need to have an unique named hEventShudown through the
- application PID e.g.: MySQLShutdown1890; MySQLShutdown2342
- */
- int10_to_str((int) GetCurrentProcessId(),strmov(shutdown_event_name,
- "MySQLShutdown"), 10);
-
- /* Must be initialized early for comparison of service name */
- system_charset_info= &my_charset_utf8mb3_general_ci;
-
-#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
- pre_initialize_performance_schema();
-#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
-
- if (my_init())
- {
- fprintf(stderr, "my_init() failed.");
- return 1;
- }
-
-
- char file_path[FN_REFLEN];
- my_path(file_path, argv[0], ""); /* Find name in path */
- fn_format(file_path,argv[0],file_path,"", MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS);
-
-
- if (argc == 2)
- {
- if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
- file_path, "", NULL))
- return 0;
-
- if (Service.IsService(argv[1])) /* Start an optional service */
- {
- /*
- Only add the service name to the groups read from the config file
- if it's not "MySQL". (The default service name should be 'mysqld'
- but we started a bad tradition by calling it MySQL from the start
- and we are now stuck with it.
- */
- if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
- load_default_groups[load_default_groups_sz-2]= argv[1];
- service_init_cmdline_args(argc, argv);
- Service.Init(argv[1], mysql_service);
- return 0;
- }
- }
- else if (argc == 3) /* install or remove any optional service */
- {
- if (!default_service_handling(argv, argv[2], argv[2], file_path, "",
- NULL))
- return 0;
- if (Service.IsService(argv[2]))
- {
- /*
- mysqld was started as
- mysqld --defaults-file=my_path\my.ini service-name
- */
- if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
- load_default_groups[load_default_groups_sz-2]= argv[2];
- service_init_cmdline_args(argc, argv);
- Service.Init(argv[2], mysql_service);
- return 0;
- }
- }
- else if (argc == 4 || argc == 5)
- {
- /*
- This may seem strange, because we handle --local-service while
- preserving 4.1's behavior of allowing any one other argument that is
- passed to the service on startup. (The assumption is that this is
- --defaults-file=file, but that was not enforced in 4.1, so we don't
- enforce it here.)
- */
- const char *extra_opt= NullS;
- const char *account_name = NullS;
- int index;
- for (index = 3; index < argc; index++)
- {
- if (!strcmp(argv[index], "--local-service"))
- account_name= "NT AUTHORITY\\LocalService";
- else
- extra_opt= argv[index];
- }
-
- if (argc == 4 || account_name)
- if (!default_service_handling(argv, argv[2], argv[2], file_path,
- extra_opt, account_name))
- return 0;
- }
- else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
- {
- /* start the default service */
- service_init_cmdline_args(argc, argv);
- Service.Init(MYSQL_SERVICENAME, mysql_service);
- return 0;
- }
-
- /* Start as standalone server */
- Service.my_argc=argc;
- Service.my_argv=argv;
- mysql_service(NULL);
- return 0;
-}
-#endif
-
-
static bool read_init_file(char *file_name)
{
MYSQL_FILE *file;
@@ -7387,6 +7170,7 @@ SHOW_VAR status_vars[]= {
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
{"Memory_used", (char*) &show_memory_used, SHOW_SIMPLE_FUNC},
{"Memory_used_initial", (char*) &start_memory_used, SHOW_LONGLONG},
+ {"Resultset_metadata_skipped", (char *) offsetof(STATUS_VAR, skip_metadata_count),SHOW_LONG_STATUS},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
{"Open_files", (char*) &my_file_opened, SHOW_SINT},
{"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
@@ -8179,6 +7963,23 @@ mysqld_get_one_option(const struct my_option *opt, char *argument,
break;
case OPT_BOOTSTRAP:
opt_noacl=opt_bootstrap=1;
+#ifdef _WIN32
+ {
+ /*
+ Check if security descriptor is passed from
+ mysql_install_db.exe.
+ Used by Windows installer to correctly setup
+ privileges on the new directories.
+ */
+ char* dir_sddl = getenv("MARIADB_NEW_DIRECTORY_SDDL");
+ if (dir_sddl)
+ {
+ ConvertStringSecurityDescriptorToSecurityDescriptor(
+ dir_sddl, SDDL_REVISION_1, &my_dir_security_attributes.lpSecurityDescriptor, NULL);
+ DBUG_ASSERT(my_dir_security_attributes.lpSecurityDescriptor);
+ }
+ }
+#endif
break;
case OPT_SERVER_ID:
::server_id= global_system_variables.server_id;
@@ -9252,6 +9053,7 @@ PSI_memory_key key_memory_binlog_ver_1_event;
PSI_memory_key key_memory_bison_stack;
PSI_memory_key key_memory_blob_mem_storage;
PSI_memory_key key_memory_dboptions_hash;
+PSI_memory_key key_memory_dbnames_cache;
PSI_memory_key key_memory_errmsgs;
PSI_memory_key key_memory_frm_string;
PSI_memory_key key_memory_gdl;
@@ -9552,6 +9354,7 @@ static PSI_memory_info all_server_memory[]=
{ &key_memory_THD_handler_tables_hash, "THD::handler_tables_hash", 0},
{ &key_memory_hash_index_key_buffer, "hash_index_key_buffer", 0},
{ &key_memory_dboptions_hash, "dboptions_hash", 0},
+ { &key_memory_dbnames_cache, "dbnames_cache", 0},
{ &key_memory_user_conn, "user_conn", 0},
// { &key_memory_LOG_POS_COORD, "LOG_POS_COORD", 0},
// { &key_memory_XID_STATE, "XID_STATE", 0},
diff --git a/sql/mysqld.h b/sql/mysqld.h
index e8dd5706e80..cd05a66fff3 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -502,6 +502,7 @@ extern PSI_memory_key key_memory_TABLE;
extern PSI_memory_key key_memory_binlog_statement_buffer;
extern PSI_memory_key key_memory_user_conn;
extern PSI_memory_key key_memory_dboptions_hash;
+extern PSI_memory_key key_memory_dbnames_cache;
extern PSI_memory_key key_memory_hash_index_key_buffer;
extern PSI_memory_key key_memory_THD_handler_tables_hash;
extern PSI_memory_key key_memory_JOIN_CACHE;
@@ -958,4 +959,14 @@ extern ulong opt_binlog_dbug_fsync_sleep;
extern uint volatile global_disable_checkpoint;
extern my_bool opt_help;
+extern int mysqld_main(int argc, char **argv);
+
+#ifdef _WIN32
+extern HANDLE hEventShutdown;
+extern void mysqld_win_initiate_shutdown();
+extern void mysqld_win_set_startup_complete();
+extern void mysqld_set_service_status_callback(void (*)(DWORD, DWORD, DWORD));
+extern void mysqld_win_set_service_name(const char *name);
+#endif
+
#endif /* MYSQLD_INCLUDED */
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
deleted file mode 100644
index 9c754763aab..00000000000
--- a/sql/nt_servc.cc
+++ /dev/null
@@ -1,555 +0,0 @@
-/**
- @file
-
- @brief
- Windows NT Service class library.
-
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
-*/
-#include <windows.h>
-#include <process.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "nt_servc.h"
-
-
-static NTService *pService;
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-NTService::NTService()
-{
-
- bOsNT = FALSE;
- //service variables
- ServiceName = NULL;
- hExitEvent = 0;
- bPause = FALSE;
- bRunning = FALSE;
- hThreadHandle = 0;
- fpServiceThread = NULL;
-
- //time-out variables
- nStartTimeOut = 15000;
- nStopTimeOut = 86400000;
- nPauseTimeOut = 5000;
- nResumeTimeOut = 5000;
-
- //install variables
- dwDesiredAccess = SERVICE_ALL_ACCESS;
- dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- dwStartType = SERVICE_AUTO_START;
- dwErrorControl = SERVICE_ERROR_NORMAL;
- szLoadOrderGroup = NULL;
- lpdwTagID = NULL;
- szDependencies = NULL;
-
- my_argc = 0;
- my_argv = NULL;
- hShutdownEvent = 0;
- nError = 0;
- dwState = 0;
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-NTService::~NTService()
-{
- if (ServiceName != NULL) delete[] ServiceName;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-
-/**
- Registers the main service thread with the service manager.
-
- @param ServiceThread pointer to the main programs entry function
- when the service is started
-*/
-
-
-long NTService::Init(LPCSTR szInternName, THREAD_FC ServiceThread)
-{
-
- pService = this;
-
- fpServiceThread = ServiceThread;
- ServiceName = new char[lstrlen(szInternName)+1];
- lstrcpy(ServiceName,szInternName);
-
- SERVICE_TABLE_ENTRY stb[] =
- {
- { (char *)szInternName, ServiceMain} ,
- { NULL, NULL }
- };
-
- return StartServiceCtrlDispatcher(stb); //register with the Service Manager
-}
-
-
-/**
- Installs the service with Service manager.
-
- nError values:
- - 0 success
- - 1 Can't open the Service manager
- - 2 Failed to create service.
-*/
-
-
-BOOL NTService::Install(int startType, LPCSTR szInternName,
- LPCSTR szDisplayName,
- LPCSTR szFullPath, LPCSTR szAccountName,
- LPCSTR szPassword)
-{
- BOOL ret_val=FALSE;
- SC_HANDLE newService, scm;
-
- if (!SeekStatus(szInternName,1))
- return FALSE;
-
- char szFilePath[_MAX_PATH];
- GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- printf("Failed to install the service (Couldn't open the SCM)\n");
- else // Install the new service
- {
- if (!(newService=
- CreateService(scm,
- szInternName,
- szDisplayName,
- dwDesiredAccess,//default: SERVICE_ALL_ACCESS
- dwServiceType, //default: SERVICE_WIN32_OWN_PROCESS
- //default: SERVICE_AUTOSTART
- (startType == 1 ? SERVICE_AUTO_START :
- SERVICE_DEMAND_START),
- dwErrorControl, //default: SERVICE_ERROR_NORMAL
- szFullPath, //exec full path
- szLoadOrderGroup, //default: NULL
- lpdwTagID, //default: NULL
- szDependencies, //default: NULL
- szAccountName, //default: NULL
- szPassword))) //default: NULL
- printf("Failed to install the service (Couldn't create service)\n");
- else
- {
- printf("Service successfully installed.\n");
- CloseServiceHandle(newService);
- ret_val=TRUE; // Everything went ok
- }
- CloseServiceHandle(scm);
- }
- return ret_val;
-}
-
-
-/**
- Removes the service.
-
- nError values:
- - 0 success
- - 1 Can't open the Service manager
- - 2 Failed to locate service
- - 3 Failed to delete service.
-*/
-
-
-BOOL NTService::Remove(LPCSTR szInternName)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- if (!SeekStatus(szInternName,0))
- return FALSE;
-
- nError=0;
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- {
- printf("Failed to remove the service (Couldn't open the SCM)\n");
- }
- else
- {
- if ((service = OpenService(scm,szInternName, DELETE)))
- {
- if (!DeleteService(service))
- printf("Failed to remove the service\n");
- else
- {
- printf("Service successfully removed.\n");
- ret_value=TRUE; // everything went ok
- }
- CloseServiceHandle(service);
- }
- else
- printf("Failed to remove the service (Couldn't open the service)\n");
- CloseServiceHandle(scm);
- }
- return ret_value;
-}
-
-/**
- this function should be called before the app. exits to stop
- the service
-*/
-void NTService::Stop(void)
-{
- SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
- StopService();
- SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
-}
-
-/**
- This is the function that is called from the
- service manager to start the service.
-*/
-
-
-void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
-{
-
- // registration function
- if (!(pService->hServiceStatusHandle =
- RegisterServiceCtrlHandler(pService->ServiceName,
- NTService::ServiceCtrlHandler)))
- goto error;
-
- // notify SCM of progress
- if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 1, 8000))
- goto error;
-
- // create the exit event
- if (!(pService->hExitEvent = CreateEvent (0, TRUE, FALSE,0)))
- goto error;
-
- if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 3,
- pService->nStartTimeOut))
- goto error;
-
- // save start arguments
- pService->my_argc=argc;
- pService->my_argv=argv;
-
- // start the service
- if (!pService->StartService())
- goto error;
-
- // wait for exit event
- WaitForSingleObject (pService->hExitEvent, INFINITE);
-
- // wait for thread to exit
- if (WaitForSingleObject (pService->hThreadHandle, INFINITE) == WAIT_TIMEOUT)
- CloseHandle(pService->hThreadHandle);
-
- pService->Exit(0);
- return;
-
-error:
- pService->Exit(GetLastError());
- return;
-}
-
-
-
-void NTService::SetRunning()
-{
- if (pService)
- pService->SetStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
-}
-
-void NTService::SetSlowStarting(unsigned long timeout)
-{
- if (pService)
- pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 0, timeout);
-}
-
-
-/* ------------------------------------------------------------------------
- StartService() - starts the application thread
- -------------------------------------------------------------------------- */
-
-BOOL NTService::StartService()
-{
- // Start the real service's thread (application)
- if (!(hThreadHandle = (HANDLE) _beginthread(fpServiceThread,0,
- (void *) this)))
- return FALSE;
- bRunning = TRUE;
- return TRUE;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::StopService()
-{
- bRunning=FALSE;
-
- // Set the event for application
- if (hShutdownEvent)
- SetEvent(hShutdownEvent);
-
- // Set the event for ServiceMain
- SetEvent(hExitEvent);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::PauseService()
-{
- bPause = TRUE;
- SuspendThread(hThreadHandle);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::ResumeService()
-{
- bPause=FALSE;
- ResumeThread(hThreadHandle);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-BOOL NTService::SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
- DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
- DWORD dwWaitHint)
-{
- BOOL bRet;
- SERVICE_STATUS serviceStatus;
-
- dwState=dwCurrentState;
-
- serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- serviceStatus.dwCurrentState = dwCurrentState;
-
- if (dwCurrentState == SERVICE_START_PENDING)
- serviceStatus.dwControlsAccepted = 0; //don't accept control events
- else
- serviceStatus.dwControlsAccepted = (SERVICE_ACCEPT_STOP |
- SERVICE_ACCEPT_PAUSE_CONTINUE |
- SERVICE_ACCEPT_SHUTDOWN);
-
- // if a specific exit code is defined,set up the win32 exit code properly
- if (dwServiceSpecificExitCode == 0)
- serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
- else
- serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
-
- serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
-
- serviceStatus.dwCheckPoint = dwCheckPoint;
- serviceStatus.dwWaitHint = dwWaitHint;
-
- // Pass the status to the Service Manager
- if (!(bRet=SetServiceStatus (hServiceStatusHandle, &serviceStatus)))
- StopService();
-
- return bRet;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::ServiceCtrlHandler(DWORD ctrlCode)
-{
- DWORD dwState;
-
- if (!pService)
- return;
-
- dwState=pService->dwState; // get current state
-
- switch(ctrlCode) {
- case SERVICE_CONTROL_SHUTDOWN:
- case SERVICE_CONTROL_STOP:
- dwState = SERVICE_STOP_PENDING;
- pService->SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1,
- pService->nStopTimeOut);
- pService->StopService();
- break;
-
- default:
- pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
- break;
- }
- //pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-void NTService::Exit(DWORD error)
-{
- if (hExitEvent)
- CloseHandle(hExitEvent);
-
- // Send a message to the scm to tell that we stop
- if (hServiceStatusHandle)
- SetStatus(SERVICE_STOPPED, error,0, 0, 0);
-
- // If the thread has started kill it ???
- // if (hThreadHandle) CloseHandle(hThreadHandle);
-
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-BOOL NTService::SeekStatus(LPCSTR szInternName, int OperationType)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- {
- DWORD ret_error=GetLastError();
- if (ret_error == ERROR_ACCESS_DENIED)
- {
- printf("Install/Remove of the Service Denied!\n");
- if (!is_super_user())
- printf("That operation should be made by an user with Administrator privileges!\n");
- }
- else
- printf("There is a problem for to open the Service Control Manager!\n");
- }
- else
- {
- if (OperationType == 1)
- {
- /* an install operation */
- if ((service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
- {
- LPQUERY_SERVICE_CONFIG ConfigBuf;
- DWORD dwSize;
-
- ConfigBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
- printf("The service already exists!\n");
- if (QueryServiceConfig(service,ConfigBuf,4096,&dwSize))
- printf("The current server installed: %s\n",
- ConfigBuf->lpBinaryPathName);
- LocalFree(ConfigBuf);
- CloseServiceHandle(service);
- }
- else
- ret_value=TRUE;
- }
- else
- {
- /* a remove operation */
- if (!(service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
- printf("The service doesn't exist!\n");
- else
- {
- SERVICE_STATUS ss;
-
- memset(&ss, 0, sizeof(ss));
- if (QueryServiceStatus(service,&ss))
- {
- DWORD dwState = ss.dwCurrentState;
- if (dwState == SERVICE_RUNNING)
- printf("Failed to remove the service because the service is running\nStop the service and try again\n");
- else if (dwState == SERVICE_STOP_PENDING)
- printf("\
-Failed to remove the service because the service is in stop pending state!\n\
-Wait 30 seconds and try again.\n\
-If this condition persist, reboot the machine and try again\n");
- else
- ret_value= TRUE;
- }
- CloseServiceHandle(service);
- }
- }
- CloseServiceHandle(scm);
- }
-
- return ret_value;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::IsService(LPCSTR ServiceName)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- if ((scm= OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE)))
- {
- if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS)))
- {
- ret_value=TRUE;
- CloseServiceHandle(service);
- }
- CloseServiceHandle(scm);
- }
- return ret_value;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::got_service_option(char **argv, const char *service_option)
-{
- char *option;
- for (option= argv[1]; *option; option++)
- if (!strcmp(option, service_option))
- return TRUE;
- return FALSE;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::is_super_user()
-{
- HANDLE hAccessToken;
- UCHAR InfoBuffer[1024];
- PTOKEN_GROUPS ptgGroups=(PTOKEN_GROUPS)InfoBuffer;
- DWORD dwInfoBufferSize;
- PSID psidAdministrators;
- SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
- UINT x;
- BOOL ret_value=FALSE;
-
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE,&hAccessToken ))
- {
- if (GetLastError() != ERROR_NO_TOKEN)
- return FALSE;
-
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hAccessToken))
- return FALSE;
- }
-
- ret_value= GetTokenInformation(hAccessToken,TokenGroups,InfoBuffer,
- 1024, &dwInfoBufferSize);
-
- CloseHandle(hAccessToken);
-
- if (!ret_value )
- return FALSE;
-
- if (!AllocateAndInitializeSid(&siaNtAuthority, 2,
- SECURITY_BUILTIN_DOMAIN_RID,
- DOMAIN_ALIAS_RID_ADMINS,
- 0, 0, 0, 0, 0, 0,
- &psidAdministrators))
- return FALSE;
-
- ret_value = FALSE;
-
- for (x=0;x<ptgGroups->GroupCount;x++)
- {
- if ( EqualSid(psidAdministrators, ptgGroups->Groups[x].Sid) )
- {
- ret_value = TRUE;
- break;
- }
-
- }
- FreeSid(psidAdministrators);
- return ret_value;
-}
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
deleted file mode 100644
index 8ba29519c8f..00000000000
--- a/sql/nt_servc.h
+++ /dev/null
@@ -1,113 +0,0 @@
-#ifndef NT_SERVC_INCLUDED
-#define NT_SERVC_INCLUDED
-
-/**
- @file
-
- @brief
- Windows NT Service class library
-
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
-*/
-
-// main application thread
-typedef void (*THREAD_FC)(void *);
-
-class NTService
-{
- public:
- NTService();
- ~NTService();
-
- BOOL bOsNT; ///< true if OS is NT, false for Win95
- //install optinos
- DWORD dwDesiredAccess;
- DWORD dwServiceType;
- DWORD dwStartType;
- DWORD dwErrorControl;
-
- LPSTR szLoadOrderGroup;
- LPDWORD lpdwTagID;
- LPSTR szDependencies;
- OSVERSIONINFO osVer;
-
- // time-out (in milisec)
- int nStartTimeOut;
- int nStopTimeOut;
- int nPauseTimeOut;
- int nResumeTimeOut;
-
- //
- DWORD my_argc;
- LPTSTR *my_argv;
- HANDLE hShutdownEvent;
- int nError;
- DWORD dwState;
-
- //init service entry point
- long Init(LPCSTR szInternName,THREAD_FC ServiceThread);
-
- //application shutdown event
- void SetShutdownEvent(HANDLE hEvent){ hShutdownEvent=hEvent; }
-
-
- //service install / un-install
- BOOL Install(int startType,LPCSTR szInternName,LPCSTR szDisplayName,
- LPCSTR szFullPath, LPCSTR szAccountName=NULL,
- LPCSTR szPassword=NULL);
- BOOL SeekStatus(LPCSTR szInternName, int OperationType);
- BOOL Remove(LPCSTR szInternName);
- BOOL IsService(LPCSTR ServiceName);
- BOOL got_service_option(char **argv, const char *service_option);
- BOOL is_super_user();
-
- /*
- SetRunning() is to be called by the application
- when initialization completes and it can accept
- stop request
- */
- void SetRunning(void);
-
- /**
- Sets a timeout after which SCM will abort service startup if SetRunning()
- was not called or the timeout was not extended with another call to
- SetSlowStarting(). Should be called when static initialization completes,
- and the variable initialization part begins
-
- @arg timeout the timeout to pass to the SCM (in milliseconds)
- */
- void SetSlowStarting(unsigned long timeout);
-
- /*
- Stop() is to be called by the application to stop
- the service
- */
- void Stop(void);
-
- protected:
- LPSTR ServiceName;
- HANDLE hExitEvent;
- SERVICE_STATUS_HANDLE hServiceStatusHandle;
- BOOL bPause;
- BOOL bRunning;
- HANDLE hThreadHandle;
- THREAD_FC fpServiceThread;
-
- void PauseService();
- void ResumeService();
- void StopService();
- BOOL StartService();
-
- static void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
- static void WINAPI ServiceCtrlHandler (DWORD ctrlCode);
-
- void Exit(DWORD error);
- BOOL SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
- DWORD dwServiceSpecificExitCode,
- DWORD dwCheckPoint,DWORD dwWaitHint);
-
-};
-/* ------------------------- the end -------------------------------------- */
-
-#endif /* NT_SERVC_INCLUDED */
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 0a87d9ccd2f..96a35e10bf2 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -4291,11 +4291,11 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
sjm_tab->type= JT_ALL;
/* Initialize full scan */
- sjm_tab->read_first_record= join_read_record_no_init;
+ sjm_tab->read_first_record= join_init_read_record;
sjm_tab->read_record.copy_field= sjm->copy_field;
sjm_tab->read_record.copy_field_end= sjm->copy_field +
sjm->sjm_table_cols.elements;
- sjm_tab->read_record.read_record_func= rr_sequential_and_unpack;
+ sjm_tab->read_record.read_record_func= read_record_func_for_rr_and_unpack;
}
sjm_tab->bush_children->end[-1].next_select= end_sj_materialize;
@@ -7145,3 +7145,16 @@ exit:
thd->lex->current_select= save_curr_select;
DBUG_RETURN(FALSE);
}
+
+/*
+ @brief
+ Check if a table is a SJM Scan table
+
+ @retval
+ TRUE SJM scan table
+ FALSE Otherwise
+*/
+bool TABLE_LIST::is_sjm_scan_table()
+{
+ return is_active_sjm() && sj_mat_info->is_sj_scan;
+}
diff --git a/sql/protocol.cc b/sql/protocol.cc
index dfab9e50ac9..7d479ab2e3b 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -211,8 +211,7 @@ bool
Protocol::net_send_ok(THD *thd,
uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong id,
- const char *message, bool is_eof,
- bool skip_flush)
+ const char *message, bool is_eof)
{
NET *net= &thd->net;
StringBuffer<MYSQL_ERRMSG_SIZE + 10> store;
@@ -285,7 +284,7 @@ Protocol::net_send_ok(THD *thd,
DBUG_ASSERT(store.length() <= MAX_PACKET_LENGTH);
error= my_net_write(net, (const unsigned char*)store.ptr(), store.length());
- if (likely(!error) && (!skip_flush || is_eof))
+ if (likely(!error))
error= net_flush(net);
thd->server_status&= ~SERVER_SESSION_STATE_CHANGED;
@@ -340,7 +339,7 @@ Protocol::net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
(thd->get_command() != COM_BINLOG_DUMP ))
{
error= net_send_ok(thd, server_status, statement_warn_count, 0, 0, NULL,
- true, false);
+ true);
DBUG_RETURN(error);
}
@@ -607,16 +606,14 @@ void Protocol::end_statement()
thd->get_stmt_da()->statement_warn_count(),
thd->get_stmt_da()->affected_rows(),
thd->get_stmt_da()->last_insert_id(),
- thd->get_stmt_da()->message(),
- thd->get_stmt_da()->skip_flush());
+ thd->get_stmt_da()->message());
break;
case Diagnostics_area::DA_DISABLED:
break;
case Diagnostics_area::DA_EMPTY:
default:
DBUG_ASSERT(0);
- error= send_ok(thd->server_status, 0, 0, 0, NULL,
- thd->get_stmt_da()->skip_flush());
+ error= send_ok(thd->server_status, 0, 0, 0, NULL);
break;
}
if (likely(!error))
@@ -635,12 +632,12 @@ void Protocol::end_statement()
bool Protocol::send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush)
+ const char *message)
{
DBUG_ENTER("Protocol::send_ok");
const bool retval=
net_send_ok(thd, server_status, statement_warn_count,
- affected_rows, last_insert_id, message, false, skip_flush);
+ affected_rows, last_insert_id, message, false);
DBUG_RETURN(retval);
}
@@ -916,6 +913,241 @@ bool Protocol_text::store_field_metadata(const THD * thd,
}
+/*
+ MARIADB_CLIENT_CACHE_METADATA support.
+
+ Bulk of the code below is dedicated to detecting whether column metadata has
+ changed after prepare, or between executions of a prepared statement.
+
+ For some prepared statements, metadata can't change without going through
+ Prepared_Statement::reprepare(), which makes detecting changes easy.
+
+ Others, "SELECT ?" & Co, are more fragile, and sensitive to input parameters,
+ or user variables. Detecting metadata change for this class of PS is harder,
+ we calculate signature (hash value), and check whether this changes between
+ executions. This is a more expensive method.
+*/
+
+
+/**
+ Detect whether column info can be changed without
+ PS repreparing.
+
+ Such colum info is called fragile. The opposite of
+ fragile is.
+
+
+ @param it - Item representing column info
+ @return true, if columninfo is "fragile", false if it is stable
+
+
+ @todo does not work due to MDEV-23913. Currently,
+ everything about prepared statements is fragile.
+*/
+
+static bool is_fragile_columnifo(Item *it)
+{
+#define MDEV_23913_FIXED 0
+#if MDEV_23913_FIXED
+ if (dynamic_cast<Item_param *>(it))
+ return true;
+
+ if (dynamic_cast<Item_func_user_var *>(it))
+ return true;
+
+ if (dynamic_cast <Item_sp_variable*>(it))
+ return true;
+
+ /* Check arguments of functions.*/
+ auto item_args= dynamic_cast<Item_args *>(it);
+ if (!item_args)
+ return false;
+ auto args= item_args->arguments();
+ auto arg_count= item_args->argument_count();
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (is_fragile_columnifo(args[i]))
+ return true;
+ }
+ return false;
+#else /* MDEV-23913 fixed*/
+ return true;
+#endif
+}
+
+
+#define INVALID_METADATA_CHECKSUM 0
+
+
+/**
+ Calculate signature for column info sent to the client as CRC32 over data,
+ that goes into the column info packet.
+ We assume that if checksum does not change, then column info was not
+ modified.
+
+ @param thd THD
+ @param list column info
+
+ @return CRC32 of the metadata
+*/
+
+static uint32 calc_metadata_hash(THD *thd, List<Item> *list)
+{
+ List_iterator_fast<Item> it(*list);
+ Item *item;
+ uint32 crc32_c= 0;
+ while ((item= it++))
+ {
+ Send_field field(thd, item);
+ auto field_type= item->type_handler()->field_type();
+ auto charset= item->charset_for_protocol();
+ /*
+ The data below should contain everything that influences
+ content of the column info packet.
+ */
+ LEX_CSTRING data[]=
+ {
+ field.table_name,
+ field.org_table_name,
+ field.col_name,
+ field.org_col_name,
+ field.db_name,
+ field.attr(MARIADB_FIELD_ATTR_DATA_TYPE_NAME),
+ field.attr(MARIADB_FIELD_ATTR_FORMAT_NAME),
+ {(const char *) &field.length, sizeof(field.length)},
+ {(const char *) &field.flags, sizeof(field.flags)},
+ {(const char *) &field.decimals, sizeof(field.decimals)},
+ {(const char *) &charset, sizeof(charset)},
+ {(const char *) &field_type, sizeof(field_type)},
+ };
+ for (const auto &chunk : data)
+ crc32_c= my_crc32c(crc32_c, chunk.str, chunk.length);
+ }
+
+ if (crc32_c == INVALID_METADATA_CHECKSUM)
+ return 1;
+ return crc32_c;
+}
+
+
+
+/**
+ Check if metadata columns have changed since last call to this
+ function.
+
+ @param send_column_info_state saved state, changed if the function
+ return true.
+ @param thd THD
+ @param list columninfo Items
+ @return true,if metadata columns have changed since last call,
+ false otherwise
+*/
+
+static bool metadata_columns_changed(send_column_info_state &state, THD *thd,
+ List<Item> &list)
+{
+ if (!state.initialized)
+ {
+ state.initialized= true;
+ state.immutable= true;
+ Item *item;
+ List_iterator_fast<Item> it(list);
+ while ((item= it++))
+ {
+ if (is_fragile_columnifo(item))
+ {
+ state.immutable= false;
+ state.checksum= calc_metadata_hash(thd, &list);
+ break;
+ }
+ }
+ state.last_charset= thd->variables.character_set_client;
+ return true;
+ }
+
+ /*
+ Since column info can change under our feet, we use more expensive
+ checksumming to check if column metadata has not changed since last time.
+ */
+ if (!state.immutable)
+ {
+ uint32 checksum= calc_metadata_hash(thd, &list);
+ if (checksum != state.checksum)
+ {
+ state.checksum= checksum;
+ state.last_charset= thd->variables.character_set_client;
+ return true;
+ }
+ }
+
+ /*
+ Character_set_client influences result set metadata, thus resend metadata
+ whenever it changes.
+ */
+ if (state.last_charset != thd->variables.character_set_client)
+ {
+ state.last_charset= thd->variables.character_set_client;
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ Determine whether column info must be sent to the client.
+ Skip column info, if client supports caching, and (prepared) statement
+ output fields have not changed.
+
+ @param thd THD
+ @param list column info
+ @param flags send flags. If Protocol::SEND_FORCE_COLUMN_INFO is set,
+ this function will return true
+ @return true, if column info must be sent to the client.
+ false otherwise
+*/
+
+static bool should_send_column_info(THD* thd, List<Item>* list, uint flags)
+{
+ if (!(thd->client_capabilities & MARIADB_CLIENT_CACHE_METADATA))
+ {
+ /* Client does not support abbreviated metadata.*/
+ return true;
+ }
+
+ if (!thd->cur_stmt)
+ {
+ /* Neither COM_PREPARE nor COM_EXECUTE run.*/
+ return true;
+ }
+
+ if (thd->spcont)
+ {
+ /* Always sent full metadata from inside the stored procedure.*/
+ return true;
+ }
+
+ if (flags & Protocol::SEND_FORCE_COLUMN_INFO)
+ return true;
+
+ auto &column_info_state= thd->cur_stmt->column_info_state;
+#ifndef DBUG_OFF
+ auto cmd= thd->get_command();
+#endif
+
+ DBUG_ASSERT(cmd == COM_STMT_EXECUTE || cmd == COM_STMT_PREPARE);
+ DBUG_ASSERT(cmd != COM_STMT_PREPARE || !column_info_state.initialized);
+
+ bool ret= metadata_columns_changed(column_info_state, thd, *list);
+
+ DBUG_ASSERT(cmd != COM_STMT_PREPARE || ret);
+ if (!ret)
+ thd->status_var.skip_metadata_count++;
+
+ return ret;
+}
+
+
/**
Send name and type of result to client.
@@ -941,30 +1173,44 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
Protocol_text prot(thd, thd->variables.net_buffer_length);
DBUG_ENTER("Protocol::send_result_set_metadata");
+ bool send_column_info= should_send_column_info(thd, list, flags);
+
if (flags & SEND_NUM_ROWS)
- { // Packet with number of elements
- uchar buff[MAX_INT_WIDTH];
+ {
+ /*
+ Packet with number of columns.
+
+ Will also have a 1 byte column info indicator, in case
+ MARIADB_CLIENT_CACHE_METADATA client capability is set.
+ */
+ uchar buff[MAX_INT_WIDTH+1];
uchar *pos= net_store_length(buff, list->elements);
+ if (thd->client_capabilities & MARIADB_CLIENT_CACHE_METADATA)
+ *pos++= (uchar)send_column_info;
+
DBUG_ASSERT(pos <= buff + sizeof(buff));
if (my_net_write(&thd->net, buff, (size_t) (pos-buff)))
DBUG_RETURN(1);
}
+ if (send_column_info)
+ {
#ifndef DBUG_OFF
- field_handlers= (const Type_handler**) thd->alloc(sizeof(field_handlers[0]) *
- list->elements);
+ field_handlers= (const Type_handler **) thd->alloc(
+ sizeof(field_handlers[0]) * list->elements);
#endif
- for (uint pos= 0; (item=it++); pos++)
- {
- prot.prepare_for_resend();
- if (prot.store_item_metadata(thd, item, pos))
- goto err;
- if (prot.write())
- DBUG_RETURN(1);
+ for (uint pos= 0; (item= it++); pos++)
+ {
+ prot.prepare_for_resend();
+ if (prot.store_item_metadata(thd, item, pos))
+ goto err;
+ if (prot.write())
+ DBUG_RETURN(1);
#ifndef DBUG_OFF
- field_handlers[pos]= item->type_handler();
+ field_handlers[pos]= item->type_handler();
#endif
+ }
}
if (flags & SEND_EOF)
@@ -1688,7 +1934,8 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
/* Send meta-data. */
- if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+ if (send_result_set_metadata(&out_param_lst,
+ SEND_NUM_ROWS | SEND_EOF | SEND_FORCE_COLUMN_INFO))
return TRUE;
/* Send data. */
diff --git a/sql/protocol.h b/sql/protocol.h
index eb11304a4d5..a1868342ab4 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -54,7 +54,7 @@ protected:
virtual bool net_store_data_cs(const uchar *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
virtual bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
- bool, bool);
+ bool);
virtual bool net_send_error_packet(THD *, uint, const char *, const char *);
#ifdef EMBEDDED_LIBRARY
char **next_field;
@@ -78,7 +78,7 @@ protected:
virtual bool send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush);
+ const char *message);
virtual bool send_eof(uint server_status, uint statement_warn_count);
@@ -93,7 +93,7 @@ public:
virtual ~Protocol() {}
void init(THD* thd_arg);
- enum { SEND_NUM_ROWS= 1, SEND_EOF= 2 };
+ enum { SEND_NUM_ROWS= 1, SEND_EOF= 2, SEND_FORCE_COLUMN_INFO= 4 };
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list);
bool send_result_set_row(List<Item> *row_items);
diff --git a/sql/records.cc b/sql/records.cc
index 900eacf5943..77ee3c65321 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -830,3 +830,32 @@ inline void SORT_INFO::unpack_addon_fields(uchar *buff)
field->unpack(field->ptr, buff + addonf->offset, buff_end, 0);
}
}
+
+
+/*
+ @brief
+ Read and unpack next record from a table
+
+ @details
+ The function first reads the next record from the table.
+ If a success then it unpacks the values to the base table fields.
+ This is used by SJM scan table to unpack the values of the materialized
+ table to the base table fields
+
+ @retval
+ 0 Record successfully read.
+ @retval
+ -1 There is no record to be read anymore.
+ >0 Error
+*/
+int read_record_func_for_rr_and_unpack(READ_RECORD *info)
+{
+ int error;
+ if ((error= info->read_record_func_and_unpack_calls(info)))
+ return error;
+
+ for (Copy_field *cp= info->copy_field; cp != info->copy_field_end; cp++)
+ (*cp->do_copy)(cp);
+
+ return error;
+}
diff --git a/sql/records.h b/sql/records.h
index 272bbd0d9b5..9bc1b98fde4 100644
--- a/sql/records.h
+++ b/sql/records.h
@@ -56,6 +56,7 @@ struct READ_RECORD
TABLE *table; /* Head-form */
Unlock_row_func unlock_row;
Read_func read_record_func;
+ Read_func read_record_func_and_unpack_calls;
THD *thd;
SQL_SELECT *select;
uint ref_length, reclength, rec_cache_size, error_offset;
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 3f3cb7677fc..2785a178e6e 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7546,13 +7546,10 @@ ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS
# MariaDB extra error numbers starts from 4000
skip-to-error-number 4000
-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_UNUSED_26 0A000
+ eng "This error never happens"
+ER_UNUSED_27
+ eng "This error never happens"
ER_WITH_COL_WRONG_LIST
eng "WITH column list and SELECT field list have different column counts"
ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE
@@ -7648,8 +7645,8 @@ ER_JSON_PATH_ARRAY
eng "JSON path should end with an array identifier in argument %d to function '%s'"
ER_JSON_ONE_OR_ALL
eng "Argument 2 to function '%s' must be "one" or "all"."
-ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE
- eng "CREATE TEMPORARY TABLE is not allowed with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE."
+ER_UNSUPPORTED_COMPRESSED_TABLE
+ eng "InnoDB refuses to write tables with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE."
ER_GEOJSON_INCORRECT
eng "Incorrect GeoJSON format specified for st_geomfromgeojson function."
ER_GEOJSON_TOO_FEW_POINTS
diff --git a/sql/slave.cc b/sql/slave.cc
index cfefe5746d4..da21df7d3c9 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -347,8 +347,7 @@ gtid_pos_table_creation(THD *thd, plugin_ref engine, LEX_CSTRING *table_name)
err= parser_state.init(thd, thd->query(), thd->query_length());
if (err)
goto end;
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
- FALSE, FALSE);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
if (unlikely(thd->is_error()))
err= 1;
/* The warning is relevant to 10.3 and earlier. */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index a0c23a08b12..1cc9a6a61d3 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -5039,15 +5039,6 @@ extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd)
return thd->get_command();
}
-
-extern "C" int thd_slave_thread(const MYSQL_THD thd)
-{
- return(thd->slave_thread);
-}
-
-
-
-
/* Returns high resolution timestamp for the start
of the current query. */
extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd)
@@ -5085,7 +5076,7 @@ thd_need_wait_reports(const MYSQL_THD thd)
}
/*
- Used by storage engines (currently TokuDB and InnoDB) to report that
+ Used by storage engines (currently InnoDB) to report that
one transaction THD is about to go to wait for a transactional lock held by
another transactions OTHER_THD.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1da82edc061..6bd08343c9e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -834,7 +834,6 @@ 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;
@@ -931,6 +930,12 @@ typedef struct system_status_var
ulong lost_connections;
ulong max_statement_time_exceeded;
/*
+ Number of times where column info was not
+ sent with prepared statement metadata.
+ */
+ ulong skip_metadata_count;
+
+ /*
Number of statements sent from the client
*/
ulong questions;
@@ -949,6 +954,7 @@ typedef struct system_status_var
ulonglong table_open_cache_hits;
ulonglong table_open_cache_misses;
ulonglong table_open_cache_overflows;
+ ulonglong send_metadata_skips;
double last_query_cost;
double cpu_time, busy_time;
uint32 threads_running;
@@ -1194,6 +1200,38 @@ public:
class Server_side_cursor;
+/*
+ Struct to catch changes in column metadata that is sent to client.
+ in the "result set metadata". Used to support
+ MARIADB_CLIENT_CACHE_METADATA.
+*/
+struct send_column_info_state
+{
+ /* Last client charset (affects metadata) */
+ CHARSET_INFO *last_charset= nullptr;
+
+ /* Checksum, only used to check changes if 'immutable' is false*/
+ uint32 checksum= 0;
+
+ /*
+ Column info can only be changed by PreparedStatement::reprepare()
+
+ There is a class of "weird" prepared statements like SELECT ? or SELECT @a
+ that are not immutable, and depend on input parameters or user variables
+ */
+ bool immutable= false;
+
+ bool initialized= false;
+
+ /* Used by PreparedStatement::reprepare()*/
+ void reset()
+ {
+ initialized= false;
+ checksum= 0;
+ }
+};
+
+
/**
@class Statement
@brief State of a single command executed against this connection.
@@ -1283,6 +1321,8 @@ public:
LEX_CSTRING db;
+ send_column_info_state column_info_state;
+
/* This is set to 1 of last call to send_result_to_client() was ok */
my_bool query_cache_is_applicable;
@@ -2386,6 +2426,8 @@ public:
/* Last created prepared statement */
Statement *last_stmt;
+ Statement *cur_stmt= 0;
+
inline void set_last_stmt(Statement *stmt)
{ last_stmt= (is_error() ? NULL : stmt); }
inline void clear_last_stmt() { last_stmt= NULL; }
@@ -6949,10 +6991,6 @@ public:
#define CF_SKIP_WSREP_CHECK 0
#endif /* WITH_WSREP */
-/**
- Do not allow it for COM_MULTI batch
-*/
-#define CF_NO_COM_MULTI (1U << 3)
/* Inline functions */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 3447032f193..9bf16220535 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -104,8 +104,137 @@ cmp_db_names(LEX_CSTRING *db1_name, const LEX_CSTRING *db2_name)
db1_name->str, db2_name->str) == 0));
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_rwlock_key key_rwlock_LOCK_dboptions;
+static PSI_rwlock_key key_rwlock_LOCK_dbnames;
+static PSI_rwlock_key key_rwlock_LOCK_rmdir;
+
+static PSI_rwlock_info all_database_names_rwlocks[]= {
+ {&key_rwlock_LOCK_dboptions, "LOCK_dboptions", PSI_FLAG_GLOBAL},
+ {&key_rwlock_LOCK_dbnames, "LOCK_dbnames", PSI_FLAG_GLOBAL},
+ {&key_rwlock_LOCK_rmdir, "LOCK_rmdir",PSI_FLAG_GLOBAL},
+};
+
+static void init_database_names_psi_keys(void)
+{
+ const char *category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_database_names_rwlocks);
+ PSI_server->register_rwlock(category, all_database_names_rwlocks, count);
+}
+#endif
+
+static mysql_rwlock_t rmdir_lock;
/*
+ Cache of C strings for existing database names.
+
+ The only use of it is to avoid repeated expensive
+ my_access() calls.
+
+ Provided operations are lookup, insert (after successfull my_access())
+ and clear (this is called whenever rmdir is called).
+*/
+struct dbname_cache_t
+{
+private:
+ Hash_set<LEX_STRING> m_set;
+ mysql_rwlock_t m_lock;
+
+ static uchar *get_key(const LEX_STRING *ls, size_t *sz, my_bool)
+ {
+ *sz= ls->length;
+ return (uchar *) ls->str;
+ }
+
+public:
+ dbname_cache_t()
+ : m_set(key_memory_dbnames_cache, table_alias_charset, 10, 0,
+ sizeof(char *), (my_hash_get_key) get_key, my_free, 0)
+ {
+ mysql_rwlock_init(key_rwlock_LOCK_dbnames, &m_lock);
+ }
+
+ bool contains(const char *s)
+ {
+ auto sz= strlen(s);
+ mysql_rwlock_rdlock(&m_lock);
+ bool ret= m_set.find(s, sz) != 0;
+ mysql_rwlock_unlock(&m_lock);
+ return ret;
+ }
+
+ void insert(const char *s)
+ {
+ auto len= strlen(s);
+ auto ls= (LEX_STRING *) my_malloc(key_memory_dbnames_cache,
+ sizeof(LEX_STRING) + strlen(s) + 1, 0);
+
+ if (!ls)
+ return;
+
+ ls->length= len;
+ ls->str= (char *) (ls + 1);
+
+ memcpy(ls->str, s, len + 1);
+ mysql_rwlock_wrlock(&m_lock);
+ bool found= m_set.find(s, len) != 0;
+ if (!found)
+ m_set.insert(ls);
+ mysql_rwlock_unlock(&m_lock);
+ if (found)
+ my_free(ls);
+ }
+
+ void clear()
+ {
+ mysql_rwlock_wrlock(&m_lock);
+ m_set.clear();
+ mysql_rwlock_unlock(&m_lock);
+ }
+
+ ~dbname_cache_t()
+ {
+ mysql_rwlock_destroy(&m_lock);
+ }
+};
+
+static dbname_cache_t* dbname_cache;
+
+static void dbname_cache_init()
+{
+ static MY_ALIGNED(16) char buf[sizeof(dbname_cache_t)];
+ DBUG_ASSERT(!dbname_cache);
+ dbname_cache= new (buf) dbname_cache_t;
+ mysql_rwlock_init(key_rwlock_LOCK_rmdir, &rmdir_lock);
+}
+
+static void dbname_cache_destroy()
+{
+ if (!dbname_cache)
+ return;
+
+ dbname_cache->~dbname_cache_t();
+ dbname_cache= 0;
+ mysql_rwlock_destroy(&rmdir_lock);
+}
+
+static int my_rmdir(const char *dir)
+{
+ auto ret= rmdir(dir);
+ if (ret)
+ return ret;
+ mysql_rwlock_wrlock(&rmdir_lock);
+ dbname_cache->clear();
+ mysql_rwlock_unlock(&rmdir_lock);
+ return 0;
+}
+
+ /*
Function we use in the creation of our hash to get key.
*/
@@ -131,7 +260,7 @@ static inline int write_to_binlog(THD *thd, const char *query, size_t q_len,
qinfo.db= db;
qinfo.db_len= (uint32)db_len;
return mysql_bin_log.write(&qinfo);
-}
+}
/*
@@ -145,26 +274,7 @@ void free_dbopt(void *dbopt)
my_free(dbopt);
}
-#ifdef HAVE_PSI_INTERFACE
-static PSI_rwlock_key key_rwlock_LOCK_dboptions;
-static PSI_rwlock_info all_database_names_rwlocks[]=
-{
- { &key_rwlock_LOCK_dboptions, "LOCK_dboptions", PSI_FLAG_GLOBAL}
-};
-
-static void init_database_names_psi_keys(void)
-{
- const char* category= "sql";
- int count;
-
- if (PSI_server == NULL)
- return;
-
- count= array_elements(all_database_names_rwlocks);
- PSI_server->register_rwlock(category, all_database_names_rwlocks, count);
-}
-#endif
/**
Initialize database option cache.
@@ -190,6 +300,7 @@ bool my_dboptions_cache_init(void)
table_alias_charset, 32, 0, 0, (my_hash_get_key)
dboptions_get_key, free_dbopt, 0);
}
+ dbname_cache_init();
return error;
}
@@ -205,6 +316,7 @@ void my_dboptions_cache_free(void)
{
dboptions_init= 0;
my_hash_free(&dboptions);
+ dbname_cache_destroy();
mysql_rwlock_destroy(&LOCK_dboptions);
}
}
@@ -692,7 +804,7 @@ mysql_create_db_internal(THD *thd, const LEX_CSTRING *db,
Restore things to beginning.
*/
path[path_len]= 0;
- if (rmdir(path) >= 0)
+ if (my_rmdir(path) >= 0)
DBUG_RETURN(-1);
/*
We come here when we managed to create the database, but not the option
@@ -1252,7 +1364,7 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
if (pos > path && pos[-1] == FN_LIBCHAR)
*--pos=0;
- if (unlikely(rmdir(path) < 0 && send_error))
+ if (unlikely(my_rmdir(path) < 0 && send_error))
{
my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
DBUG_RETURN(1);
@@ -1824,7 +1936,7 @@ bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db)
length= build_table_filename(path, sizeof(path)-1, new_db.str, "", "", 0);
if (length && path[length-1] == FN_LIBCHAR)
path[length-1]=0; // remove ending '\'
- rmdir(path);
+ my_rmdir(path);
goto exit;
}
@@ -1919,20 +2031,34 @@ exit:
TRUE The directory does not exist.
*/
+
bool check_db_dir_existence(const char *db_name)
{
char db_dir_path[FN_REFLEN + 1];
uint db_dir_path_len;
+ if (dbname_cache->contains(db_name))
+ return 0;
+
db_dir_path_len= build_table_filename(db_dir_path, sizeof(db_dir_path) - 1,
db_name, "", "", 0);
if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR)
db_dir_path[db_dir_path_len - 1]= 0;
- /* Check access. */
+ /*
+ Check access.
- return my_access(db_dir_path, F_OK);
+ The locking is to prevent creating permanent stale
+ entries for deleted databases, in case of
+ race condition with my_rmdir.
+ */
+ mysql_rwlock_rdlock(&rmdir_lock);
+ int ret= my_access(db_dir_path, F_OK);
+ if (!ret)
+ dbname_cache->insert(db_name);
+ mysql_rwlock_unlock(&rmdir_lock);
+ return ret;
}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 01f67476688..f3472cac330 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -360,6 +360,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning))
DBUG_RETURN(TRUE);
+ if (table_list->has_period())
+ {
+ if (!table_list->period_conditions.start.item->const_item()
+ || !table_list->period_conditions.end.item->const_item())
+ {
+ my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF");
+ DBUG_RETURN(true);
+ }
+ }
+
if (delete_history)
table->vers_write= false;
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index b3ef0d89a98..9af57ea6c01 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -302,7 +302,6 @@ void
Diagnostics_area::reset_diagnostics_area()
{
DBUG_ENTER("reset_diagnostics_area");
- m_skip_flush= FALSE;
#ifdef DBUG_OFF
m_can_overwrite_status= FALSE;
/** Don't take chances in production */
diff --git a/sql/sql_error.h b/sql/sql_error.h
index a0497af78cb..318d5076534 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -1022,14 +1022,6 @@ public:
{ DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK ||
m_status == DA_OK_BULK); return m_message; }
- bool skip_flush() const
- {
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK);
- return m_skip_flush;
- }
-
- void set_skip_flush()
- { m_skip_flush= TRUE; }
uint sql_errno() const
{
@@ -1215,9 +1207,6 @@ private:
/** Set to make set_error_status after set_{ok,eof}_status possible. */
bool m_can_overwrite_status;
- /** Skip flushing network buffer after writing OK (for COM_MULTI) */
- bool m_skip_flush;
-
/** Message buffer. Can be used by OK or ERROR status. */
char m_message[MYSQL_ERRMSG_SIZE];
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index f09d7140d3b..b20aa5f766d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -113,9 +113,7 @@
#include "wsrep_trans_observer.h" /* wsrep transaction hooks */
static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state,
- bool is_com_multi,
- bool is_next_command);
+ Parser_state *parser_state);
#endif /* WITH_WSREP */
/**
@@ -391,7 +389,7 @@ const LEX_CSTRING command_name[257]={
{ STRING_WITH_LEN("Slave_worker") }, //251
{ STRING_WITH_LEN("Slave_IO") }, //252
{ STRING_WITH_LEN("Slave_SQL") }, //253
- { STRING_WITH_LEN("Com_multi") }, //254
+ { 0, 0},
{ STRING_WITH_LEN("Error") } // Last command number 255
};
@@ -490,7 +488,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 | CF_NO_COM_MULTI;
+ server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK;
server_command_flags[COM_QUIT]= CF_SKIP_WSREP_CHECK;
server_command_flags[COM_PROCESS_INFO]= CF_SKIP_WSREP_CHECK;
@@ -519,7 +517,6 @@ void init_update_queries(void)
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_REGISTER_SLAVE]= 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));
@@ -958,7 +955,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
save_vio= thd->net.vio;
thd->net.vio= 0;
thd->clear_error(1);
- dispatch_command(COM_QUERY, thd, buf, (uint)len, FALSE, FALSE);
+ dispatch_command(COM_QUERY, thd, buf, (uint)len);
thd->client_capabilities= save_client_capabilities;
thd->net.vio= save_vio;
@@ -1084,7 +1081,7 @@ int bootstrap(MYSQL_FILE *file)
break;
}
- mysql_parse(thd, thd->query(), length, &parser_state, FALSE, FALSE);
+ mysql_parse(thd, thd->query(), length, &parser_state);
bootstrap_error= thd->is_error();
thd->protocol->end_statement();
@@ -1132,6 +1129,19 @@ void cleanup_items(Item *item)
DBUG_VOID_RETURN;
}
+#ifdef WITH_WSREP
+static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables)
+{
+ for (const TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ LEX_CSTRING db= table->db, tn= table->table_name;
+ if (get_table_category(&db, &tn) < TABLE_CATEGORY_INFORMATION)
+ return false;
+ }
+ return true;
+}
+#endif /* WITH_WSREP */
+#ifndef EMBEDDED_LIBRARY
static enum enum_server_command fetch_command(THD *thd, char *packet)
{
enum enum_server_command
@@ -1148,21 +1158,6 @@ static enum enum_server_command fetch_command(THD *thd, char *packet)
DBUG_RETURN(command);
}
-
-#ifdef WITH_WSREP
-static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables)
-{
- for (const TABLE_LIST *table= tables; table; table= table->next_global)
- {
- LEX_CSTRING db= table->db, tn= table->table_name;
- if (get_table_category(&db, &tn) < TABLE_CATEGORY_INFORMATION)
- return false;
- }
- return true;
-}
-#endif /* WITH_WSREP */
-#ifndef EMBEDDED_LIBRARY
-
/**
Read one command from connection and execute it (query or simple command).
This function is called in loop from thread function.
@@ -1351,7 +1346,7 @@ 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), FALSE, FALSE);
+ (uint) (packet_length-1));
DBUG_ASSERT(!thd->apc_target.is_enabled());
out:
@@ -1462,45 +1457,6 @@ static void wsrep_copy_query(THD *thd)
}
#endif /* WITH_WSREP */
-/**
- 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, size_t packet_length)
-{
- uint counter= 0;
- DBUG_ENTER("maria_multi_check");
- while (packet_length)
- {
- char *packet_start= packet;
- size_t subpacket_length= net_field_length((uchar **)&packet_start);
- size_t length_length= packet_start - packet;
- // length of command + 3 bytes where that length was stored
- DBUG_PRINT("info", ("sub-packet length: %zu + %zu command: %x",
- subpacket_length, length_length,
- packet_start[3]));
-
- if (subpacket_length == 0 ||
- (subpacket_length + length_length) > packet_length)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
- MYF(0));
- DBUG_RETURN(0);
- }
-
- counter++;
- packet= packet_start + subpacket_length;
- packet_length-= (subpacket_length + length_length);
- }
- DBUG_RETURN(counter);
-}
#if defined(WITH_ARIA_STORAGE_ENGINE)
@@ -1537,8 +1493,6 @@ public:
@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.
@@ -1552,8 +1506,7 @@ public:
COM_QUIT/COM_SHUTDOWN
*/
bool dispatch_command(enum enum_server_command command, THD *thd,
- char* packet, uint packet_length, bool is_com_multi,
- bool is_next_command)
+ char* packet, uint packet_length)
{
NET *net= &thd->net;
bool error= 0;
@@ -1634,14 +1587,6 @@ 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;
- if (is_com_multi)
- thd->get_stmt_da()->set_skip_flush();
- }
if (unlikely(thd->security_ctx->password_expired &&
command != COM_QUERY &&
@@ -1856,8 +1801,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (WSREP(thd))
{
if (wsrep_mysql_parse(thd, thd->query(), thd->query_length(),
- &parser_state,
- is_com_multi, is_next_command))
+ &parser_state))
{
WSREP_DEBUG("Deadlock error for: %s", thd->query());
mysql_mutex_lock(&thd->LOCK_thd_data);
@@ -1869,8 +1813,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
else
#endif /* WITH_WSREP */
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
- is_com_multi, is_next_command);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
! thd->is_error())
@@ -1954,8 +1897,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (WSREP(thd))
{
if (wsrep_mysql_parse(thd, beginning_of_next_stmt,
- length, &parser_state,
- is_com_multi, is_next_command))
+ length, &parser_state))
{
WSREP_DEBUG("Deadlock error for: %s", thd->query());
mysql_mutex_lock(&thd->LOCK_thd_data);
@@ -1968,8 +1910,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
else
#endif /* WITH_WSREP */
- mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
- is_com_multi, is_next_command);
+ mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
}
@@ -2020,13 +1961,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
packet= arg_end + 1;
- // thd->reset_for_next_command reset state => restore it
- if (is_next_command)
- {
- thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
- if (is_com_multi)
- thd->get_stmt_da()->set_skip_flush();
- }
lex_start(thd);
/* Must be before we init the table list. */
@@ -2314,84 +2248,6 @@ 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;
-
- {
- char *packet_start= packet;
- /* We have to store next length because it will be destroyed by '\0' */
- size_t next_subpacket_length= net_field_length((uchar **)&packet_start);
- size_t next_length_length= packet_start - packet;
- unsigned char *readbuff= net->buff;
-
- if (net_allocate_new_packet(net, thd, MYF(0)))
- break;
-
- PSI_statement_locker *save_locker= thd->m_statement_psi;
- sql_digest_state *save_digest= thd->m_digest;
- thd->m_statement_psi= NULL;
- thd->m_digest= NULL;
-
- while (packet_length)
- {
- current_com++;
- size_t subpacket_length= next_subpacket_length + next_length_length;
- size_t length_length= next_length_length;
- if (subpacket_length < packet_length)
- {
- packet_start= packet + subpacket_length;
- next_subpacket_length= net_field_length((uchar**)&packet_start);
- next_length_length= packet_start - (packet + subpacket_length);
- }
- /* safety like in do_command() */
- packet[subpacket_length]= '\0';
-
- enum enum_server_command subcommand=
- fetch_command(thd, (packet + length_length));
-
- if (server_command_flags[subcommand] & CF_NO_COM_MULTI)
- {
- my_error(ER_BAD_COMMAND_IN_MULTI, MYF(0),
- command_name[subcommand].str);
- goto com_multi_end;
- }
-
- if (dispatch_command(subcommand, thd, packet + (1 + length_length),
- (uint)(subpacket_length - (1 + length_length)), TRUE,
- (current_com != counter)))
- {
- DBUG_ASSERT(thd->is_error());
- goto com_multi_end;
- }
-
- DBUG_ASSERT(subpacket_length <= packet_length);
- packet+= subpacket_length;
- packet_length-= (uint)subpacket_length;
- }
-
-com_multi_end:
- thd->m_statement_psi= save_locker;
- thd->m_digest= save_digest;
-
- /* release old buffer */
- net_flush(net);
- 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
@@ -2460,11 +2316,8 @@ dispatch_end:
thd_proc_info(thd, "Updating status");
/* Finalize server status flags after executing a command. */
thd->update_server_status();
- if (command != COM_MULTI)
- {
- thd->protocol->end_statement();
- query_cache_end_of_result(thd);
- }
+ thd->protocol->end_statement();
+ query_cache_end_of_result(thd);
}
if (drop_more_results)
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
@@ -2492,8 +2345,7 @@ dispatch_end:
thd->m_statement_psi= NULL;
thd->m_digest= NULL;
- if (!is_com_multi)
- thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+ thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
thd->reset_kill_query(); /* Ensure that killed_errmsg is released */
/*
@@ -7831,9 +7683,7 @@ static void wsrep_prepare_for_autocommit_retry(THD* thd,
}
static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state,
- bool is_com_multi,
- bool is_next_command)
+ Parser_state *parser_state)
{
bool is_autocommit=
!thd->in_multi_stmt_transaction_mode() &&
@@ -7842,7 +7692,7 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
do
{
retry_autocommit= false;
- mysql_parse(thd, rawbuf, length, parser_state, is_com_multi, is_next_command);
+ mysql_parse(thd, rawbuf, length, parser_state);
/*
Convert all ER_QUERY_INTERRUPTED errors to ER_LOCK_DEADLOCK
@@ -7948,15 +7798,10 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
@param thd Current thread
@param rawbuf Begining of the query text
@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,
- bool is_com_multi,
- bool is_next_command)
+ Parser_state *parser_state)
{
DBUG_ENTER("mysql_parse");
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on_MYSQLparse(););
@@ -7980,12 +7825,6 @@ 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 (is_com_multi)
- thd->get_stmt_da()->set_skip_flush();
- }
if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
{
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 0e5cc7f4bad..37861cf224f 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -91,8 +91,7 @@ bool is_log_table_write_query(enum enum_sql_command command);
bool alloc_query(THD *thd, const char *packet, size_t packet_length);
void mysql_init_select(LEX *lex);
void mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state, bool is_com_multi,
- bool is_next_command);
+ Parser_state *parser_state);
bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel);
void create_select_for_variable(THD *thd, LEX_CSTRING *var_name);
void create_table_set_open_action_and_adjust_tables(LEX *lex);
@@ -103,8 +102,7 @@ int bootstrap(MYSQL_FILE *file);
int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
- char* packet, uint packet_length,
- bool is_com_multi, bool is_next_command);
+ char* packet, uint packet_length);
void log_slow_statement(THD *thd);
bool append_file_to_dir(THD *thd, const char **filename_ptr,
const LEX_CSTRING *table_name);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 7dd025f03e4..285846558d2 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -334,9 +334,13 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
error= my_net_write(net, buff, sizeof(buff));
if (stmt->param_count && likely(!error))
{
- error= thd->protocol_text.send_result_set_metadata((List<Item> *)
- &stmt->lex->param_list,
- Protocol::SEND_EOF);
+ /*
+ Force the column info to be written
+ (in this case PS parameter type info).
+ */
+ error= thd->protocol_text.send_result_set_metadata(
+ (List<Item> *)&stmt->lex->param_list,
+ Protocol::SEND_EOF | Protocol::SEND_FORCE_COLUMN_INFO);
}
if (likely(!error))
@@ -3444,10 +3448,15 @@ static void mysql_stmt_execute_common(THD *thd,
thd->protocol= &thd->protocol_binary;
MYSQL_EXECUTE_PS(thd->m_statement_psi, stmt->m_prepared_stmt);
+ auto save_cur_stmt= thd->cur_stmt;
+ thd->cur_stmt= stmt;
+
if (!bulk_op)
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
else
stmt->execute_bulk_loop(&expanded_query, open_cursor, packet, packet_end);
+
+ thd->cur_stmt= save_cur_stmt;
thd->protocol= save_protocol;
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
@@ -4206,6 +4215,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
old_stmt_arena= thd->stmt_arena;
thd->stmt_arena= this;
+ auto save_cur_stmt= thd->cur_stmt;
+ thd->cur_stmt= this;
Parser_state parser_state;
if (parser_state.init(thd, thd->query(), thd->query_length()))
@@ -4213,6 +4224,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->restore_backup_statement(this, &stmt_backup);
thd->restore_active_arena(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
+ thd->cur_stmt = save_cur_stmt;
DBUG_RETURN(TRUE);
}
@@ -4222,6 +4234,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex_start(thd);
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_PREPARE;
+
error= (parse_sql(thd, & parser_state, NULL) ||
thd->is_error() ||
init_param_array(this));
@@ -4297,6 +4310,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
cleanup_stmt();
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
+ thd->cur_stmt= save_cur_stmt;
if (likely(error == 0))
{
@@ -4755,6 +4769,7 @@ Prepared_statement::reprepare()
it's failed, we need to return all the warnings to the user.
*/
thd->get_stmt_da()->clear_warning_info(thd->query_id);
+ column_info_state.reset();
}
else
{
@@ -5397,7 +5412,7 @@ protected:
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
- bool, bool);
+ bool);
bool net_send_error_packet(THD *, uint, const char *, const char *);
bool begin_dataset();
bool begin_dataset(THD *thd, uint numfields);
@@ -5560,7 +5575,7 @@ bool Protocol_local::net_store_data_cs(const uchar *from, size_t length,
bool
Protocol_local::net_send_ok(THD *thd,
uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong id, const char *message, bool, bool)
+ ulonglong affected_rows, ulonglong id, const char *message, bool)
{
DBUG_ENTER("emb_net_send_ok");
MYSQL_DATA *data;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 808d49d4067..caaf6d2f3e3 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -3869,6 +3869,16 @@ JOIN::add_sorting_to_table(JOIN_TAB *tab, ORDER *order)
tab->select);
if (!tab->filesort)
return true;
+
+ TABLE *table= tab->table;
+ if ((tab == join_tab + const_tables) &&
+ table->pos_in_table_list &&
+ table->pos_in_table_list->is_sjm_scan_table())
+ {
+ tab->filesort->set_all_read_bits= TRUE;
+ tab->filesort->unpack= unpack_to_base_table_fields;
+ }
+
/*
Select was moved to filesort->select to force join_init_read_record to use
sorted result instead of reading table through select.
@@ -7916,7 +7926,8 @@ best_access_path(JOIN *join,
access is to use the same index IDX, with the same or more key parts.
(note: it is not clear how this rule is/should be extended to
index_merge quick selects). Also if we have a hash join we prefer that
- over a table scan
+ over a table scan. This heuristic doesn't apply if the quick select
+ uses the group-by min-max optimization.
(3) See above note about InnoDB.
(4) NOT ("FORCE INDEX(...)" is used for table and there is 'ref' access
path, but there is no quick select)
@@ -7934,7 +7945,9 @@ best_access_path(JOIN *join,
Json_writer_object trace_access_scan(thd);
if ((records >= s->found_records || best > s->read_time) && // (1)
!(best_key && best_key->key == MAX_KEY) && // (2)
- !(s->quick && best_key && s->quick->index == best_key->key && // (2)
+ !(s->quick &&
+ s->quick->get_type() != QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX && // (2)
+ best_key && s->quick->index == best_key->key && // (2)
best_max_key_part >= s->table->opt_range[best_key->key].key_parts) &&// (2)
!((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
@@ -14239,37 +14252,8 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
can be used without tmp. table.
*/
bool can_subst_to_first_table= false;
- bool first_is_in_sjm_nest= false;
- if (first_is_base_table)
- {
- TABLE_LIST *tbl_for_first=
- join->join_tab[join->const_tables].table->pos_in_table_list;
- first_is_in_sjm_nest= tbl_for_first->sj_mat_info &&
- tbl_for_first->sj_mat_info->is_used;
- }
- /*
- Currently we do not employ the optimization that uses multiple
- equalities for ORDER BY to remove tmp table in the case when
- the first table happens to be the result of materialization of
- a semi-join nest ( <=> first_is_in_sjm_nest == true).
-
- When a semi-join nest is materialized and scanned to look for
- possible matches in the remaining tables for every its row
- the fields from the result of materialization are copied
- into the record buffers of tables from the semi-join nest.
- So these copies are used to access the remaining tables rather
- than the fields from the result of materialization.
-
- Unfortunately now this so-called 'copy back' technique is
- supported only if the rows are scanned with the rr_sequential
- function, but not with other rr_* functions that are employed
- when the result of materialization is required to be sorted.
-
- TODO: either to support 'copy back' technique for the above case,
- or to get rid of this technique altogether.
- */
if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_ORDERBY_EQ_PROP) &&
- first_is_base_table && !first_is_in_sjm_nest &&
+ first_is_base_table &&
order->item[0]->real_item()->type() == Item::FIELD_ITEM &&
join->cond_equal)
{
@@ -20230,19 +20214,6 @@ do_select(JOIN *join, Procedure *procedure)
}
-int rr_sequential_and_unpack(READ_RECORD *info)
-{
- int error;
- if (unlikely((error= rr_sequential(info))))
- return error;
-
- for (Copy_field *cp= info->copy_field; cp != info->copy_field_end; cp++)
- (*cp->do_copy)(cp);
-
- return error;
-}
-
-
/**
@brief
Instantiates temporary table
@@ -21538,6 +21509,8 @@ bool test_if_use_dynamic_range_scan(JOIN_TAB *join_tab)
int join_init_read_record(JOIN_TAB *tab)
{
+ bool need_unpacking= FALSE;
+ JOIN *join= tab->join;
/*
Note: the query plan tree for the below operations is constructed in
save_agg_explain_data.
@@ -21545,6 +21518,12 @@ int join_init_read_record(JOIN_TAB *tab)
if (tab->distinct && tab->remove_duplicates()) // Remove duplicates.
return 1;
+ if (join->top_join_tab_count != join->const_tables)
+ {
+ TABLE_LIST *tbl= tab->table->pos_in_table_list;
+ need_unpacking= tbl ? tbl->is_sjm_scan_table() : FALSE;
+ }
+
tab->build_range_rowid_filter_if_needed();
if (tab->filesort && tab->sort_table()) // Sort table.
@@ -21552,6 +21531,11 @@ int join_init_read_record(JOIN_TAB *tab)
DBUG_EXECUTE_IF("kill_join_init_read_record",
tab->join->thd->set_killed(KILL_QUERY););
+
+
+ if (!tab->preread_init_done && tab->preread_init())
+ return 1;
+
if (tab->select && tab->select->quick && tab->select->quick->reset())
{
/* Ensures error status is propagated back to client */
@@ -21562,19 +21546,7 @@ int join_init_read_record(JOIN_TAB *tab)
/* make sure we won't get ER_QUERY_INTERRUPTED from any code below */
DBUG_EXECUTE_IF("kill_join_init_read_record",
tab->join->thd->reset_killed(););
- if (!tab->preread_init_done && tab->preread_init())
- return 1;
-
- if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select, tab->filesort_result, 1,1, FALSE))
- return 1;
- return tab->read_record.read_record();
-}
-
-int
-join_read_record_no_init(JOIN_TAB *tab)
-{
Copy_field *save_copy, *save_copy_end;
/*
@@ -21584,12 +21556,19 @@ join_read_record_no_init(JOIN_TAB *tab)
save_copy= tab->read_record.copy_field;
save_copy_end= tab->read_record.copy_field_end;
- init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select, tab->filesort_result, 1, 1, FALSE);
+ if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
+ tab->select, tab->filesort_result, 1, 1, FALSE))
+ return 1;
tab->read_record.copy_field= save_copy;
tab->read_record.copy_field_end= save_copy_end;
- tab->read_record.read_record_func= rr_sequential_and_unpack;
+
+ if (need_unpacking)
+ {
+ tab->read_record.read_record_func_and_unpack_calls=
+ tab->read_record.read_record_func;
+ tab->read_record.read_record_func = read_record_func_for_rr_and_unpack;
+ }
return tab->read_record.read_record();
}
@@ -29467,6 +29446,20 @@ void JOIN::init_join_cache_and_keyread()
}
+/*
+ @brief
+ Unpack temp table fields to base table fields.
+*/
+
+void unpack_to_base_table_fields(TABLE *table)
+{
+ JOIN_TAB *tab= table->reginfo.join_tab;
+ for (Copy_field *cp= tab->read_record.copy_field;
+ cp != tab->read_record.copy_field_end; cp++)
+ (*cp->do_copy)(cp);
+}
+
+
/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index fad008ebcb9..f39a70cc3e1 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -223,7 +223,7 @@ typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool);
Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab);
int rr_sequential(READ_RECORD *info);
-int rr_sequential_and_unpack(READ_RECORD *info);
+int read_record_func_for_rr_and_unpack(READ_RECORD *info);
Item *remove_pushed_top_conjuncts(THD *thd, Item *cond);
Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
COND_EQUAL **cond_eq,
@@ -2353,7 +2353,6 @@ create_virtual_tmp_table(THD *thd, Field *field)
int test_if_item_cache_changed(List<Cached_item> &list);
int join_init_read_record(JOIN_TAB *tab);
-int join_read_record_no_init(JOIN_TAB *tab);
void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key);
inline Item * and_items(THD *thd, Item* cond, Item *item)
{
@@ -2411,6 +2410,7 @@ int print_explain_message_line(select_result_sink *result,
void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table,
key_map possible_keys);
+void unpack_to_base_table_fields(TABLE *table);
/****************************************************************************
Temporary table support for SQL Runtime
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index a474d7c25e9..3b23328183c 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -559,6 +559,7 @@ public:
Addon_fields *addon_fields; // Descriptors for companion fields.
Sort_keys *sort_keys;
bool using_pq;
+ bool set_all_read_bits;
uchar *unique_buff;
bool not_killable;
@@ -579,6 +580,8 @@ public:
}
void init_for_filesort(uint sortlen, TABLE *table,
ha_rows maxrows, bool sort_positions);
+
+ void (*unpack)(TABLE *);
/// Enables the packing of addons if possible.
void try_to_pack_addons(ulong max_length_for_sort_data);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0b6f099b1b0..99c3d879f04 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11752,6 +11752,16 @@ table_primary_derived:
if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
MYSQL_YYABORT;
}
+/* Start SQL_MODE_ORACLE_SPECIFIC
+ | subquery
+ opt_for_system_time_clause
+ {
+ LEX_CSTRING alias;
+ if ($1->make_unique_derived_name(thd, &alias) ||
+ !($$= Lex->parsed_derived_table($1->master_unit(), $2, &alias)))
+ MYSQL_YYABORT;
+ }
+End SQL_MODE_ORACLE_SPECIFIC */
;
opt_outer:
diff --git a/sql/table.h b/sql/table.h
index 83e8b69b5e3..48a08c24de5 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1137,7 +1137,7 @@ struct TABLE_SHARE
bool write_frm_image(const uchar *frm_image, size_t frm_length);
bool write_par_image(const uchar *par_image, size_t par_length);
- /* Only used by tokudb */
+ /* Only used by S3 */
bool write_frm_image(void)
{ return frm_image ? write_frm_image(frm_image->str, frm_image->length) : 0; }
@@ -2777,6 +2777,7 @@ struct TABLE_LIST
*/
const char *get_table_name() const { return view != NULL ? view_name.str : table_name.str; }
bool is_active_sjm();
+ bool is_sjm_scan_table();
bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); }
st_select_lex_unit *get_unit();
st_select_lex *get_single_select();
diff --git a/sql/threadpool.h b/sql/threadpool.h
index 27da872c5cc..11132b3bf95 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -37,6 +37,8 @@ extern uint threadpool_mode; /* Thread pool implementation , windows or generic
#define DEFAULT_THREADPOOL_STALL_LIMIT 500U
struct TP_connection;
+struct st_vio;
+
extern void tp_callback(TP_connection *c);
extern void tp_timeout_handler(TP_connection *c);
@@ -113,7 +115,7 @@ struct TP_connection
virtual void wait_begin(int type)= 0;
virtual void wait_end() = 0;
-
+ IF_WIN(virtual,) void init_vio(st_vio *){};
};
@@ -134,6 +136,7 @@ struct TP_pool
};
#ifdef _WIN32
+
struct TP_pool_win:TP_pool
{
TP_pool_win();
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index e8eb0dcc29d..168579d984b 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -28,6 +28,10 @@
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
+#ifdef _WIN32
+#include "threadpool_winsockets.h"
+#endif
+
/* Threadpool parameters */
uint threadpool_min_threads;
@@ -48,7 +52,7 @@ TP_STATISTICS tp_stats;
static void threadpool_remove_connection(THD *thd);
static int threadpool_process_request(THD *thd);
-static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data);
+static THD* threadpool_add_connection(CONNECT *connect, TP_connection *c);
extern bool do_command(THD*);
@@ -220,7 +224,7 @@ error:
}
-static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
+static THD *threadpool_add_connection(CONNECT *connect, TP_connection *c)
{
THD *thd= NULL;
@@ -243,11 +247,10 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
}
delete connect;
- thd->event_scheduler.data = scheduler_data;
+ thd->event_scheduler.data= c;
server_threads.insert(thd);
thd->set_mysys_var(mysys_var);
-
/* Login. */
thread_attach(thd);
re_init_net_server_extension(thd);
@@ -261,6 +264,8 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
if (thd_prepare_connection(thd))
goto end;
+ c->init_vio(thd->net.vio);
+
/*
Check if THD is ok, as prepare_new_connection_state()
can fail, for example if init command failed.
@@ -405,6 +410,9 @@ static bool tp_init()
pool= 0;
return true;
}
+#ifdef _WIN32
+ init_win_aio_buffers(max_connections);
+#endif
return false;
}
@@ -506,6 +514,9 @@ static void tp_wait_end(THD *thd)
static void tp_end()
{
delete pool;
+#ifdef _WIN32
+ destroy_win_aio_buffers();
+#endif
}
static void tp_post_kill_notification(THD *thd)
diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc
index b6bb47e8f29..709bb95eb98 100644
--- a/sql/threadpool_generic.cc
+++ b/sql/threadpool_generic.cc
@@ -29,8 +29,8 @@
#include <sql_plist.h>
#include <threadpool.h>
#include <algorithm>
-
-#ifdef HAVE_IOCP
+#ifdef _WIN32
+#include "threadpool_winsockets.h"
#define OPTIONAL_IO_POLL_READ_PARAM this
#else
#define OPTIONAL_IO_POLL_READ_PARAM 0
@@ -347,7 +347,7 @@ static void* native_event_get_userdata(native_event *event)
return event->portev_user;
}
-#elif defined(HAVE_IOCP)
+#elif defined(_WIN32)
static TP_file_handle io_poll_create()
@@ -358,29 +358,8 @@ static TP_file_handle io_poll_create()
int io_poll_start_read(TP_file_handle pollfd, TP_file_handle fd, void *, void *opt)
{
- static char c;
- TP_connection_generic *con= (TP_connection_generic *)opt;
- OVERLAPPED *overlapped= &con->overlapped;
- if (con->vio_type == VIO_TYPE_NAMEDPIPE)
- {
- if (ReadFile(fd, &c, 0, NULL, overlapped))
- return 0;
- }
- else
- {
- WSABUF buf;
- buf.buf= &c;
- buf.len= 0;
- DWORD flags=0;
-
- if (WSARecv((SOCKET)fd, &buf, 1,NULL, &flags,overlapped, NULL) == 0)
- return 0;
- }
-
- if (GetLastError() == ERROR_IO_PENDING)
- return 0;
-
- return 1;
+ auto c= (TP_connection_generic *) opt;
+ return (int) c->win_sock.begin_read();
}
@@ -429,20 +408,33 @@ int io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
}
-int io_poll_wait(TP_file_handle pollfd, native_event *events, int maxevents, int timeout_ms)
+static void *native_event_get_userdata(native_event *event)
{
- ULONG n;
- BOOL ok = GetQueuedCompletionStatusEx(pollfd, events,
- maxevents, &n, timeout_ms, FALSE);
-
- return ok ? (int)n : -1;
+ return (void *) event->lpCompletionKey;
}
-
-static void* native_event_get_userdata(native_event *event)
+int io_poll_wait(TP_file_handle pollfd, native_event *events, int maxevents,
+ int timeout_ms)
{
- return (void *)event->lpCompletionKey;
+ ULONG n;
+ if (!GetQueuedCompletionStatusEx(pollfd, events, maxevents, &n, timeout_ms, FALSE))
+ return -1;
+
+ /* Update win_sock with number of bytes read.*/
+ for (ULONG i= 0; i < n; i++)
+ {
+ auto ev= &events[i];
+ auto c= (TP_connection_generic *) native_event_get_userdata(ev);
+ /* null userdata zero means shutdown (see PostQueuedCompletionStatus() usage*/
+ if (c)
+ {
+ c->win_sock.end_read(ev->dwNumberOfBytesTransferred, 0);
+ }
+ }
+
+ return (int) n;
}
+
#endif
@@ -1005,7 +997,7 @@ void thread_group_destroy(thread_group_t *thread_group)
io_poll_close(thread_group->pollfd);
thread_group->pollfd= INVALID_HANDLE_VALUE;
}
-#ifndef HAVE_IOCP
+#ifndef _WIN32
for(int i=0; i < 2; i++)
{
if(thread_group->shutdown_pipe[i] != -1)
@@ -1052,7 +1044,7 @@ static int wake_thread(thread_group_t *thread_group,bool due_to_stall)
*/
static int wake_listener(thread_group_t *thread_group)
{
-#ifndef HAVE_IOCP
+#ifndef _WIN32
if (pipe(thread_group->shutdown_pipe))
{
return -1;
@@ -1398,12 +1390,6 @@ TP_connection_generic::TP_connection_generic(CONNECT *c):
bound_to_poll_descriptor(false),
waiting(false),
fix_group(false)
-#ifdef HAVE_IOCP
-, overlapped()
-#endif
-#ifdef _WIN32
-, vio_type(c->vio_type)
-#endif
{
DBUG_ASSERT(c->vio_type != VIO_CLOSED);
diff --git a/sql/threadpool_generic.h b/sql/threadpool_generic.h
index acf5ec6978b..b7a35b7cbf0 100644
--- a/sql/threadpool_generic.h
+++ b/sql/threadpool_generic.h
@@ -23,6 +23,7 @@
#ifdef _WIN32
#include <windows.h>
+#include "threadpool_winsockets.h"
/* AIX may define this, too ?*/
#define HAVE_IOCP
#endif
@@ -75,11 +76,11 @@ struct TP_connection_generic :public TP_connection
TP_connection_generic(CONNECT* c);
~TP_connection_generic();
- virtual int init() { return 0; };
- virtual void set_io_timeout(int sec);
- virtual int start_io();
- virtual void wait_begin(int type);
- virtual void wait_end();
+ int init() override { return 0; }
+ void set_io_timeout(int sec) override;
+ int start_io() override;
+ void wait_begin(int type) override;
+ void wait_end() override;
thread_group_t* thread_group;
TP_connection_generic* next_in_queue;
@@ -90,12 +91,12 @@ struct TP_connection_generic :public TP_connection
bool bound_to_poll_descriptor;
int waiting;
bool fix_group;
-#ifdef HAVE_IOCP
- OVERLAPPED overlapped;
-#endif
#ifdef _WIN32
- enum_vio_type vio_type;
+ win_aiosocket win_sock{};
+ void init_vio(st_vio *vio) override
+ { win_sock.init(vio);}
#endif
+
};
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index 6003b06bc7b..df8a6c216a3 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -30,6 +30,9 @@
#include <debug_sync.h>
#include <threadpool.h>
#include <windows.h>
+#include <set_var.h>
+
+#include "threadpool_winsockets.h"
/* Log a warning */
static void tp_log_warning(const char *msg, const char *fct)
@@ -43,8 +46,6 @@ static PTP_POOL pool;
static TP_CALLBACK_ENVIRON callback_environ;
static DWORD fls;
-static bool skip_completion_port_on_success = false;
-
PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ()
{
return pool? &callback_environ: 0;
@@ -83,22 +84,21 @@ struct TP_connection_win:public TP_connection
public:
TP_connection_win(CONNECT*);
~TP_connection_win();
- virtual int init();
- virtual int start_io();
- virtual void set_io_timeout(int sec);
- virtual void wait_begin(int type);
- virtual void wait_end();
-
- ulonglong timeout;
- enum_vio_type vio_type;
- HANDLE handle;
- OVERLAPPED overlapped;
- PTP_CALLBACK_INSTANCE callback_instance;
- PTP_IO io;
- PTP_TIMER timer;
- PTP_WORK work;
- bool long_callback;
-
+ int init() override;
+ void init_vio(st_vio *vio) override;
+ int start_io() override;
+ void set_io_timeout(int sec) override;
+ void wait_begin(int type) override;
+ void wait_end() override;
+
+ ulonglong timeout=ULLONG_MAX;
+ OVERLAPPED overlapped{};
+ PTP_CALLBACK_INSTANCE callback_instance{};
+ PTP_IO io{};
+ PTP_TIMER timer{};
+ PTP_WORK work{};
+ bool long_callback{};
+ win_aiosocket sock;
};
struct TP_connection *new_TP_connection(CONNECT *connect)
@@ -125,120 +125,50 @@ void TP_pool_win::add(TP_connection *c)
}
}
+#define CHECK_ALLOC_ERROR(op) \
+ do \
+ { \
+ if (!(op)) \
+ { \
+ tp_log_warning("Allocation failed", #op); \
+ } \
+ } while (0)
TP_connection_win::TP_connection_win(CONNECT *c) :
- TP_connection(c),
- timeout(ULONGLONG_MAX),
- callback_instance(0),
- io(0),
- timer(0),
- work(0)
+ TP_connection(c)
{
-}
+ /* Assign io completion callback */
+ HANDLE h= c->vio_type == VIO_TYPE_NAMEDPIPE ? c->pipe
+ : (HANDLE)mysql_socket_getfd(c->sock);
-#define CHECK_ALLOC_ERROR(op) if (!(op)) {tp_log_warning("Allocation failed", #op); DBUG_ASSERT(0); return -1; }
+ CHECK_ALLOC_ERROR(io=CreateThreadpoolIo(h, io_completion_callback, this, &callback_environ));
+ CHECK_ALLOC_ERROR(timer= CreateThreadpoolTimer(timer_callback, this, &callback_environ));
+ CHECK_ALLOC_ERROR(work= CreateThreadpoolWork(work_callback, this, &callback_environ));
+}
int TP_connection_win::init()
{
-
- memset(&overlapped, 0, sizeof(OVERLAPPED));
- switch ((vio_type = connect->vio_type))
- {
- case VIO_TYPE_SSL:
- case VIO_TYPE_TCPIP:
- handle= (HANDLE) mysql_socket_getfd(connect->sock);
- break;
- case VIO_TYPE_NAMEDPIPE:
- handle= connect->pipe;
- break;
- default:
- abort();
- }
-
-
- /* Performance tweaks (s. MSDN documentation)*/
- UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE;
- if (skip_completion_port_on_success)
- {
- flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
- }
- (void)SetFileCompletionNotificationModes(handle, flags);
- /* Assign io completion callback */
- CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ));
- CHECK_ALLOC_ERROR(timer= CreateThreadpoolTimer(timer_callback, this, &callback_environ));
- CHECK_ALLOC_ERROR(work= CreateThreadpoolWork(work_callback, this, &callback_environ));
- return 0;
+ return !io || !timer || !work ;
}
+void TP_connection_win::init_vio(st_vio* vio)
+{
+ sock.init(vio);
+}
/*
Start asynchronous read
*/
int TP_connection_win::start_io()
{
- DWORD num_bytes = 0;
- static char c;
- WSABUF buf;
- buf.buf= &c;
- buf.len= 0;
- DWORD flags=0;
- DWORD last_error= 0;
-
- int retval;
StartThreadpoolIo(io);
-
- if (vio_type == VIO_TYPE_TCPIP || vio_type == VIO_TYPE_SSL)
- {
- /* Start async io (sockets). */
- if (WSARecv((SOCKET)handle , &buf, 1, &num_bytes, &flags,
- &overlapped, NULL) == 0)
- {
- retval= last_error= 0;
- }
- else
- {
- retval= -1;
- last_error= WSAGetLastError();
- }
- }
- else
- {
- /* Start async io (named pipe) */
- if (ReadFile(handle, &c, 0, &num_bytes,&overlapped))
- {
- retval= last_error= 0;
- }
- else
- {
- retval= -1;
- last_error= GetLastError();
- }
- }
-
- if (retval == 0 || last_error == ERROR_MORE_DATA)
- {
- /*
- IO successfully finished (synchronously).
- If skip_completion_port_on_success is set, we need to handle it right
- here, because completion callback would not be executed by the pool.
- */
- if (skip_completion_port_on_success)
- {
- CancelThreadpoolIo(io);
- io_completion_callback(callback_instance, this, &overlapped, last_error,
- num_bytes, io);
- }
- return 0;
- }
-
- if (last_error == ERROR_IO_PENDING)
+ if (sock.begin_read())
{
- return 0;
+ /* Some error occurred */
+ CancelThreadpoolIo(io);
+ return -1;
}
-
- /* Some error occurred */
- CancelThreadpoolIo(io);
- return -1;
+ return 0;
}
/*
@@ -305,7 +235,7 @@ void tp_win_callback_prolog()
{
/* Running in new worker thread*/
FlsSetValue(fls, (void *)1);
- statistic_increment(thread_created, &LOCK_status);
+ thread_created++;
tp_stats.num_worker_threads++;
my_thread_init();
}
@@ -350,6 +280,10 @@ static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io)
{
TP_connection_win *c= (TP_connection_win *)context;
+
+ /* How many bytes were preread into read buffer */
+ c->sock.end_read((ULONG)nbytes, io_result);
+
/*
Execute high priority connections immediately.
'Yield' in case of low priority connections, i.e SubmitThreadpoolWork (with the same callback)
@@ -412,12 +346,24 @@ int TP_pool_win::init()
InitializeThreadpoolEnvironment(&callback_environ);
SetThreadpoolCallbackPool(&callback_environ, pool);
- if (threadpool_max_threads)
+ if (IS_SYSVAR_AUTOSIZE(&threadpool_max_threads))
+ {
+ /*
+ Nr 500 comes from Microsoft documentation,
+ there is no API for GetThreadpoolThreadMaxThreads()
+ */
+ SYSVAR_AUTOSIZE(threadpool_max_threads,500);
+ }
+ else
{
SetThreadpoolThreadMaximum(pool, threadpool_max_threads);
}
- if (threadpool_min_threads)
+ if (IS_SYSVAR_AUTOSIZE(&threadpool_min_threads))
+ {
+ SYSVAR_AUTOSIZE(threadpool_min_threads,1);
+ }
+ else
{
if (!SetThreadpoolThreadMinimum(pool, threadpool_min_threads))
{
@@ -426,6 +372,18 @@ int TP_pool_win::init()
}
}
+
+ if (IS_SYSVAR_AUTOSIZE(&global_system_variables.threadpool_priority))
+ {
+ /*
+ There is a notable overhead for "auto" priority implementation,
+ use "high" which handles socket IO callbacks as they come
+ without rescheduling to work queue.
+ */
+ SYSVAR_AUTOSIZE(global_system_variables.threadpool_priority,
+ TP_PRIORITY_HIGH);
+ }
+
TP_POOL_STACK_INFORMATION stackinfo;
stackinfo.StackCommit = 0;
stackinfo.StackReserve = (SIZE_T)my_thread_stack_size;
diff --git a/sql/threadpool_winsockets.cc b/sql/threadpool_winsockets.cc
new file mode 100644
index 00000000000..6b4758a451f
--- /dev/null
+++ b/sql/threadpool_winsockets.cc
@@ -0,0 +1,259 @@
+/* Copyright (C) 2012 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+ */
+
+#include <winsock2.h>
+#include <my_global.h>
+#include <violite.h>
+#include "threadpool_winsockets.h"
+#include <algorithm>
+#include <vector>
+#include <mutex>
+
+/*
+ A cache for IO buffers for asynchronous socket(or named pipe) reads.
+
+ Considerations on Windows : since Windows locks the AIO buffers in physical memory,
+ it is important that these buffers are compactly allocated.
+ We try to to prevent any kinds of memory fragmentation
+
+ A relatively small region (at most 1MB) is allocated, for equally sized smallish(256 bytes)
+ This allow buffers. The region is pagesize-aligned (via VirtualAlloc allocation)
+
+ We use smallish IO buffers, 256 bytes is probably large enough for most of
+ the queries. Larger buffers could have funny effects(thread hogginng)
+ on threadpool scheduling in case client is using protocol pipelining.
+
+ Also note, that even in an unlikely situation where cache runs out of buffers,
+ this does not lead to errors, zero szed reads will be used in WSARecv then.
+*/
+
+constexpr size_t READ_BUFSIZ= 256;
+class AIO_buffer_cache
+{
+ const size_t ITEM_SIZE= READ_BUFSIZ;
+
+ /** Limit the whole cache to 1MB*/
+ const size_t MAX_SIZE= 1048576;
+
+ /* Allocation base */
+ char *m_base= 0;
+
+ /* "Free list" with LIFO policy */
+ std::vector<char *> m_cache;
+ std::mutex m_mtx;
+ size_t m_elements=0;
+
+public:
+ void set_size(size_t n_items);
+ char *acquire_buffer();
+ void release_buffer(char *v);
+ void clear();
+ ~AIO_buffer_cache();
+};
+
+
+void AIO_buffer_cache::set_size(size_t n_items)
+{
+ DBUG_ASSERT(!m_base);
+ m_elements= std::min(n_items, MAX_SIZE / ITEM_SIZE);
+ auto sz= m_elements * ITEM_SIZE;
+
+ m_base=
+ (char *) VirtualAlloc(0, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (!m_base)
+ {
+ m_elements= 0;
+ return;
+ }
+
+ /* Try to help memory manager here, by prelocking region in memory*/
+ (void) VirtualLock(m_base, sz);
+
+ m_cache.reserve(m_elements);
+ for (ssize_t i= m_elements - 1; i >= 0 ; i--)
+ m_cache.push_back(m_base + i * ITEM_SIZE);
+}
+
+/*
+ Returns a buffer, or NULL if no free buffers.
+
+ LIFO policy is implemented, so we do not touch too many
+ pages (no std::stack though)
+*/
+char *AIO_buffer_cache::acquire_buffer()
+{
+ std::unique_lock<std::mutex> lk(m_mtx);
+ if (m_cache.empty())
+ return nullptr;
+ auto p= m_cache.back();
+ m_cache.pop_back();
+ return p;
+}
+
+void AIO_buffer_cache::release_buffer(char *v)
+{
+ std::unique_lock<std::mutex> lk(m_mtx);
+ m_cache.push_back(v);
+}
+
+void AIO_buffer_cache::clear()
+{
+ if (!m_base)
+ return;
+
+ /* Check that all items are returned to the cache. */
+ DBUG_ASSERT(m_cache.size() == m_elements);
+ VirtualFree(m_base, 0, MEM_RELEASE);
+ m_cache.clear();
+ m_base= 0;
+ m_elements= 0;
+}
+
+AIO_buffer_cache::~AIO_buffer_cache() { clear(); }
+
+/* Global variable for the cache buffers.*/
+AIO_buffer_cache read_buffers;
+
+win_aiosocket::~win_aiosocket()
+{
+ if (m_buf_ptr)
+ read_buffers.release_buffer(m_buf_ptr);
+}
+
+
+/** Return number of unread bytes.*/
+size_t win_aiosocket::buffer_remaining()
+{
+ return m_buf_datalen - m_buf_off;
+}
+
+static my_bool my_vio_has_data(st_vio *vio)
+{
+ auto sock= (win_aiosocket *) vio->tp_ctx;
+ return sock->buffer_remaining() || sock->m_orig_vio_has_data(vio);
+}
+
+/*
+ (Half-)buffered read.
+
+ The buffer is filled once, by completion of the async IO.
+
+ We do not refill the buffer once it is read off,
+ does not make sense.
+*/
+static size_t my_vio_read(st_vio *vio, uchar *dest, size_t sz)
+{
+ auto sock= (win_aiosocket *) vio->tp_ctx;
+ DBUG_ASSERT(sock);
+
+ auto nbytes= std::min(sock->buffer_remaining(), sz);
+
+ if (nbytes > 0)
+ {
+ /* Copy to output, adjust the offset.*/
+ memcpy(dest, sock->m_buf_ptr + sock->m_buf_off, nbytes);
+ sock->m_buf_off += nbytes;
+ return nbytes;
+ }
+
+ return sock->m_orig_vio_read(vio, dest, sz);
+}
+
+DWORD win_aiosocket::begin_read()
+{
+ DWORD err = ERROR_SUCCESS;
+ static char c;
+ WSABUF buf;
+
+ DBUG_ASSERT(!buffer_remaining());
+
+ /*
+ If there is no internal buffer to store data,
+ we do zero size read, but still need a valid
+ pointer for the buffer parameter.
+ */
+ if (m_buf_ptr)
+ buf= {(ULONG)READ_BUFSIZ, m_buf_ptr};
+ else
+ buf= {0, &c};
+
+
+ if (!m_is_pipe)
+ {
+ /* Do async io (sockets). */
+ DWORD flags= 0;
+ if (WSARecv((SOCKET) m_handle, &buf, 1, 0, &flags, &m_overlapped, NULL))
+ err= WSAGetLastError();
+ }
+ else
+ {
+ /* Do async read (named pipe) */
+ if (ReadFile(m_handle, buf.buf, buf.len, 0, &m_overlapped))
+ err= GetLastError();
+ }
+
+ if (!err || err == ERROR_IO_PENDING)
+ return 0;
+ return err;
+}
+
+void win_aiosocket::end_read(ULONG nbytes, DWORD err)
+{
+ DBUG_ASSERT(!buffer_remaining());
+ DBUG_ASSERT(!nbytes || m_buf_ptr);
+ m_buf_off= 0;
+ m_buf_datalen= nbytes;
+}
+
+void win_aiosocket::init(Vio *vio)
+{
+ m_is_pipe= vio->type == VIO_TYPE_NAMEDPIPE;
+ m_handle=
+ m_is_pipe ? vio->hPipe : (HANDLE) mysql_socket_getfd(vio->mysql_socket);
+
+ SetFileCompletionNotificationModes(m_handle, FILE_SKIP_SET_EVENT_ON_HANDLE);
+ if (vio->type == VIO_TYPE_SSL)
+ {
+ /*
+ TODO : This requires fixing viossl to call our manipulated VIO
+ */
+ return;
+ }
+
+ if (!(m_buf_ptr = read_buffers.acquire_buffer()))
+ {
+ /* Ran out of buffers, that's fine.*/
+ return;
+ }
+
+ vio->tp_ctx= this;
+
+ m_orig_vio_has_data= vio->has_data;
+ vio->has_data= my_vio_has_data;
+
+ m_orig_vio_read= vio->read;
+ vio->read= my_vio_read;
+}
+
+void init_win_aio_buffers(unsigned int n_buffers)
+{
+ read_buffers.set_size(n_buffers);
+}
+
+extern void destroy_win_aio_buffers()
+{
+ read_buffers.clear();
+}
diff --git a/sql/threadpool_winsockets.h b/sql/threadpool_winsockets.h
new file mode 100644
index 00000000000..ca2068b759d
--- /dev/null
+++ b/sql/threadpool_winsockets.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2020 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+ */
+#pragma once
+
+#include <WinSock2.h>
+#include <windows.h>
+
+struct st_vio;
+
+struct win_aiosocket
+{
+ /** OVERLAPPED is needed by all Windows AIO*/
+ OVERLAPPED m_overlapped{};
+ /** Handle to pipe, or socket */
+ HANDLE m_handle{};
+ /** Whether the m_handle refers to pipe*/
+ bool m_is_pipe{};
+
+ /* Read buffer handling */
+
+ /** Pointer to buffer of size READ_BUFSIZ. Can be NULL.*/
+ char *m_buf_ptr{};
+ /** Offset to current buffer position*/
+ size_t m_buf_off{};
+ /** Size of valid data in the buffer*/
+ size_t m_buf_datalen{};
+
+ /* Vio handling */
+ /** Pointer to original vio->vio_read/vio->has_data function */
+ size_t (*m_orig_vio_read)(st_vio *, unsigned char *, size_t){};
+ char (*m_orig_vio_has_data)(st_vio *){};
+
+
+
+ /**
+ Begins asynchronnous reading from socket/pipe.
+ On IO completion, pre-read some bytes into internal buffer
+ */
+ DWORD begin_read();
+
+ /**
+ Update number of bytes returned, and IO error status
+
+ Should be called right after IO is completed
+ GetQueuedCompletionStatus() , or threadpool IO completion
+ callback would return nbytes and the error.
+
+ Sets the valid data length in the read buffer.
+ */
+ void end_read(ULONG nbytes, DWORD err);
+
+ /**
+ Override VIO routines with ours, accounting for
+ one-shot buffering.
+ */
+ void init(st_vio *vio);
+
+ /** Return number of unread bytes.*/
+ size_t buffer_remaining();
+
+ /* Frees the read buffer.*/
+ ~win_aiosocket();
+};
+
+/* Functions related to IO buffers caches.*/
+extern void init_win_aio_buffers(unsigned int n_buffers);
+extern void destroy_win_aio_buffers();
diff --git a/sql/upgrade_conf_file.cc b/sql/upgrade_conf_file.cc
index a08fd087172..09bd2866798 100644
--- a/sql/upgrade_conf_file.cc
+++ b/sql/upgrade_conf_file.cc
@@ -42,13 +42,19 @@ static const char *removed_variables[] =
"have_partitioning",
"innodb_adaptive_flushing_method",
"innodb_adaptive_hash_index_partitions",
+"innodb_adaptive_max_sleep_delay",
"innodb_additional_mem_pool_size",
"innodb_api_bk_commit_interval",
"innodb_api_disable_rowlock",
"innodb_api_enable_binlog",
"innodb_api_enable_mdl",
"innodb_api_trx_level",
+"innodb_background_scrub_data_check_interval",
+"innodb_background_scrub_data_compressed",
+"innodb_background_scrub_data_interval",
+"innodb_background_scrub_data_uncompressed",
"innodb_blocking_buffer_pool_restore",
+"innodb_buffer_pool_instances",
"innodb_buffer_pool_populate",
"innodb_buffer_pool_restore_at_startup",
"innodb_buffer_pool_shm_checksum",
@@ -62,6 +68,8 @@ static const char *removed_variables[] =
"innodb_cleaner_lsn_age_factor",
"innodb_cleaner_max_flush_time",
"innodb_cleaner_max_lru_time",
+"innodb_commit_concurrency",
+"innodb_concurrency_tickets",
"innodb_corrupt_table_action",
"innodb_dict_size_limit",
"innodb_doublewrite_file",
@@ -88,12 +96,16 @@ static const char *removed_variables[] =
"innodb_log_archive",
"innodb_log_block_size",
"innodb_log_checksum_algorithm",
-"innodb_rollback_segments",
+"innodb_log_checksums",
+"innodb_log_compressed_pages",
+"innodb_log_files_in_group",
+"innodb_log_optimize_ddl",
"innodb_max_bitmap_file_size",
"innodb_max_changed_pages",
"innodb_merge_sort_block_size",
"innodb_mirrored_log_groups",
"innodb_mtflush_threads",
+"innodb_page_cleaners",
"innodb_persistent_stats_root_page",
"innodb_print_lock_wait_timeout_info",
"innodb_purge_run_now",
@@ -101,15 +113,23 @@ static const char *removed_variables[] =
"innodb_read_ahead",
"innodb_recovery_stats",
"innodb_recovery_update_relay_log",
+"innodb_replication_delay",
+"innodb_rollback_segments",
+"innodb_scrub_log",
+"innodb_scrub_log_speed",
"innodb_show_locks_held",
"innodb_show_verbose_locks",
"innodb_stats_auto_update",
"innodb_stats_sample_pages",
"innodb_stats_update_need_lock",
"innodb_support_xa",
+"innodb_sync_array_size",
+"innodb_thread_concurrency",
"innodb_thread_concurrency_timer_based",
+"innodb_thread_sleep_delay",
"innodb_track_changed_pages",
"innodb_track_redo_log_now",
+"innodb_undo_logs",
"innodb_use_fallocate",
"innodb_use_global_flush_log_at_trx_commit",
"innodb_use_mtflush",
diff --git a/sql/winmain.cc b/sql/winmain.cc
new file mode 100644
index 00000000000..fb5da40cf2f
--- /dev/null
+++ b/sql/winmain.cc
@@ -0,0 +1,371 @@
+/* Copyright (C) 2020 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 Street, Fifth Floor, Boston, MA 02110-1335 USA
+*/
+
+/*
+ main() function for the server on Windows is implemented here.
+ The core functionality is implemented elsewhere, in mysqld_main(), and running as
+ service is done here.
+
+ Main tasks of the service are
+
+ 1. Report current status back to service control manager. Here we're
+ providing callbacks so code outside of winmain.cc can call it
+ (via mysqld_set_service_status_callback())
+
+ 2. React to notification, the only one we care about is the "stop"
+ notification. we initiate shutdown, when instructed.
+
+ Note that our service might not be too Windows-friendly, as it might take
+ a while to startup (recovery), and a while to shut down(innodb cleanups).
+
+ Most of the code more of less standard service stuff, taken from Microsoft
+ docs examples.
+
+ Notable oddity in running services, is that we do not know for sure,
+ whether we should run as a service or not (there is no --service parameter that
+ would tell).Heuristics are used, and if the last command line argument is
+ valid service name, we try to run as service, but fallback to usual process
+ if this fails.
+
+ As an example, even if mysqld.exe is started with command line like "mysqld.exe --help",
+ it is entirely possible that mysqld.exe run as service "--help".
+
+ Apart from that, now deprecated and obsolete service registration/removal functionality is
+ still provided (mysqld.exe --install/--remove)
+*/
+
+#include <my_global.h>
+#include <mysqld.h>
+#include <log.h>
+
+#include <stdio.h>
+#include <windows.h>
+#include <string>
+#include <cassert>
+
+static SERVICE_STATUS svc_status{SERVICE_WIN32_OWN_PROCESS};
+static SERVICE_STATUS_HANDLE svc_status_handle;
+static char *svc_name;
+
+static char **orig_argv;
+static int orig_argc;
+
+static int install_service(int argc, char **argv, const char *name);
+static int remove_service(const char *name);
+
+/*
+ Report service status to SCM. This function is indirectly invoked
+ by the server to report state transitions.
+
+ 1. from START_PENDING to SERVICE_RUNNING, when we start accepting user connections
+ 2. from SERVICE_RUNNING to STOP_PENDING, when we start shutdown
+ 3. from STOP_PENDING to SERVICE_STOPPED, in mysqld_exit()
+ sometimes also START_PENDING to SERVICE_STOPPED, on startup errors
+*/
+static void report_svc_status(DWORD current_state, DWORD exit_code, DWORD wait_hint)
+{
+ if (!svc_status_handle)
+ return;
+
+ static DWORD check_point= 1;
+ svc_status.dwCurrentState= current_state;
+ svc_status.dwWaitHint= wait_hint;
+
+ if (exit_code)
+ {
+ svc_status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
+ svc_status.dwServiceSpecificExitCode= exit_code;
+ }
+ else
+ {
+ svc_status.dwWin32ExitCode= 0;
+ }
+
+ if (current_state == SERVICE_START_PENDING)
+ svc_status.dwControlsAccepted= 0;
+ else
+ svc_status.dwControlsAccepted= SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
+
+ if ((current_state == SERVICE_RUNNING) || (current_state == SERVICE_STOPPED))
+ svc_status.dwCheckPoint= 0;
+ else
+ svc_status.dwCheckPoint= check_point++;
+
+ SetServiceStatus(svc_status_handle, &svc_status);
+}
+
+/* Report unexpected errors. */
+static void svc_report_event(const char *svc_name, const char *command)
+{
+ char buffer[80];
+ sprintf_s(buffer, "mariadb service %s, %s failed with %d",
+ svc_name, command, GetLastError());
+ OutputDebugString(buffer);
+}
+
+/*
+ Service control function.
+ Reacts to service stop, initiates shutdown.
+*/
+static void WINAPI svc_ctrl_handle(DWORD cntrl)
+{
+ switch (cntrl)
+ {
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ sql_print_information(
+ "Windows service \"%s\": received %s",
+ svc_name,
+ cntrl == SERVICE_CONTROL_STOP? "SERVICE_CONTROL_STOP": "SERVICE_CONTROL_SHUTDOWN");
+
+ /* The below will also set the status to STOP_PENDING. */
+ mysqld_win_initiate_shutdown();
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ default:
+ break;
+ }
+}
+
+/* Service main routine, mainly runs mysqld_main() */
+static void WINAPI svc_main(DWORD svc_argc, char **svc_argv)
+{
+ /* Register the handler function for the service */
+ char *name= svc_argv[0];
+
+ svc_status_handle= RegisterServiceCtrlHandler(name, svc_ctrl_handle);
+ if (!svc_status_handle)
+ {
+ svc_report_event(name, "RegisterServiceCtrlHandler");
+ return;
+ }
+ report_svc_status(SERVICE_START_PENDING, NO_ERROR, 0);
+
+ /* Make server report service status via our callback.*/
+ mysqld_set_service_status_callback(report_svc_status);
+
+ /* This would add service name entry to load_defaults.*/
+ mysqld_win_set_service_name(name);
+
+ /*
+ Do not pass the service name parameter (last on the command line)
+ to mysqld_main(), it is unaware of it.
+ */
+ orig_argv[orig_argc - 1]= 0;
+ mysqld_main(orig_argc - 1, orig_argv);
+}
+
+/*
+ This start the service. Sometimes it will fail, because
+ currently we do not know for sure whether we run as service or not.
+ If this fails, the fallback is to run as normal process.
+*/
+static int run_as_service(char *name)
+{
+ SERVICE_TABLE_ENTRY stb[]= {{name, svc_main}, {0, 0}};
+ if (!StartServiceCtrlDispatcher(stb))
+ {
+ assert(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ Check for valid existing service name.
+ Part of our guesswork, whether we run as service or not.
+*/
+static bool is_existing_service(const char *name)
+{
+ if (strchr(name, '\\') || strchr(name, '/'))
+ {
+ /* Invalid characters in service name */
+ return false;
+ }
+
+ SC_HANDLE sc_service= 0, scm= 0;
+ bool ret= ((scm= OpenSCManager(0, 0, SC_MANAGER_ENUMERATE_SERVICE)) != 0) &&
+ ((sc_service= OpenService(scm, name, SERVICE_QUERY_STATUS)) != 0);
+
+ if (sc_service)
+ CloseServiceHandle(sc_service);
+ if (scm)
+ CloseServiceHandle(scm);
+
+ return ret;
+}
+
+/*
+ If service name is not given to --install/--remove
+ it is assumed to be "MySQL" (traditional handling)
+*/
+static const char *get_svc_name(const char *arg)
+{
+ return arg ? arg : "MySQL";
+}
+
+/*
+ Main function on Windows.
+ Runs mysqld as normal process, or as a service.
+
+ Plus, the obsolete functionality to register/remove services.
+*/
+int main(int argc, char **argv)
+{
+ orig_argv= argv;
+ orig_argc= argc;
+
+ /*
+ If no special arguments are given, service name is nor present
+ run as normal program.
+ */
+ if (argc == 1)
+ return mysqld_main(argc, argv);
+
+ auto cmd= argv[1];
+
+ /* Handle install/remove */
+ if (!strcmp(cmd, "--install") || !strcmp(cmd, "--install-manual"))
+ return install_service(argc, argv, get_svc_name(argv[2]));
+
+ if (!strcmp(cmd, "--remove"))
+ return remove_service(get_svc_name(argv[2]));
+
+ /* Try to run as service, and fallback to mysqld_main(), if this fails */
+ svc_name= argv[argc - 1];
+ if (is_existing_service(svc_name) && !run_as_service(svc_name))
+ return 0;
+ svc_name= 0;
+
+ /* Run as normal program.*/
+ return mysqld_main(argc, argv);
+}
+
+
+/*
+ Register/remove services functionality.
+ This is kept for backward compatibility only, and is
+ superseeded by much more versatile mysql_install_db.exe
+
+ "mysqld --remove=svc" has no advantage over
+ OS own "sc delete svc"
+*/
+static void ATTRIBUTE_NORETURN die(const char *func, const char *name)
+{
+ DWORD err= GetLastError();
+ fprintf(stderr, "FATAL ERROR : %s failed (%lu)\n", func, err);
+ switch (err)
+ {
+ case ERROR_SERVICE_EXISTS:
+ fprintf(stderr, "Service %s already exists.\n", name);
+ break;
+ case ERROR_SERVICE_DOES_NOT_EXIST:
+ fprintf(stderr, "Service %s does not exist.\n", name);
+ break;
+ case ERROR_ACCESS_DENIED:
+ fprintf(stderr, "Access is denied. "
+ "Make sure to run as elevated admin user.\n");
+ break;
+ case ERROR_INVALID_NAME:
+ fprintf(stderr, "Invalid service name '%s'\n", name);
+ default:
+ break;
+ }
+ exit(1);
+}
+
+static inline std::string quoted(const char *src)
+{
+ std::string s;
+ s.append("\"").append(src).append("\"");
+ return s;
+}
+
+static int install_service(int argc, char **argv, const char *name)
+{
+ std::string cmdline;
+
+ char path[MAX_PATH];
+ auto nSize = GetModuleFileName(0, path, sizeof(path));
+
+ if (nSize == (DWORD) sizeof(path) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ die("GetModuleName", name);
+
+ cmdline.append(quoted(path));
+
+ const char *user= 0;
+ // mysqld --install[-manual] name ...[--local-service]
+ if (argc > 2)
+ {
+ for (int i= 3; argv[i]; i++)
+ {
+ if (!strcmp(argv[i], "--local-service"))
+ user= "NT AUTHORITY\\LocalService";
+ else
+ {
+ cmdline.append(" ").append(quoted(argv[i]));
+ }
+ }
+ }
+ cmdline.append(" ").append(quoted(name));
+
+ DWORD start_type;
+ if (!strcmp(argv[1], "--install-manual"))
+ start_type= SERVICE_DEMAND_START;
+ else
+ start_type= SERVICE_AUTO_START;
+
+ SC_HANDLE scm, sc_service;
+ if (!(scm= OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE)))
+ die("OpenSCManager", name);
+
+ if (!(sc_service= CreateService(
+ scm, name, name, SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS, start_type, SERVICE_ERROR_NORMAL,
+ cmdline.c_str(), 0, 0, 0, user, 0)))
+ die("CreateService", name);
+
+ char description[]= "MariaDB database server";
+ SERVICE_DESCRIPTION sd= {description};
+ ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd);
+
+ CloseServiceHandle(sc_service);
+ CloseServiceHandle(scm);
+
+ printf("Service '%s' successfully installed.\n", name);
+ return 0;
+}
+
+static int remove_service(const char *name)
+{
+ SC_HANDLE scm, sc_service;
+
+ if (!(scm= OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE)))
+ die("OpenSCManager", name);
+
+ if (!(sc_service= OpenService(scm, name, DELETE)))
+ die("OpenService", name);
+
+ if (!DeleteService(sc_service))
+ die("DeleteService", name);
+
+ CloseServiceHandle(sc_service);
+ CloseServiceHandle(scm);
+
+ printf("Service '%s' successfully deleted.\n", name);
+ return 0;
+}
diff --git a/sql/winservice.c b/sql/winservice.c
index d7cfd2f7584..a11087e5cd5 100644
--- a/sql/winservice.c
+++ b/sql/winservice.c
@@ -40,7 +40,7 @@ void get_file_version(const char *path, int *major, int *minor, int *patch)
*major= *minor= *patch= 0;
size= GetFileVersionInfoSize(path, &version_handle);
- if (size == 0)
+ if (size == 0)
return;
ver= (char *)malloc(size);
if(!GetFileVersionInfo(path, version_handle, size, ver))
@@ -65,7 +65,7 @@ void normalize_path(char *path, size_t size)
char *p;
strcpy_s(buf, MAX_PATH, path+1);
p= strchr(buf, '"');
- if (p)
+ if (p)
*p=0;
}
else
@@ -136,15 +136,15 @@ static void get_datadir_from_ini(const char *ini, char *service_name, char *data
/*
Retrieve some properties from windows mysqld service binary path.
- We're interested in ini file location and datadir, and also in version of
+ We're interested in ini file location and datadir, and also in version of
the data. We tolerate missing mysqld.exe.
- Note that this function carefully avoids using mysql libraries (e.g dbug),
+ Note that this function carefully avoids using mysql libraries (e.g dbug),
since it is used in unusual environments (windows installer, MFC), where we
- do not have much control over how threads are created and destroyed, so we
+ do not have much control over how threads are created and destroyed, so we
cannot assume MySQL thread initilization here.
*/
-int get_mysql_service_properties(const wchar_t *bin_path,
+int get_mysql_service_properties(const wchar_t *bin_path,
mysqld_service_properties *props)
{
int numargs;
@@ -193,9 +193,10 @@ int get_mysql_service_properties(const wchar_t *bin_path,
if(wcsstr(mysqld_path, L".exe") == NULL)
wcscat(mysqld_path, L".exe");
- if(wcsicmp(file_part, L"mysqld.exe") != 0 &&
+ if(wcsicmp(file_part, L"mysqld.exe") != 0 &&
wcsicmp(file_part, L"mysqld-debug.exe") != 0 &&
- wcsicmp(file_part, L"mysqld-nt.exe") != 0)
+ wcsicmp(file_part, L"mysqld-nt.exe") != 0 &&
+ wcsicmp(file_part, L"mariadbd.exe") != 0)
{
/* The service executable is not mysqld. */
goto end;
@@ -205,7 +206,7 @@ int get_mysql_service_properties(const wchar_t *bin_path,
/* If mysqld.exe exists, try to get its version from executable */
if (GetFileAttributes(props->mysqld_exe) != INVALID_FILE_ATTRIBUTES)
{
- get_file_version(props->mysqld_exe, &props->version_major,
+ get_file_version(props->mysqld_exe, &props->version_major,
&props->version_minor, &props->version_patch);
}
@@ -235,7 +236,7 @@ int get_mysql_service_properties(const wchar_t *bin_path,
{
/*
Hard, although a rare case, we're guessing datadir and defaults-file.
- On Windows, defaults-file is traditionally install-root\my.ini
+ On Windows, defaults-file is traditionally install-root\my.ini
and datadir is install-root\data
*/
char install_root[MAX_PATH];
@@ -297,7 +298,7 @@ int get_mysql_service_properties(const wchar_t *bin_path,
}
/*
- If version could not be determined so far, try mysql_upgrade_info in
+ If version could not be determined so far, try mysql_upgrade_info in
database directory.
*/
if(props->version_major == 0)
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
index ed6f3ebc881..c7859ac9df3 100644
--- a/sql/wsrep_schema.cc
+++ b/sql/wsrep_schema.cc
@@ -196,7 +196,7 @@ static int execute_SQL(THD* thd, const char* sql, uint length) {
thd->set_query((char*)sql, length);
thd->set_query_id(next_query_id());
- mysql_parse(thd, (char*)sql, length, & parser_state, FALSE, FALSE);
+ mysql_parse(thd, (char*)sql, length, & parser_state);
if (thd->is_error()) {
WSREP_WARN("Wsrep_schema::execute_sql() failed, %d %s\nSQL: %s",
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index 6a7b16d243e..25992459060 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -1460,7 +1460,7 @@ static int run_sql_command(THD *thd, const char *query)
return -1;
}
- mysql_parse(thd, thd->query(), thd->query_length(), &ps, FALSE, FALSE);
+ mysql_parse(thd, thd->query(), thd->query_length(), &ps);
if (thd->is_error())
{
int const err= thd->get_stmt_da()->sql_errno();
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index d894fa6d555..97aca456369 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -97,12 +97,6 @@ static void wsrep_set_wsrep_on()
strcmp(wsrep_provider, WSREP_NONE);
}
-/* This is intentionally declared as a weak global symbol, so that
-linking will succeed even if the server is built with a dynamically
-linked InnoDB. */
-ulong innodb_lock_schedule_algorithm __attribute__((weak));
-struct handlerton* innodb_hton_ptr __attribute__((weak));
-
bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
{
if (var_type == OPT_GLOBAL) {
@@ -138,18 +132,7 @@ bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
bool wsrep_on_check(sys_var *self, THD* thd, set_var* var)
{
- bool new_wsrep_on= (bool)var->save_result.ulonglong_value;
-
- if (check_has_super(self, thd, var))
- return true;
-
- if (new_wsrep_on && innodb_hton_ptr && innodb_lock_schedule_algorithm != 0) {
- my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled "
- "if innodb_lock_schedule_algorithm=VATS. Please configure"
- " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0));
- return true;
- }
- return false;
+ return check_has_super(self, thd, var);
}
bool wsrep_causal_reads_update (sys_var *self, THD* thd, enum_var_type var_type)