From 9df343e6ed2b60941973b3e221596c6bae1213c9 Mon Sep 17 00:00:00 2001
From: Siavosh Kasravi <siavosh.kasravi@gmail.com>
Date: Sat, 13 Jan 2024 01:49:29 +0330
Subject: [PATCH] MDEV-30813 implement RESTART command

---
 client/mysql.cc                               |  1 +
 client/mysqltest.cc                           |  1 +
 mysql-test/main/restart.result                | 13 +++
 mysql-test/main/restart.test                  | 41 ++++++++
 mysql-test/main/restart_dead_parent.result    |  3 +
 mysql-test/main/restart_dead_parent.test      | 16 ++++
 mysql-test/main/restart_no_parent.result      |  3 +
 mysql-test/main/restart_no_parent.test        | 14 +++
 .../rpl/include/rpl_restart_wait_slaves.inc   | 96 +++++++++++++++++++
 .../rpl/r/rpl_restart_wait_slaves.result      | 29 ++++++
 .../suite/rpl/t/rpl_restart_wait_slaves.cnf   | 16 ++++
 .../suite/rpl/t/rpl_restart_wait_slaves.test  | 11 +++
 sql-bench/crash-me.sh                         | 41 ++++----
 sql/mysqld.cc                                 | 15 ++-
 sql/mysqld.h                                  |  2 +-
 sql/share/errmsg-utf8.txt                     |  6 ++
 sql/sql_cmd.h                                 |  2 +-
 sql/sql_parse.cc                              | 23 +++++
 sql/sql_yacc.yy                               | 10 +-
 support-files/mariadb.service.in              |  3 +
 20 files changed, 317 insertions(+), 29 deletions(-)
 create mode 100644 mysql-test/main/restart.result
 create mode 100644 mysql-test/main/restart.test
 create mode 100644 mysql-test/main/restart_dead_parent.result
 create mode 100644 mysql-test/main/restart_dead_parent.test
 create mode 100644 mysql-test/main/restart_no_parent.result
 create mode 100644 mysql-test/main/restart_no_parent.test
 create mode 100644 mysql-test/suite/rpl/include/rpl_restart_wait_slaves.inc
 create mode 100644 mysql-test/suite/rpl/r/rpl_restart_wait_slaves.result
 create mode 100644 mysql-test/suite/rpl/t/rpl_restart_wait_slaves.cnf
 create mode 100644 mysql-test/suite/rpl/t/rpl_restart_wait_slaves.test

diff --git a/client/mysql.cc b/client/mysql.cc
index febbf53e87e..ac008cc2218 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -758,6 +758,7 @@ static COMMANDS commands[] = {
   { "REPEAT", 0, 0, 0, ""},
   { "REQUIRE", 0, 0, 0, ""},
   { "RESET", 0, 0, 0, ""},
+  { "RESTART", 0, 0, 0, ""},
   { "RESTORE", 0, 0, 0, ""},
   { "RESTRICT", 0, 0, 0, ""},
   { "RESUME", 0, 0, 0, ""},
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index e2b229f3756..7055d62539d 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -9677,6 +9677,7 @@ void init_re(void)
     "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
     "[[:space:]]*SHOW[[:space:]]|"
     "[[:space:]]*SHUTDOWN[[:space:]]|"
+    "[[:space:]]*RESTART[[:space:]]|"
     "[[:space:]]*SLAVE[[:space:]]|"
     "[[:space:]]*TRUNCATE[[:space:]]|"
     "[[:space:]]*UNINSTALL[[:space:]]+|"
diff --git a/mysql-test/main/restart.result b/mysql-test/main/restart.result
new file mode 100644
index 00000000000..fc9fe024073
--- /dev/null
+++ b/mysql-test/main/restart.result
@@ -0,0 +1,13 @@
+CREATE USER user1@localhost;
+connect  c1,localhost,user1,,;
+RESTART;
+ERROR 42000: Access denied; you need (at least one of) the SHUTDOWN privilege(s) for this operation
+connection default;
+disconnect c1;
+connect  c1,localhost,root,,;
+CREATE PROCEDURE try_restart() RESTART;
+DROP PROCEDURE try_restart;
+RESTART;
+connection default;
+DROP USER user1@localhost;
+FOUND 3 /(mysqld|mariadbd)(\.exe)? \(initiated by: root\[root\] @ localhost \[(::1)?\]\): Trying to restart.../ in mysqld.1.err
diff --git a/mysql-test/main/restart.test b/mysql-test/main/restart.test
new file mode 100644
index 00000000000..06196e1b57f
--- /dev/null
+++ b/mysql-test/main/restart.test
@@ -0,0 +1,41 @@
+--source include/not_embedded.inc
+
+--disable_service_connection
+#
+# RESTART statement
+#
+# $MARIADBD_PARENT_PID must be set to 1 for this test
+
+
+CREATE USER user1@localhost;
+
+connect (c1,localhost,user1,,);
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+RESTART;
+
+connection default;
+disconnect c1;
+--connect (c1,localhost,root,,)
+
+CREATE PROCEDURE try_restart() RESTART;
+DROP PROCEDURE try_restart;
+
+--let $_expect_file_name= `select regexp_replace(@@tmpdir, '^.*/','')`
+--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/$_expect_file_name.expect
+--exec echo "wait" > $_expect_file_name
+RESTART;
+
+--connection default
+--source include/wait_until_disconnected.inc
+
+--exec echo "restart" > $_expect_file_name
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+DROP USER user1@localhost;
+
+--let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err
+--let SEARCH_PATTERN=(mysqld|mariadbd)(\.exe)? \(initiated by: root\[root\] @ localhost \[(::1)?\]\): Trying to restart...
+--source include/search_pattern_in_file.inc
+--enable_service_connection
diff --git a/mysql-test/main/restart_dead_parent.result b/mysql-test/main/restart_dead_parent.result
new file mode 100644
index 00000000000..1188bd1c810
--- /dev/null
+++ b/mysql-test/main/restart_dead_parent.result
@@ -0,0 +1,3 @@
+connect  c1,localhost,root,,;
+RESTART;
+ERROR HY000: Monitor proccess (PID=4194304) seems to be dead
diff --git a/mysql-test/main/restart_dead_parent.test b/mysql-test/main/restart_dead_parent.test
new file mode 100644
index 00000000000..af37c10aaf4
--- /dev/null
+++ b/mysql-test/main/restart_dead_parent.test
@@ -0,0 +1,16 @@
+--source include/not_embedded.inc
+--source include/linux.inc
+
+--disable_service_connection
+#
+# RESTART statement when is set but the process is dead
+#
+# $MARIADBD_PARENT_PID must be set to 4194304 for this test
+
+
+--connect (c1,localhost,root,,)
+
+--error ER_DEAD_MONITOR_PROCESS
+RESTART;
+
+--enable_service_connection
diff --git a/mysql-test/main/restart_no_parent.result b/mysql-test/main/restart_no_parent.result
new file mode 100644
index 00000000000..4da43d558ce
--- /dev/null
+++ b/mysql-test/main/restart_no_parent.result
@@ -0,0 +1,3 @@
+connect  c1,localhost,root,,;
+RESTART;
+ERROR HY000: $MARIADBD_PARENT_PID is invalid or unset
diff --git a/mysql-test/main/restart_no_parent.test b/mysql-test/main/restart_no_parent.test
new file mode 100644
index 00000000000..c72c10664ca
--- /dev/null
+++ b/mysql-test/main/restart_no_parent.test
@@ -0,0 +1,14 @@
+--source include/not_embedded.inc
+
+--disable_service_connection
+#
+# RESTART statement when $MARIADBD_PARENT_PID is not set
+#
+# $MARIADBD_PARENT_PID must be unset for this test
+
+--connect (c1,localhost,root,,)
+
+--error ER_NO_PARENT_PID
+RESTART;
+
+--enable_service_connection
diff --git a/mysql-test/suite/rpl/include/rpl_restart_wait_slaves.inc b/mysql-test/suite/rpl/include/rpl_restart_wait_slaves.inc
new file mode 100644
index 00000000000..4f92b4216e3
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_restart_wait_slaves.inc
@@ -0,0 +1,96 @@
+--connection server_1
+
+CREATE TABLE t1 (a INT) ENGINE=innodb;
+
+--save_master_pos
+
+--connection server_2
+--sync_with_master
+
+--connection server_3
+--sync_with_master
+
+--connection server_4
+--source include/stop_slave.inc
+
+--connection server_1
+--disable_query_log
+--let $count=1000
+while ($count)
+{
+ INSERT INTO t1 SET a=1;
+ --dec $count
+}
+--enable_query_log
+--save_master_pos
+
+# Shutdown master and restart server_4 who will be waiting for the master
+# to start replication at its shutdown beginning phase.
+# The being forked out server_4 dump thread must relate to a record
+# in slave_list, and it won't start sending out binlog events
+# until has received a signal from the shutdown thread.
+# This also proves delivery to a  started-in-middle-of-shutdown slave.
+--connection server_1
+SET @@GLOBAL.debug_dbug="+d,simulate_delay_at_shutdown";
+
+--connection server_4
+--source include/start_slave.inc
+
+--connection server_1
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+--send RESTART WAIT FOR ALL SLAVES
+--reap
+--source include/wait_until_disconnected.inc
+#
+# MDEV-18450 liveness condition:
+# Despite shutdown even "late" slave #4 is in sync
+#
+--connection server_4
+--sync_with_master
+
+--connection server_3
+--sync_with_master
+
+--connection server_2
+--sync_with_master
+
+--connection server_1
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+--connection default
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+--connection server_1
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+#
+# Cleanup
+#
+--connection server_1
+DROP TABLE t1;
+
+# Slaves IO thread will receive the disconnect error when master was shutdown
+# so we are allowing error on start.
+--connection server_2
+--disable_warnings
+--let rpl_allow_error=1
+--source include/start_slave.inc
+--enable_warnings
+
+--connection server_3
+--disable_warnings
+--let rpl_allow_error=1
+--source include/start_slave.inc
+--enable_warnings
+
+--connection server_4
+--disable_warnings
+--let rpl_allow_error=1
+--source include/start_slave.inc
+--enable_warnings
diff --git a/mysql-test/suite/rpl/r/rpl_restart_wait_slaves.result b/mysql-test/suite/rpl/r/rpl_restart_wait_slaves.result
new file mode 100644
index 00000000000..74139e53ca3
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_restart_wait_slaves.result
@@ -0,0 +1,29 @@
+include/rpl_init.inc [topology=1->2, 1->3, 1->4]
+connection server_1;
+CREATE TABLE t1 (a INT) ENGINE=innodb;
+connection server_2;
+connection server_3;
+connection server_4;
+include/stop_slave.inc
+connection server_1;
+connection server_1;
+SET @@GLOBAL.debug_dbug="+d,simulate_delay_at_shutdown";
+connection server_4;
+include/start_slave.inc
+connection server_1;
+RESTART WAIT FOR ALL SLAVES;
+connection server_4;
+connection server_3;
+connection server_2;
+connection server_1;
+connection default;
+connection server_1;
+connection server_1;
+DROP TABLE t1;
+connection server_2;
+include/start_slave.inc
+connection server_3;
+include/start_slave.inc
+connection server_4;
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.cnf b/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.cnf
new file mode 100644
index 00000000000..8ff9df0384d
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.cnf
@@ -0,0 +1,16 @@
+!include ../my.cnf
+
+[mysqld.1]
+log_warnings=3
+[mysqld.2]
+
+[mysqld.3]
+
+[mysqld.4]
+
+[ENV]
+SERVER_MYPORT_3=		@mysqld.3.port
+SERVER_MYSOCK_3=		@mysqld.3.socket
+
+SERVER_MYPORT_4=		@mysqld.4.port
+SERVER_MYSOCK_4=		@mysqld.4.socket
diff --git a/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.test b/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.test
new file mode 100644
index 00000000000..ce149de2406
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_restart_wait_slaves.test
@@ -0,0 +1,11 @@
+#
+# MDEV-30813, MDEV-30813 "Slow" shutdown to wait for slaves that are to be fed
+# with everything in the master binlog before shutdown completes.
+#
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--let $rpl_topology=1->2, 1->3, 1->4
+--source include/rpl_init.inc
+
+--source include/rpl_restart_wait_slaves.inc
+--source include/rpl_end.inc
diff --git a/sql-bench/crash-me.sh b/sql-bench/crash-me.sh
index 26f66f94735..fc6c76b9187 100644
--- a/sql-bench/crash-me.sh
+++ b/sql-bench/crash-me.sh
@@ -984,8 +984,8 @@ try_and_report("Automatic row id", "automatic_rowid",
    ["RTRIM (2 arg)",'rtrim2arg',"rtrim('..abcd..','.')",'..abcd',1],
    ["EXPAND",'expand2arg',"expand('abcd',6)",'abcd  ',0],
    ["REPLACE (2 arg) ",'replace2arg',"replace('AbCd','bC')",'Ad',1],
-   ["MAPCHAR",'mapchar',"mapchar('A�')",'Aa',1],
-   ["ALPHA",'alpha',"alpha('A�',2)",'AA',1],
+   ["MAPCHAR",'mapchar',"mapchar('A�')",'Aa',1],
+   ["ALPHA",'alpha',"alpha('A�',2)",'AA',1],
    ["ASCII in string cast",'ascii_string',"ascii('a')",'a',1],
    ["EBCDIC in string cast",'ebcdic_string',"ebcdic('a')",'a',1],
    ["TRUNC (1 arg)",'trunc1arg',"trunc(222.6)",222,0],
@@ -3647,24 +3647,25 @@ sub check_reserved_words
             'PROC' =>  3,           'PURGE' =>  3,       'RAISERROR' =>  3,
              'RAW' =>  3,        'READTEXT' =>  3,     'RECONFIGURE' =>  3,
           'REGEXP' =>  3,          'RENAME' =>  3,     'REPLICATION' =>  3,
-         'REQUIRE' =>  3,        'RESOURCE' =>  3,         'RESTORE' =>  3,
-           'RLIKE' =>  3,        'ROWCOUNT' =>  3,      'ROWGUIDCOL' =>  3,
-           'ROWID' =>  3,          'ROWNUM' =>  3,           'RTREE' =>  3,
-            'RULE' =>  3,            'SAVE' =>  3,         'SETUSER' =>  3,
-           'SHARE' =>  3,            'SHOW' =>  3,        'SHUTDOWN' =>  3,
-          'SONAME' =>  3,         'SPATIAL' =>  3,  'SQL_BIG_RESULT' =>  3,
-'SQL_CALC_FOUND_ROWS' =>  3,'SQL_SMALL_RESULT' =>  3,        'SSL' =>  3,
-        'STARTING' =>  3,      'STATISTICS' =>  3,   'STRAIGHT_JOIN' =>  3,
-         'STRIPED' =>  3,      'SUCCESSFUL' =>  3,         'SYNONYM' =>  3,
-         'SYSDATE' =>  3,          'TABLES' =>  3,      'TERMINATED' =>  3,
-        'TEXTSIZE' =>  3,        'TINYBLOB' =>  3,         'TINYINT' =>  3,
-        'TINYTEXT' =>  3,             'TOP' =>  3,            'TRAN' =>  3,
-        'TRUNCATE' =>  3,         'TSEQUAL' =>  3,           'TYPES' =>  3,
-             'UID' =>  3,          'UNLOCK' =>  3,        'UNSIGNED' =>  3,
-      'UPDATETEXT' =>  3,             'USE' =>  3,  'USER_RESOURCES' =>  3,
-        'VALIDATE' =>  3,       'VARBINARY' =>  3,        'VARCHAR2' =>  3,
-         'WAITFOR' =>  3,        'WARNINGS' =>  3,       'WRITETEXT' =>  3,
-             'XOR' =>  3,      'YEAR_MONTH' =>  3,        'ZEROFILL' =>  3
+         'REQUIRE' =>  3,        'RESOURCE' =>  3,         'RESTART' =>  3, 
+         'RESTORE' =>  3,           'RLIKE' =>  3,        'ROWCOUNT' =>  3,
+      'ROWGUIDCOL' =>  3,           'ROWID' =>  3,          'ROWNUM' =>  3,
+           'RTREE' =>  3,            'RULE' =>  3,            'SAVE' =>  3,
+         'SETUSER' =>  3,           'SHARE' =>  3,            'SHOW' =>  3,    
+        'SHUTDOWN' =>  3,          'SONAME' =>  3,         'SPATIAL' =>  3, 
+  'SQL_BIG_RESULT' =>  3, 'SQL_CALC_FOUND_ROWS' =>  3,'SQL_SMALL_RESULT' =>  3,    
+             'SSL' =>  3,        'STARTING' =>  3,      'STATISTICS' =>  3, 
+   'STRAIGHT_JOIN' =>  3,         'STRIPED' =>  3,      'SUCCESSFUL' =>  3,   
+         'SYNONYM' =>  3,         'SYSDATE' =>  3,          'TABLES' =>  3,  
+      'TERMINATED' =>  3,        'TEXTSIZE' =>  3,        'TINYBLOB' =>  3,    
+         'TINYINT' =>  3,        'TINYTEXT' =>  3,             'TOP' =>  3,     
+            'TRAN' =>  3,        'TRUNCATE' =>  3,         'TSEQUAL' =>  3,      
+           'TYPES' =>  3,             'UID' =>  3,          'UNLOCK' =>  3,  
+        'UNSIGNED' =>  3,      'UPDATETEXT' =>  3,             'USE' =>  3, 
+  'USER_RESOURCES' =>  3,        'VALIDATE' =>  3,       'VARBINARY' =>  3,   
+        'VARCHAR2' =>  3,         'WAITFOR' =>  3,        'WARNINGS' =>  3,  
+       'WRITETEXT' =>  3,             'XOR' =>  3,      'YEAR_MONTH' =>  3,    
+        'ZEROFILL' =>  3
 );
 
 
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 9526773ad78..1acf74318bc 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -307,6 +307,7 @@ static TYPELIB tc_heuristic_recover_typelib=
 
 const char *first_keyword= "first";
 const char *my_localhost= "localhost", *delayed_user= "DELAYED";
+constexpr int RESTART_EXIT_CODE= 16;
 
 bool opt_large_files= sizeof(my_off_t) > 4;
 static my_bool opt_autocommit; ///< for --autocommit command-line option
@@ -379,6 +380,7 @@ bool opt_endinfo, using_udf_functions;
 my_bool locked_in_memory;
 bool opt_using_transactions;
 bool volatile abort_loop;
+bool volatile restarting;
 uint volatile global_disable_checkpoint;
 #if defined(_WIN32)
 ulong slow_start_timeout;
@@ -1649,13 +1651,14 @@ static my_bool warn_threads_active_after_phase_2(THD *thd, void *)
   thread itself.
 */
 
-static void break_connect_loop()
+static void break_connect_loop(bool restart= 0)
 {
 #ifdef EXTRA_DEBUG
   int count=0;
 #endif
 
   abort_loop= 1;
+  restarting= restart;
 
 #if defined(_WIN32)
   mysqld_win_initiate_shutdown();
@@ -1724,7 +1727,7 @@ static void break_connect_loop()
   (first thread wins).
 */
 
-void kill_mysql(THD *thd)
+void kill_mysql(THD *thd, bool restart)
 {
   char user_host_buff[MAX_USER_HOST_SIZE + 1];
   char *user, *expected_shutdown_user= 0;
@@ -1757,7 +1760,7 @@ void kill_mysql(THD *thd)
 
   if (thd->lex->is_shutdown_wait_for_slaves)
     shutdown_wait_for_slaves= true;
-  break_connect_loop();
+  break_connect_loop(restart);
 }
 
 
@@ -3569,6 +3572,7 @@ SHOW_VAR com_status_vars[]= {
   {"replace_select",       STMT_STATUS(SQLCOM_REPLACE_SELECT)},
   {"reset",                STMT_STATUS(SQLCOM_RESET)},
   {"resignal",             STMT_STATUS(SQLCOM_RESIGNAL)},
+  {"restart",              STMT_STATUS(SQLCOM_RESTART)},
   {"revoke",               STMT_STATUS(SQLCOM_REVOKE)},
   {"revoke_all",           STMT_STATUS(SQLCOM_REVOKE_ALL)},
   {"revoke_role",          STMT_STATUS(SQLCOM_REVOKE_ROLE)},
@@ -6009,7 +6013,7 @@ int mysqld_main(int argc, char **argv)
 
   /* Shutdown requested */
   char *user= shutdown_user.load(std::memory_order_relaxed);
-  sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
+  sql_print_information(restarting ? ER_DEFAULT(ER_RESTARTING): ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
                         user ? user : "unknown");
   if (user)
     my_free(user);
@@ -6042,7 +6046,7 @@ int mysqld_main(int argc, char **argv)
 #if (defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY))
   ERR_remove_state(0);
 #endif
-  mysqld_exit(0);
+  mysqld_exit(restarting ? RESTART_EXIT_CODE : 0);
   return 0;
 }
 
@@ -7813,6 +7817,7 @@ static int mysql_init_variables(void)
   opt_endinfo= using_udf_functions= 0;
   opt_using_transactions= 0;
   abort_loop= select_thread_in_use= signal_thread_in_use= 0;
+  restarting= 0;
   grant_option= 0;
   aborted_threads= aborted_connects= aborted_connects_preauth= 0;
   malloc_calls= 0;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 11e1ee1c8da..3826d2a571d 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -74,7 +74,7 @@ enum enum_slave_parallel_mode {
 };
 
 /* Function prototypes */
-void kill_mysql(THD *thd);
+void kill_mysql(THD *thd, bool restart= 0);
 void close_connection(THD *thd, uint sql_errno= 0);
 void handle_connection_in_main_thread(CONNECT *thd);
 void create_thread_to_handle_connection(CONNECT *connect);
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index b69f2807e46..fdb21f7f3f1 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -12272,3 +12272,9 @@ ER_JSON_SCHEMA_KEYWORD_UNSUPPORTED
         sw "%s neno kuu halitumiki"
 ER_JSON_NO_VARIABLE_SCHEMA
         eng "Variable schema is not supported."
+ER_NO_PARENT_PID
+        eng "$MARIADBD_PARENT_PID is invalid or unset"
+ER_DEAD_MONITOR_PROCESS
+        eng "Monitor proccess (PID=%s) seems to be dead"
+ER_RESTARTING
+        eng "%s (initiated by: %s): Trying to restart..."
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index bedd7e118f0..955a2914e9d 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -89,7 +89,7 @@ enum enum_sql_command {
   SQLCOM_GET_DIAGNOSTICS,
   SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP,
   SQLCOM_SHOW_EXPLAIN,
-  SQLCOM_SHOW_ANALYZE, SQLCOM_SHUTDOWN,
+  SQLCOM_SHOW_ANALYZE, SQLCOM_SHUTDOWN, SQLCOM_RESTART,
   SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE,
   SQLCOM_COMPOUND,
   SQLCOM_SHOW_GENERIC,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 6557f1c5ecd..9c35e6157be 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5337,6 +5337,29 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
     my_ok(thd);
 #else
     my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+#endif
+    break;
+  case SQLCOM_RESTART:
+#ifndef EMBEDDED_LIBRARY
+    if (check_global_access(thd,SHUTDOWN_ACL))
+      goto error;
+   {
+    const char* mpid = getenv("MARIADBD_PARENT_PID");
+    if(!mpid) {
+      my_error(ER_NO_PARENT_PID, MYF(0));
+      break;
+    }
+#ifndef _WIN32
+    if(kill(atoi(mpid), 0) == -1 && errno == ESRCH) {
+      my_error(ER_DEAD_MONITOR_PROCESS, MYF(0), mpid);
+      break;
+    }
+#endif
+   }
+    kill_mysql(thd, 1);
+    my_ok(thd);
+#else
+    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
 #endif
     break;
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index b83d9f21f5d..506e5033ef6 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1789,7 +1789,7 @@ rule:
         single_multi opt_wild
         opt_and
         select_var_list select_var_list_init help
-        opt_extended_describe shutdown
+        opt_extended_describe shutdown restart
         opt_format_json
         prepare execute deallocate
         statement
@@ -2097,6 +2097,7 @@ verb_clause:
         | replace
         | reset
         | resignal_stmt
+        | restart
         | revoke
         | rollback
         | savepoint
@@ -14869,6 +14870,11 @@ shutdown_option:
         }
         ;
 
+restart:
+        RESTART_SYM { Lex->sql_command= SQLCOM_RESTART; }
+        shutdown_option {}
+        ;
+
 /* change database */
 
 use:
@@ -15987,6 +15993,7 @@ keyword_verb_clause:
         | ROLLBACK_SYM          /* Verb clause. Reserved in Oracle */
         | SAVEPOINT_SYM         /* Verb clause. Reserved in Oracle */
         | SHUTDOWN              /* Verb clause                     */
+        | RESTART_SYM           /* Verb clause                     */
         | TRUNCATE_SYM          /* Verb clause. Reserved in Oracle */
         ;
 
@@ -16292,7 +16299,6 @@ keyword_sp_var_and_label:
         | REPLAY_SYM
         | REPLICATION
         | RESOURCES
-        | RESTART_SYM
         | RESUME_SYM
         | RETURNED_SQLSTATE_SYM
         | RETURNS_SYM
diff --git a/support-files/mariadb.service.in b/support-files/mariadb.service.in
index 1af0106e929..e086a11e61d 100644
--- a/support-files/mariadb.service.in
+++ b/support-files/mariadb.service.in
@@ -103,6 +103,9 @@ KillSignal=SIGTERM
 # Don't want to see an automated SIGKILL ever
 SendSIGKILL=no
 
+Environment=MARIADBD_PARENT_PID=${SYSTEMD_EXEC_PID}
+RestartForceExitStatus=16
+
 # Restart crashed server only, on-failure would also restart, for example, when
 # my.cnf contains unknown option
 Restart=on-abort
-- 
2.43.0

