#!/bin/bash
# ================================================================================
#
# LOCALDUMP :: POSTGRES
# 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  v1.0
# 2017-03-29  .....  v1.1  added restore
# 2022-01-20         v1.2  fixes with shellcheck
# 2022-03-17         v1.3  WIP: add lines with prefix __DB__
# 2023-09-20         v1.4  FIX could not change directory to "/root": Permission denied
# 2024-10-02  ah     v1.5  rename backup and restore function
# 2024-12-13  ah     v1.6  backup uses a snapshot db
# 2024-12-16  ah     v1.7  on snapshot mode it can fallback for single database to normal pg_dump of origin
# ================================================================================

if [ -z "$BACKUP_TARGETDIR" ]; then
  echo "ERROR: you cannot start $(basename $0) directly"
  rc=$rc+1
  exit 1
fi

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

  # unix user of postgres database
  PGUSER=postgres


# --------------------------------------------------------------------------------
# FUNCTION
# --------------------------------------------------------------------------------

# backup all databases
function pgsql.backup(){

  local DATABASE
  local SNAPSHOTDB
  local OUTFILE
  local snapshotprefix="snapshot_"
  local bUseSnapshot
  local bUseSnapshot4DB

  typeset -i bUseSnapshot=0

  create_targetdir

  # ----- change owner of directory because of su command
  chown ${PGUSER}. "${BACKUP_TARGETDIR}"

  # ----- GO

  # prevent could not change directory to "/root": Permission denied
  (
    cd /tmp

    # Detect a readonly or writeable postgres host.
    TESTDB="imlbackup_createtest"
    su ${PGUSER} -c "dropdb ${TESTDB}" >/dev/null 2>&1
    if su ${PGUSER} -c "psql ${BACKUP_PARAMS} -c \"CREATE DATABASE ${TESTDB};\"" >/dev/null 2>&1
    then
      bUseSnapshot=1
      su ${PGUSER} -c "dropdb ${TESTDB}"
      echo "INFO: enabling snapshots"
    else
      echo "INFO: create database failed for snapshots - dumping databases directly"
    fi

    sSqlGetDblist="select datname from pg_database where not datistemplate and datallowconn order by datname;"
    for DATABASE in $( su ${PGUSER} -c "psql ${BACKUP_PARAMS} -At -c '$sSqlGetDblist' postgres" 2>/dev/null | grep -v "^$snapshotprefix" )
    do
      echo -n "__DB__${SERVICENAME} backup $DATABASE ... "

      bUseSnapshot4DB=$bUseSnapshot

      SNAPSHOTDB="${DATABASE}"
      test $bUseSnapshot4DB -eq 1 && SNAPSHOTDB="${snapshotprefix}${DATABASE}"

      OUTFILE="${BACKUP_TARGETDIR}/$(get_outfile ${DATABASE}).sql"

      myrc=0

      # drop snapshot db first - just in case
      if [ $bUseSnapshot4DB -eq 1 ]; then 
        echo -n " snapshot ... "
        su ${PGUSER} -c "dropdb ${SNAPSHOTDB}" >/dev/null 2>&1
        if ! su ${PGUSER} -c "psql ${BACKUP_PARAMS} -c \"CREATE DATABASE ${SNAPSHOTDB} WITH TEMPLATE ${DATABASE};\"" >/dev/null 2>&1; then
          echo -n " nope, using origin ... "
          bUseSnapshot4DB=0
          SNAPSHOTDB="${DATABASE}"
        fi
      fi
      if [ $myrc -eq 0 ]; then

        echo -n "backup ... "
        if su ${PGUSER} -c "pg_dump ${BACKUP_PARAMS} -Fp ${SNAPSHOTDB} >$OUTFILE"; then
          fetchrc >/dev/null
          test $bUseSnapshot4DB -eq 1 && ( 
            echo -n "delete snapshot ... "
            su ${PGUSER} -c "dropdb ${SNAPSHOTDB}"
          )

          db._compressDumpfile "$OUTFILE"
        else
          fetchrc
          test $bUseSnapshot4DB -eq 1 && su ${PGUSER} -c "dropdb ${SNAPSHOTDB}"
        fi
      else
        cecho error "ERROR!"
      fi

    done
  )
}



# 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 pgsql.restore(){
  sMyfile=$1
  sMyDb=$2

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

  echo

  echo import to "$sMyDb"...

  h2 "deleting database [$sMyDb] ..."
  color cmd
  su ${PGUSER} -c "dropdb ${sMyDb}"
  color reset

  h2 "ensure that database exists ..."
  color cmd
  su ${PGUSER} -c "psql ${BACKUP_PARAMS} -c \"CREATE DATABASE ${sMyDb};\""
  fetchrc
  color reset

  h2 "import ..."
  ls -l "${sMyfile}"
  echo "import to database [${sMyDb}]"
  color cmd
  zcat "${sMyfile}" | su ${PGUSER} -c "psql ${BACKUP_PARAMS} -d ${sMyDb}"
  fetchrc
  color reset

}


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

# ----- requirements

j_requireBinary "psql" 1

if [ $rc -ne 0 ]; then
  color.echo error "ERROR: Missing psql binary. Your Sqlite data cannot be dumped."
else

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

fi

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

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