#!/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" # --------------------------------------------------------------------------------