#!/bin/bash
# ================================================================================
#
# LOCALDUMP :: MYSQL / MARIADB
# create gzipped plain text backups from each scheme
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@iml.unibe.ch>
# ds - Daniel Schueler <daniel.schueler@iml.unibe.ch>
#
# 2016-11-10  ah,ds  v0.8  needs to be testet
# 2017-03-28  .....  v1.0  added restore
# 2022-01-20  ah     v1.1  fixes with shellcheck
# 2022-02-14  ah     v2.0  rewrite with class like functions
# 2022-02-18  ah     v2.1  WIP: added counters
# 2022-03-17         v2.2  WIP: add lines with prefix __DB__
# 2022-03-21         v2.3  Check if the created dump contains "insert" 
# 2022-03-24         v2.4  On empty dumps: detect if source db is empty too
# 2023-10-06         v2.5  mysqldump params can be customized in jobs/env
# 2023-10-02         v2.6  Detect a connect error before backup; remove unneeded code
# ================================================================================

if [ -z "$LOCALDUMP_LOADED" ]; then
  echo "ERROR: you cannot start $(basename $0) directly. Start localdump.sh instead."
  rc+=1
  exit 1
fi

# --------------------------------------------------------------------------------
# CONFIG
# --------------------------------------------------------------------------------

# counters
typeset -i mysql_COUNT_CREATE=0
typeset -i mysql_COUNT_DUMPS=0
typeset -i mysql_COUNT_DB=0
typeset -i mysql_COUNT_ERRORS=0

# --------------------------------------------------------------------------------
# PUBLIC FUNCTIONS I :: DB SCHEME - METHODS LOW LEVEL
# --------------------------------------------------------------------------------

# create a database scheme
# param  string  name of the dabase scheme
function mysql.db.create(){
  local _dbname=$1
  echo "CREATE DATABASE IF NOT EXISTS \`${_dbname}\`;" | mysql ${BACKUP_PARAMS}
  fetchrc >/dev/null
  test $myrc -eq 0 && mysql_COUNT_CREATE+=1
  test $myrc -eq 0 || mysql_COUNT_ERRORS+=1
}

# dump [database] --> [file] 
# dump a single database into given file
#
# global LD_MYSQL_DUMP_PARAMS  string  parameters from file jobs/env_defaults
#
# param  string  name of database to dump
# param  string  name of output file
function mysql.db.dump(){

  local _dbname=$1
  local _dumpfile=$2

  if mysqldump ${BACKUP_PARAMS} $LD_MYSQL_DUMP_PARAMS --result-file="$_dumpfile" "$_dbname" 2>&1; then
    fetchrc >/dev/null

    if ! grep -q -iE "(CREATE|INSERT)" "$_dumpfile"
    then
      local _iTables
      typeset -i _iTables
      _iTables=$( mysql ${BACKUP_PARAMS} --skip-column-names --batch -e "use $_dbname; show tables ;" | wc -l )
      if [ $_iTables -eq 0 ];
      then
        echo "EMPTY DATABASE ... "
      else
        echo "ERROR: no data - the dump doesn't contain any CREATE or INSERT statement."
        # force an error
        false; fetchrc >/dev/null
      fi
    fi
  else
    fetchrc
  fi

  test $myrc -eq 0 && mysql_COUNT_DUMPS+=1
  test $myrc -eq 0 || mysql_COUNT_ERRORS+=1

}

# import [file] --> [database] 
# import a single db dump into a given db
# param  string  name of file
# param  string  name of target db scheme
function mysql.db.import(){
  local _dumpfile=$1
  local _dbname=$2
  zcat "$_dumpfile" | mysql $BACKUP_PARAMS "${_dbname}"
  fetchrc >/dev/null
  test $myrc -eq 0 && mysql_COUNT_IMPORT+=1
  test $myrc -eq 0 || mysql_COUNT_ERRORS+=1
}

# show a list of existing databases
function mysql.db.list(){
  # mysql -Ee "show databases ;" | grep "^Database:" | awk '{ print $2 }'
  local _result=$( mysql ${BACKUP_PARAMS} -Ee "show databases ;" )
  fetchrc >/dev/null
  test $myrc -eq 0 && mysql_COUNT_DB=$( echo "$_result" | grep -c "^Database:" ) 
  test $myrc -eq 0 && echo "$_result" | grep "^Database:" | awk '{ print $2 }'
  test $myrc -eq 0 || mysql_COUNT_ERRORS+=1
}

# --------------------------------------------------------------------------------
# PUBLIC FUNCTIONS II :: HIGH LEVEL
# --------------------------------------------------------------------------------


# start database backup of all schemes of this service
# no prameters
function mysql.backup(){

  local _dbname
  local _outfile

  create_targetdir

  # it sets the variable "mysql_COUNT_DB"
  mysql.db.list >/dev/null 2>&1

  if [ $mysql_COUNT_DB -eq 0 ]; then
    rc=1
    echo "ERROR: no databases found or an connect error occured."
  else
    echo Found databases: $mysql_COUNT_DB
    for _dbname in $( mysql.db.list )
    do
      echo -n "__DB__${SERVICENAME} backup $_dbname ... "
      _outfile="${BACKUP_TARGETDIR}/$(get_outfile ${_dbname}).sql"

      mysql.db.dump "$_dbname" "$_outfile"
      db._compressDumpfile "$_outfile"

    done
  fi
}

# restore database dump file into database
# param  string  database dump file (gzipped)
# param  string  optional: database to import; default: database is parsed from file
function mysql.restore(){

  local _infile=$1
  local _dbname=$2

  if [ -z "$_dbname" ]; then
    h2 "analyze dump $_infile"
    _dbname=$(guessDB $_infile)
    echo "detected db schema from file: [${_dbname}]"
  else
    echo "db schema from param 2: [${_dbname}]"
  fi

  echo

  echo import to "$_dbname"...

  h2 ensure that database exists ...
  color cmd
  # echo "CREATE DATABASE IF NOT EXISTS ${_dbname};" | mysql
  mysql.db.create "${_dbname}"
  color reset

  h2 import ...
  ls -l "$_infile"
  echo "import to database [${_dbname}]"
  color cmd
  # zcat "$_infile" | mysql "${_dbname}"
  mysql.db.import "$_infile" "${_dbname}"
  fetchrc
  color reset

}

# WIP: show status
# function mysql.status(){
#   h2 "WIP: Status"
#   h3 "Databases (max. 15)"
#   mysql.db.list | head -15
#   h3 "Counters"
#   cat <<EOSTATUS

# found Dbs: $mysql_COUNT_DB
# created  : $mysql_COUNT_CREATE
# dumped   : $mysql_COUNT_DUMPS

# ERRORS   : $mysql_COUNT_ERRORS

# EOSTATUS
# }

# --------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------

action=$1
shift 1
"${SERVICENAME}.$action" $*

echo "__DB__$SERVICENAME INFO: $0 $action $* [$SERVICENAME] final returncode rc=$rc"

# --------------------------------------------------------------------------------
