#!/bin/bash
# This script contains functions common to proxysql-admin related scripts.
# (currently only Percona XtraDB cluster in combination with ProxySQL is supported)
# Version 2.0
###############################################################################################

# This program is copyright 2016-2022 Percona LLC and/or its affiliates.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# 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 or later
#
# You should have received a copy of the GNU General Public License version 2
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.

#-------------------------------------------------------------------------------
#
# Step 1 : Bash internal configuration
#


#-------------------------------------------------------------------------------
#
# Step 2 : Global variables
#


#-------------------------------------------------------------------------------
#
# Step 3 : Helper functions
#


# Executes a SQL query with the (fully) specified server
#
# Globals:
#   DEBUG
#   TIMEOUT
#
# Arguments:
#   1: lineno
#   2: the name of the user
#   3: the user's password
#   4: the hostname of the server
#   5: the port used to connect to the server
#   6: the query to be run
#   7: (optional) arguments to the mysql client
#   8: (optional) additional options, space separated
#      Available options:
#       "hide_output"
#         This will not show the output of the query when DEBUG is set.
#         Used to stop the display of sensitve information (such as passwords)
#         from being displayed when debugging.
#
function exec_sql() {
  local lineno=$1
  local user=$2
  local password=$3
  local hostname=$4
  local port=$5
  local query=$6
  local args=""
  local more_options=""
  local retvalue
  local retoutput
  local default_auth=""
  local defaults=""

  if [[ $# -ge 7 ]]; then
    args=$7
  fi

  if [[ $# -ge 8 ]]; then
    more_options=$8
  fi

  debug "$lineno" "exec_sql : $user@$hostname:$port ==> $query"

  if [[ $MYSQL_CLIENT_VERSION == "8.0" ]]; then
    default_auth="default-auth=mysql_native_password"
  fi

  defaults=$(printf '[client]\nuser=%s\npassword="%s"\nhost=%s\nport=%s\nconnect-timeout=%s\n%s' \
    "${user}" \
    "${password}" \
    "${hostname}" \
    "${port}" \
    "${TIMEOUT}" \
    "${default_auth}"
  )

  if [[ $USE_STDIN_FOR_CREDENTIALS -eq 1 ]]; then
    retoutput=$(printf "%s" "${defaults}" | mysql --defaults-file=/dev/stdin --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
    retvalue=$?
  else
    retoutput=$(mysql --defaults-file=<(echo "${defaults}") --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
    retvalue=$?
  fi

  if [[ $DEBUG -eq 1 ]]; then
    local number_of_newlines=0
    local dbgoutput=$retoutput

    if [[ " $more_options " =~ [[:space:]]hide_output[[:space:]] ]]; then
      dbgoutput="**** data hidden ****"
    fi

    if [[ -n $dbgoutput ]]; then
      number_of_newlines=$(printf "%s" "${dbgoutput}" | wc -l)
    fi

    if [[ $retvalue -ne 0 ]]; then
      debug "" "--> query failed $retvalue"
    elif [[ -z $dbgoutput ]]; then
      debug "" "--> query returned $retvalue : <query returned no data>"
    elif [[ ${number_of_newlines} -eq 0 ]]; then
      debug "" "--> query returned $retvalue : ${dbgoutput}"
    else
      debug "" "--> query returned $retvalue : <data follows>"
      printf "%s\n" "${dbgoutput}" | while IFS= read -r line; do
        debug "" "----> $line"
      done
    fi
  fi

  printf "%s" "${retoutput}"
  return $retvalue
}


# Executes a SQL query on proxysql
#
# Globals:
#   PROXYSQL_USERNAME
#   PROXYSQL_PASSWORD
#   PROXYSQL_HOSTNAME
#   PROXYSQL_PORT
#
# Arguments:
#   1: lineno
#   2: The SQL query
#   3: (optional) Additional arguments to the mysql client for the query
#   4: (optional) more options, see exec_sql
#
function proxysql_exec() {
  local lineno=$1
  local query=$2
  local args=""
  local more_options=""

  if [[ $# -ge 3 ]]; then
    args=$3
  fi

  if [[ -z $args ]]; then
    args="--skip-column_names"
  fi

  if [[ $# -ge 4 ]]; then
    more_options=$4
  fi

  exec_sql "$lineno" "$PROXYSQL_USERNAME" "$PROXYSQL_PASSWORD" \
           "$PROXYSQL_HOSTNAME" "$PROXYSQL_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on a specific node in the cluster
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#
# Arguments:
#   1: lineno
#   2: cluster host
#   3: cluster port
#   4: The SQL query
#   5: Additional arguments to the mysql client for the query
#   6: (optional) more options, see exec_sql
#
function cluster_exec() {
  local lineno=$1
  local cluster_host=$2
  local cluster_port=$3
  local query=$4
  local args=""
  local more_options=""

  if [[ $# -ge 5 ]]; then
    args=$5
  fi

  if [[ $# -ge 6 ]]; then
    more_options=$6
  fi

  exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \
           "$cluster_host" "$cluster_port" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on the CLUSTER_HOSTNAME/CLUSTER_PORT
# specified in the config file.
#
# Globals:
#   CLUSTER_USERNAME
#   CLUSTER_PASSWORD
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#
# Arguments:
#   1: lineno
#   2: The SQL query
#   3: Additional arguments to the mysql client for the query
#   4: (optional) more options, see exec_sql
#
function mysql_exec() {
  local lineno=$1
  local query=$2
  local args=""
  local more_options=""

  if [[ $# -ge 3 ]]; then
    args=$3
  fi

  if [[ $# -ge 4 ]]; then
    more_options=$4
  fi

  cluster_exec "$lineno" "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# Executes a SQL query on a cluster ndde with the monitor credentials
#
# Globals:
#   CLUSTER_HOSTNAME
#   CLUSTER_PORT
#
# Arguments:
#   1: lineno
#   2: The monitor username
#   3: The monitor password
#   4: Additional arguments to the mysql client for the query
#   5: The SQL query
#   6: (optional) more options, see exec_sql
#
function monitor_exec() {
  local lineno=$1
  local user=$2
  local password=$3
  local args=$4
  local query=$5
  local more_options=""

  if [[ $# -ge 6 ]]; then
    more_options=$7
  fi

  exec_sql "$lineno" "$user" "$password" \
           "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
           "$query" "$args" "$more_options"

  return $?
}


# This will move the configuration from memory to the runtime (load)
# and from memory to disk (save)
#
# Globals:
#   None
#
# Arguments:
#   1: the proxysql data that is being loaded and saved
#      (for example "MYSQL USERS" or "MYSQL SERVERS").
#   2: the lineno where this function was invoked
#   3: (optional) call "SAVE $1 FROM RUNTIME" after
#      loading data to runtime.  This is done for
#      MYSQL USERS to load the encrypted version of the
#      password field.
#
# This function will exit the program if an error occurs while
# loaded to runtime or saving to disk.
#
function proxysql_load_to_runtime_save_to_disk() {
  local data_type=$1
  local lineno=$2
  local reload_from_runtime=0

  if [[ $# -ge 3 ]]; then
    reload_from_runtime=$3
  fi

  proxysql_exec "$LINENO" "LOAD ${data_type} TO RUNTIME"
  check_cmd $? "$lineno" "Failed to load the ${data_type} configuration to runtime."\
                       "\n-- Please check the ProxySQL configuration and status."
  debug "$lineno" "Loaded ${data_type} to runtime"

  if [[ $reload_from_runtime -eq 1 ]]; then
    # This has a specific purpose for the MYSQL USERS
    # This will cause the password field to be loaded with the encrypted version
    # of the password field
    proxysql_exec "$LINENO" "SAVE ${data_type} FROM RUNTIME"
    check_cmd $? "$lineno" "Failed to load the ${data_type} configuration from the runtime."\
                         "\n-- Please check the ProxySQL configuration and status."
    debug "$lineno" "Saved ${data_type} from runtime"
  fi

  proxysql_exec "$LINENO" "SAVE ${data_type} TO DISK;"
  check_cmd $? "$lineno" "Failed to save the ${data_type} configuration to disk."\
                       "\n-- Please check the ProxySQL configuration and status."
  debug "$lineno" "Saved ${data_type} to disk"
}


# Update mysql server version details in proxysql db
# Globals:
#   WRITER_HOSTGROUP_ID
#   CLUSTER_HOSTNAME (overwrites)
#   CLUSTER_PORT (overwrites)
#
# Arguments:
#   None
function update_mysql_version()
{
  local cluster_node

  # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
  cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
  check_cmd $? "$LINENO" "Could not find a primary cluster node"

  # Reset the central cluster node (so that calls to mysql_exec) will
  # work with this new node, rather than the node in the config file
  CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
  CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
  cluster_connection_check

  proxysql_mysql_version_string=$(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name like 'mysql-server_version'")
  check_cmd $? "$LINENO"  "Failed to select the mysql-server_version variables from ProxySQL."\
                          "\n-- Please check the ProxySQL connection parameters and status."
  mysql_version_string=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'-' -f1 )
  check_cmd $? "$LINENO"  "Failed to select the mysql version info from Cluster node."\
                          "\n-- Please check the PXC connection parameters and status."
  if [[ $proxysql_mysql_version_string != $mysql_version_string ]]; then
    proxysql_exec "$LINENO" \
    "UPDATE global_variables
      SET variable_value='$mysql_version_string'
      WHERE variable_name='mysql-server_version';"
    check_cmd $? "$LINENO"  "Failed to set the mysql-server_version variables in ProxySQL."\
                          "\n-- Please check the ProxySQL connection parameters and status."
    echo "ProxySQL MySQL version changed to $mysql_version_string"
    proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO
  fi
}

# Adds an application user to ProxySQL
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   CLUSTER_HOSTNAME (overwrites)
#   CLUSTER_PORT (overwrites)
#
# Arguments:
#   None
#
function adduser(){
  debug "$LINENO" "adduser ()"

  local cluster_node

  # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
  cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
  check_cmd $? "$LINENO" "Could not find a primary cluster node"

  # Reset the central cluster node (so that calls to mysql_exec) will
  # work with this new node, rather than the node in the config file
  CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
  CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
  cluster_connection_check

  local cluster_app_write_username
  local cluster_app_write_password
  local safe_cluster_app_write_username
  local safe_cluster_app_write_password

  echo -e "\nAdding PXC application user to the ProxySQL database"
  echo -n "Enter the PXC application user name: "
  read -r cluster_app_write_username
  while [[ -z "$cluster_app_write_username" ]]
  do
    echo -n "No input entered. Enter the PXC application user name: "
    read -r cluster_app_write_username
  done
  safe_cluster_app_write_username=${cluster_app_write_username//\'/\'\'}

  read -r -s -p  "Enter the PXC application user password: " cluster_app_write_password
  while [[ -z "$cluster_app_write_password" ]]
  do
    read -r -s -p  "No input entered. Enter the PXC application user password: " cluster_app_write_password
  done
  safe_cluster_app_write_password=${cluster_app_write_password//\'/\'\'}

  # check to see if the user already exists
  local check_user
  local check_cluster_user

  check_user=$(proxysql_exec "$LINENO" \
                  "SELECT username
                   FROM mysql_users
                   WHERE username='$safe_cluster_app_write_username'")
  check_cmd $? "$LINENO" "Could not retrieve the user from ProxySQL."\
                       "\n-- Check the ProxySQL connection parameters and status."
  if [[ -n "$check_user" ]]; then
    error "$LINENO" "The application user '$cluster_app_write_username' already exists in ProxySQL."
    exit 1
  fi

  check_cluster_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$safe_cluster_app_write_username'")
  check_cmd $? "$LINENO" "Could not retrieve the user from PXC."\
                       "\n-- Check the PXC connection parameters and status."
  if [[ -z "$check_cluster_user" ]]; then
    local check_param
    echo -e "\n\n"
    read -r -p "The application user '$cluster_app_write_username' does not exist in PXC. Would you like to proceed [y/n] ? " check_param
    case $check_param in
      y|Y)
        proxysql_exec "$LINENO" \
          "INSERT INTO mysql_users
            (username,password,active,default_hostgroup)
           VALUES
            ('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);"
        check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
                             "\n-- Please check the ProxySQL connection parameters and status."
        echo -e "\nPlease create the user ${BD}$cluster_app_write_username${NBD} in PXC to access the application through ProxySQL"
      ;;
      n|N)
        exit 0
      ;;
      *)
        error "" "Please type [y/n]!"
        exit 1
      ;;
    esac
  else
    proxysql_exec "$LINENO" \
      "INSERT INTO mysql_users
          (username,password,active,default_hostgroup)
       values
          ('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);"
    check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
                         "\n-- Please check the ProxySQL connection parameters and status."
  fi

  proxysql_load_to_runtime_save_to_disk "MYSQL USERS" "$LINENO" "1"
}


# Synchronizes the users between ProxySQL and PXC
#
# This function was created to auto sync all the existing users already
# in MySQL to proxySQL's mysql_users table.  As there is not much point
# in having users in ProxySQL that don't exist in MySQL, this function
# will delete any users from ProxySQL that were not found in MySQL.
#
# Going forward you can add/remove application users in MySQL then
# rerun proxysql-admin with the --syncusers switch to replicate the changes
# to ProxySQL.
#
# LIMITATIONS: Will not work properly in cases where the same user name
#              exists in MySQL with several hosts and different passwords.
#              This will cause ProxySQL to throw a "UNIQUE constraint failed"
#              error message.
#
# Globals:
#   WRITER_HOSTGROUP_ID
#   SYNCUSERS
#   CLUSTER_HOSTNAME (overwrites)
#   CLUSTER_PORT (overwrites)
#   SINGLE_SERVER
#
# Arguments:
#   None
#
function syncusers() {
  debug "$LINENO" "syncusers ()"

  local mysql_version
  local password_field
  local changes_made=0

  # If a single server has been specified with --server, use that server
  # and skip the cluster check (it may be a standalone node)
  if [[ -n $SINGLE_SERVER ]]; then
    local ws_address

    ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER")
    CLUSTER_HOSTNAME=$(echo "$ws_address" | cut -d' ' -f1)
    CLUSTER_PORT=$(echo "$ws_address" | cut -d' ' -f2)

    echo -e "\nSyncing user accounts from server(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"

  else
    local cluster_node

    # Get current MySQL users, filter out header row and mysql.sys user
    # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
    cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
    check_cmd $? "$LINENO" "Could not find a primary cluster node"

    # Reset the central cluster node (so that calls to mysql_exec) will
    # work with this new node, rather than the node in the config file
    CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
    CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)

    echo -e "\nSyncing user accounts from PXC(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"

  fi

  # Check that we can connect to CLUSTER_HOSTNAME:CLUSTER_PORT
  cluster_connection_check

  mysql_version=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'.' -f1,2 )
  check_cmd $? "$LINENO" "Could not connect to the server."\
                       "\n-- Please check the server connection parameters and status."

  case $mysql_version in
    5.5 | 5.6)
      password_field="Password"
      ;;
    5.7 | 8.0)
      password_field="authentication_string"
      ;;
    10.0 | 10.1 | 10.2 | 10.3 | 10.4 | 10.5 | 10.6)
      password_field="Password"
      ;;
     *)
      error "$LINENO" "Unexpected database server version: ${mysql_version}"\
                    "\n-- This version of proxysql-admin needs to be updated."
      exit 1
      ;;
  esac

  # Filter out the internal system users
  mysql_users=$(mysql_exec "$LINENO" "SELECT User,${password_field} FROM mysql.user where ${password_field}!=''" "" "hide_output" |
                  grep -E -v "^(mysql.sys|mysql.session|mysql.infoschema|mysql.pxc)" |
                  sort |
                  uniq )
  check_cmd $? "$LINENO" "Failed to load the user list from the server."\
                       "\n-- Please check the server connection parameters and status."

  #Checking whether user is part of proxysql admin user list
  # Get current ProxySQL users and filter out header row
  proxysql_users=$(get_proxysql_users)

  # TEST FOR USERS THAT EXIST IN MYSQL BUT NOT IN PROXYSQL HERE AND ADD

  # Escape all backslashes here, because the read will evaluate
  # the escaped chars
  mysql_users=${mysql_users//\\/\\\\}
  while read line; do
    if [[ -z $line ]]; then
      continue
    fi

    mysql_user=$line
    debug "$LINENO" "Processing MySQL user:${mysql_user}"

    local match=0
    proxysql_users=${proxysql_users//\\/\\\\}
    while read pline; do
      if [[ -z $pline ]]; then
        continue
      fi
      if [ "$pline" == "$mysql_user" ];then
        match=1
        break
      fi
    done< <(printf "%s\n" "${proxysql_users}")

    if [[ $match -eq 0 ]]; then
      local user password
      user=$(echo "$mysql_user" | cut -f1)
      password=$(echo "$mysql_user" | cut -f2)

      # escape SQL input
      # Since we're using single quotes within the SQL statement, only need
      # to escape the single quotes for SQL
      password=${password//\'/\'\'}

      # Check if same username exists with a different password
      # delete the user to recreate.
      while read pline; do
        if [[ -z $pline ]]; then
          continue
        fi
        local puser=$(echo "$pline" | cut -f1)
        if [[ "$puser" == "$user" ]]; then
          echo "Removing existing user from ProxySQL: $user"
          proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${user}' and default_hostgroup=$WRITER_HOSTGROUP_ID"
          check_cmd $? "$LINENO" "Failed to delete the user ($user) from ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
          proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${user}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)"
          check_cmd $? "$LINENO" "Failed to delete the query rule for user ($user) from ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
          changes_made=1
          break
        fi
      done< <(printf "%s\n" "${proxysql_users}")

      local is_proxysql_admin_user
      is_proxysql_admin_user=$(proxysql_admin_user_check "$user")
      if [[ $is_proxysql_admin_user -eq 1 ]]; then
        echo -e "\nNote : '$user' is in proxysql admin user list, this user cannot be added to ProxySQL"\
                "\n-- (For more info, see https://github.com/sysown/proxysql/issues/709)"
      else
        check_user=$(proxysql_exec "$LINENO" "SELECT username from mysql_users where username='${user}'")
        if [[ -z $check_user ]]; then
          echo "Adding user to ProxySQL: $user"
          proxysql_exec "$LINENO" \
            "INSERT INTO mysql_users
              (username, password, active, default_hostgroup)
             VALUES
              ('${user}', '${password}', 1, $WRITER_HOSTGROUP_ID)"
          check_cmd $? "$LINENO" "Failed to add the user ($user) from PXC to ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
          if [[ $ADD_QUERY_RULE -eq 1 ]];then
            add_query_rule "${user}"
          fi
          changes_made=1
        else
          echo "Cannot add the user (${user}). The user (${user}) already exists in ProxySQL database with different hostgroup."
          check_user=""
        fi
      fi
    fi
  done< <(printf "%s\n" "${mysql_users}")

  if [[ $SYNCUSERS -eq 1 ]]; then
    # TEST FOR USERS THAT EXIST IN PROXYSQL BUT NOT IN MYSQL HERE AND REMOVE
    # Again get all users
    proxysql_users=$(get_proxysql_users)

    while read pline; do
      if [[ -z $pline ]]; then
        continue
      fi
      proxysql_user=$pline
      debug "$LINENO" "Processing ProxySQL user:${proxysql_user}"

      local match=0
      while read -r line; do
        if [[ -z $line ]]; then
          continue
        fi
        local proxysql_user_name mysql_user_name
        proxysql_user_name=$(echo "${proxysql_user}" | cut -f1)
        mysql_user_name=$(echo "${line}" | cut -f1)
        debug "$LINENO" "Comparing proxysql user:${proxysql_user_name} to mysql user:${mysql_user_name}"
        if [ "$proxysql_user_name" == "$mysql_user_name" ];then
          match=1
          break
        fi
      done< <(printf "%s\n", "$mysql_users")

      if [ "$match" -eq 0 ];then
        # Delete the ProxySQL user
        local user
        user=$(echo "$proxysql_user" | cut -f1)
        echo -e "\nRemoving (non-MySQL) user from ProxySQL: $user"
        proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${user}' and default_hostgroup=$WRITER_HOSTGROUP_ID"
        check_cmd $? "$LINENO" "Failed to delete the user ($user) from ProxySQL database."\
                             "\n-- Please check the ProxySQL connection parameters and status."
        proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${user}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)"
        check_cmd $? "$LINENO" "Failed to delete the query rule for user ($user) from ProxySQL database."\
                               "\n-- Please check the ProxySQL connection parameters and status."
        changes_made=1
      fi
    done< <(printf "%s\n" "$proxysql_users")
  fi

  if [[ $changes_made -eq 1 ]]; then
    proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO
    proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO
  fi
}

# Returns a list of users from the ProxySQL database
#
# Globals:
#   WRITER_HOSTGROUP_ID
#
# Arguments:
#   None
#
function get_proxysql_users() {
  local proxysql_users
  proxysql_users=$(proxysql_exec "$LINENO" \
                    "SELECT username,password
                      FROM mysql_users
                      WHERE password!='' AND default_hostgroup=$WRITER_HOSTGROUP_ID" "" "hide_output")
  check_cmd $? "$LINENO" "Failed to load user list from ProxySQL database."\
                       "\n-- Please check the ProxySQL connection parameters and status."
  proxysql_users=$(echo "$proxysql_users" |
                      sort |
                      uniq )
  printf "%s" "$proxysql_users"
}


# Checks if a user is a ProxySQL admin user
#
# Globals:
#   None
#
# Arguments:
#   1: the name of the use to be checked
#
# Outputs (to stdout):
#   1 if the user is a proxysql admin user
#   0 if the user is not a proxysql admin user
#
function proxysql_admin_user_check(){
  local userchk=$1
  local proxysql_admin_users
  local is_proxysql_admin_user

  proxysql_admin_users=($(proxysql_exec "$LINENO" \
                            "select variable_value
                             from global_variables
                             where variable_name like 'admin-%_credentials'" |
                              cut -d':' -f1 |
                              grep -v variable_value))
  if [[ " ${proxysql_admin_users[@]} " =~ [[:space:]]${userchk}[[:space:]] ]]; then
    is_proxysql_admin_user=1
  else
    is_proxysql_admin_user=0
  fi
  printf "%s" "$is_proxysql_admin_user"
}


# Returns success if the native scheduler is in use.
# Returns success(0) if the native proxysql scheduler is in use.
# Else returns failure(1)
#
# In this case, the native scheduler is used if entries are
# found in mysql_galera_hostgroups, mysql_group_replication_hostgroups,
# or mysql_replication_hostgroups.
#
# Globals:
#   None
#
# Arguments:
#   Parameter 1:  line number
#
# Returns:
#   0 (success)   the native scheduler is being used
#   1 (failure)   the native scheduler is not being used
#   255 (failure) error, unable to connect to proxysql
#
function is_native_scheduler_in_use()
{
  local lineno=$1
  local count

  count=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
            FROM runtime_mysql_group_replication_hostgroups
            WHERE active=1")
  [[ $? -ne 0 ]] && return 255
  [[ $count -gt 0 ]] && return 0

  count=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
            FROM runtime_mysql_galera_hostgroups
            WHERE active=1")
  [[ $? -ne 0 ]] && return 255
  [[ $count -gt 0 ]] && return 0

  count=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
            FROM runtime_mysql_replication_hostgroups")
  [[ $? -ne 0 ]] && return 255
  [[ $count -gt 0 ]] && return 0

  return 1
}

function is_go_scheduler_in_use()
{
  local lineno=$1
  local count

  count=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
            FROM runtime_scheduler
            WHERE active=1 AND
            filename LIKE '%pxc_scheduler_handler'")
  [[ $? -ne 0 ]] && return 255
  [[ $count -gt 0 ]] && return 0
  return 1
}

function is_other_scheduler_in_use()
{
  local lineno=$1
  local count

  count=$(proxysql_exec "$LINENO" \
          "SELECT COUNT(*)
            FROM runtime_scheduler
            WHERE active=1 AND
            filename NOT LIKE '%pxc_scheduler_handler'")
  [[ $? -ne 0 ]] && return 255
  [[ $count -gt 0 ]] && return 0
  return 1
}


function native_scheduler_capability_check()
{
  local lineno=$1
  if is_go_scheduler_in_use "${lineno}"; then
    # TODO: better error messages
    error "${lineno}" "Unable to setup the native ProxySQL scheduler." \
                    "\n---- The Percona go scheduler is currently in use" \
                    "\n---- and must be removed."
    return 1;
  fi

  if is_other_scheduler_in_use "${lineno}"; then
    # TODO: better error messages
    error "${lineno}" "Unable to setup the native ProxySQL scheduler." \
                    "\n---- Another scheduler is currently in use" \
                    "\n---- and must be removed."
    return 1;
  fi
  return 0;
}

function go_scheduler_capability_check()
{
  local lineno=$1
  if is_native_scheduler_in_use "${lineno}"; then
    # TODO: better error messages
    error "${lineno}" "Unable to setup the Percona go scheduler." \
                    "\n---- The ProxySQL native scheduler is in use" \
                    "\n---- and must be removed."
    return 1;
  fi

  if is_other_scheduler_in_use "${lineno}"; then
    # TODO: better error messages
    error "${lineno}" "Unable to setup the Percona go scheduler." \
                    "\n---- Another scheduler is in use" \
                    "\n---- and must be removed."
    return 1;
  fi

  return 0;
}
