WL#12002: SET system variable values as a component service

Affects: Server-8.0   —   Status: Complete

The replication components need to be able to set system variable values and
provoke the linked effects around these.
E.g. they need to be able to set the OFFLINE_MODE and the SUPER_READ_ONLY variables.

Thus we plan to use this occasion to introduce a more generic and simplified
"write the system variable" values API that also generally useful and does not
suffer the over-engineering issues the current very complex C++ API for setting
system variables has.

WL#9424 adds support to read the system variables values, so that part is
covered and this the current worklog will focus on setting the values only. 

User/Dev Stories
================

InnoDB Cluster Stories (these are the ones we should address
completely):

- As a MySQL GR developer, I want to BLOCK ALL NEW and UNPRIVILEGED
  connections if during startup the server is unable to join the
  group, so that no application connects and executes against the
  server once the startup finishes successfully but without joining a
  group.

- As a MySQL GR developer, I want to KILL ALL EXISTING connections if
  at any point in time, group replication is stopped explicitly by the
  user or implicitly due to an error, so that existing applications
  connected to the server do not read stale data.

- As a MySQL GR developer, I want to BLOCK ALL NEW and UNPRIVILEGED
  connections if at any point in time, group replication is stopped
  explicitly by the user or implicitly due to an error, so that
  existing applications connected to the server do not read stale
  data.

- As a MySQL GR developer, I want to allow only privileged users
  (SUPER?) to connect to the MySQL server, once the server has entered
  an error state after it was in the group or after it failed to join
  the group, so that a privileged user can go in, fix the issue and
  bring the server back ONLINE in the group.

Async replication stories (these are stories we could address if the
service is designed and implemented in a way that we could leverage
for streaming replication):

- As a MySQL Async replication developer, I want to KILL ALL EXISTING
  connections if at any point in time, source-replica replication is
  stopped explicitly by the user or implicitly due to an applier
  error, so that existing applications connected to the server do not
  read stale data.

- As a MySQL Async replication developer, I want to BLOCK ALL NEW and
  UNPRIVILEGED connections if at any point in time, source-replica
  replication is stopped explicitly by the user or implicitly due to
  an error, so that existing applications connected to the server do
  not read stale data.

- As a MySQL async replication developer, I want to only allow
  privileged users (SUPER?) to connect to the MySQL server, once the
  server has entered an error state after it was happily replicating
  from the source or after it failed to start replication, so that a
  privileged user can go in, fix the issue and bring the server back
  in the replication stream.

Generic stories (these are stories we could address if the service is
designed and implemented in a generic way, usable by a
plugin/component writer):

- As a MySQL plugin developer, I want to KILL ALL EXISTING connections
  if at any point in time a certain condition/guarantee/property in
  the plugin is met, so that applications relying on that
  condition/guarantee/property are not broken.

- As a MySQL plugin developer, I want to BLOCK ALL NEW and
  UNPRIVILEGED connections if at any point in time a certain
  condition/guarantee/property in the plugin is met, so that
  applications relying on that condition/guarantee/property are not
  broken.

- As a MySQL plugin developer, I want to only allow privileged users
  (SUPER?) to connect to the MySQL server, once the server has failed
  to meet some condition imposed by the plugin, so that a privileged
  user can go in, fix the issue and bring the server back online.
FR1: all variables that are settable via the current base class sys_var
(currently everything except SET PASSWORD and SET collection_client) can be set.
FR2: variables will be fully set one at a time: i.e. if the user is setting
multiple values at once they'll need to handle rollbacks.
NF3: the method will keep a read lock on LOCK_system_variables_hash for the
duration of it running. I.e. no variables can be registered or unregistered during the time of execution.
FR4: To be settable via the method the variable must accept string values or be
able to convert string values to a native value.
FR5: if there are any side effects from setting the value these will affect the
current transaction in the user session supplied via THD, provided the temp one is not used
NF6: The application will need to check the current THD's diagnostic context for more detailed errors.
NF7: it's safer to not be calling this from user session threads or where the
THD passed is not also the current thread. 

REQUIREMENTS DERIVED FROM THE USER STORIES
==========================================

- If the server fails to join the group during startup, the service
  MUST provide functionality for the plugin to instruct the server to
  BLOCK NEW and UNPRIVILEGED connections.

- If the server drops out of the group involuntarily (e.g., network
  partition), the service MUST provide functionality for the plugin to
  instruct the server to KILL all existing connections.

- If the server drops out of the group involuntarily (e.g., network
  partition), service MUST provide functionality for the plugin to
  instruct the server to BLOCK NEW and UNPRIVILEGED connections.

- If the server stops because of an error in the applier (e.g.,
  duplicate key violation), the service MUST provide functionality for
  the plugin to instruct the server to KILL all existing connections.

- If the server stops because of an error in the applier (e.g.,
  duplicate key violation), the service MUST provide functionality for
  the plugin to instruct the server to BLOCK NEW and UNPRIVILEGED
  connections.

- If the group replication plugin is stopped by the user, the service
  MUST provide functionality for the plugin to instruct the server to
  KILL ALL existing UNPRIVILEGED connections.

- The service implementation MUST allow for new and privilege users
  (SUPER?) to connect after all connections are KILLED.
BEGIN_SERVICE_DEFINITION(mysql_system_variable_update_string)
  
 /**
    Sets the value of a system variable to a new string value.
  
    @param thd thread session handle. if NULL a temp one will be created and then
    removed.
    @param set_variable_type: one of [GLOBAL, PERSIST, PERSIST_ONLY].
      If NULL, then assumes GLOBAL.
    @param variable_name_base: a mysql string of the variable name prefix. NULL
    of none
    @param variable_name: a mysql string of the variable name
    @param variable_value: a mysql string to set as value
    @retval FALSE: success
    @retval TRUE: failure, see THD if supplied.
 */
 DECLARE_BOOL_METHOD(set,
                     (MYSQL_THD thd, const char *set_variable_type,
                      my_h_string variable_name_base, my_h_string variable_name,
                      my_h_string variable_value));
 END_SERVICE_DEFINITION(mysql_system_variable_update_string)
Altering the current parser functionality to use the new service is a subject to
a different worklog !

The implementation is going to look approximately like this:
system_variable_set(THD *, const char *variable_type, char *variable_name, const
char *variable_value)
{

  LOCK(system_variables_lock);
  sys_var var(string_to_type(variable_type), lookup(variable_name), NULL,
Item_string(variable_value));
  sql_set_variables(thd, &var, false);
  UNLOCK(system_variables_lock);
}