#!/bin/bash # Author: Clemens Schwaighofer # Description: # Drop and restore one database from a dump created by created by pg_db_dump_file.sh function usage () { cat <<- EOT Restores a single database dump to a database Usage: ${0##/*/} -o -d -f [-h ] [-p ] [-e ] [-i ] [-j ] [-s] [-r] [-n] -o : The user who will be owner of the database to be restored -d : The database to restore the file to -f : the data that should be loaded -h : optional hostname, if not given 'localhost' is used. Use 'local' to use unix socket -p : optional port number, if not given '5432' is used -e : optional encoding name, if not given 'UTF8' is used -i : optional postgresql version in the format X.Y, if not given the default is used (current active) -j : Run how many jobs Parallel. If not set, 2 jobs are run parallel -s: Restore only schema, no data -r: use redhat base paths instead of debian -n: dry run, do not do anything, just test flow EOT } _port=5432 _host='local'; _encoding='UTF8'; # role=''; schema=''; NO_ASK=0; TEMPLATEDB='template0'; SCHEMA_ONLY=0; ERROR=0; REDHAT=0; DRY_RUN=0; BC='/usr/bin/bc'; PORT_REGEX="^[0-9]{4,5}$"; OPTARG_REGEX="^-"; # log path LOG_PATH=''; MAX_JOBS=''; PG_PARAMS=(); PG_PARAM_ROLE=(); # if we have options, set them and then ignore anything below while getopts ":o:d:h:f:p:e:i:j:rqnms" opt; do # pre test for unfilled if [ "${opt}" = ":" ] || [[ "${OPTARG-}" =~ ${OPTARG_REGEX} ]]; then if [ "${opt}" = ":" ]; then CHECK_OPT=${OPTARG}; else CHECK_OPT=${opt}; fi; case ${CHECK_OPT} in o) echo "-o needs an owner name"; ERROR=1; ;; d) echo "-d needs a database name"; ERROR=1; ;; h) echo "-h needs a host name"; ERROR=1; ;; f) echo "-f needs a file name"; ERROR=1; ;; p) echo "-h needs a port number"; ERROR=1; ;; e) echo "-e needs an encoding"; ERROR=1; ;; i) echo "-i needs a postgresql version"; ERROR=1; ;; j) echo "-j needs a numeric value for parallel jobs"; ERROR=1; ;; esac fi; case $opt in # o|owner) o) if [ -z "$owner" ]; then owner=$OPTARG; # if not standard user we need to set restore role # so tables/etc get set to new user # role="--no-owner --role $owner"; PG_PARAM_ROLE=("--no-owner" "--role" "$owner"); fi; ;; # d|database) d) if [ -z "$database" ]; then database=$OPTARG; fi; ;; # e|encoding) e) if [ -z "$encoding" ]; then encoding=$OPTARG; fi; ;; # f|file) f) if [ -z "$file" ]; then file=$OPTARG; fi; ;; # h|hostname) h) if [ -z "$_host" ]; then # if local it is socket if [ "$OPTARG" != "local" ]; then PG_PARAMS+=("-h" "${OPTARG}"); fi; _host=$OPTARG; fi; ;; # p|port) p) if [ -z "$port" ]; then PG_PARAMS+=("-p" "${OPTARG}"); _port=$OPTARG; fi; ;; # i|ident) i) if [ -z "$ident" ]; then ident=$OPTARG; fi; ;; # j|jobs) j) MAX_JOBS=${OPTARG}; ;; # q|quiet) q) NO_ASK=1; ;; # r|redhat) r) REDHAT=1; ;; # n|dry-run) n) DRY_RUN=1; ;; # s|schema-only) s) SCHEMA_ONLY=1 schema='-s'; ;; # m|help) m) usage; exit 0; ;; \?) echo -e "\n Option does not exist: $OPTARG\n"; usage; exit 1; ;; esac; done; if [ "${ERROR}" -eq 1 ]; then exit 0; fi; # check that the port is a valid number if ! [[ "$_port" =~ $PORT_REGEX ]]; then echo "The port needs to be a valid number: $_port"; exit 1; fi; NUMBER_REGEX="^[0-9]{1,}$"; # find the max allowed jobs based on the cpu count # because setting more than this is not recommended _max_jobs=$(nproc --all); # if the MAX_JOBS is not number or smaller 1 or greate _max_jobs if [ -n "${MAX_JOBS}" ]; then # check that it is a valid number if ! [[ "$MAX_JOBS" =~ $NUMBER_REGEX ]]; then echo "Please enter a number for the -j option"; exit 1; fi; if [ "${MAX_JOBS}" -lt 1 ] || [ "${MAX_JOBS}" -gt "${_max_jobs}" ]; then echo "The value for the jobs option -j cannot be smaller than 1 or bigger than ${_max_jobs}"; exit 1; fi; else # auto set the MAX_JOBS based on the cpu count MAX_JOBS=${_max_jobs}; fi; # check if we have the 'bc' command available or not if [ -f "${BC}" ]; then BC_OK=1; else BC_OK=0; fi; if [ ! -f "${file}" ]; then echo "File name needs to be provided or file could not be found"; exit 1; fi; # METHOD: convert_time # PARAMS: timestamp in seconds or with milliseconds (nnnn.nnnn) # RETURN: formated string with human readable time (d/h/m/s) # CALL : var=$(convert_time $timestamp); # DESC : converts a timestamp or a timestamp with float milliseconds to a human readable format # output is in days/hours/minutes/seconds function convert_time { timestamp=${1}; # round to four digits for ms timestamp=$(printf "%1.4f" "$timestamp"); # get the ms part and remove any leading 0 ms=$(echo "${timestamp}" | cut -d "." -f 2 | sed -e 's/^0*//'); timestamp=$(echo "${timestamp}" | cut -d "." -f 1); timegroups=(86400 3600 60 1); # day, hour, min, sec timenames=("d" "h" "m" "s"); # day, hour, min, sec output=( ); time_string=''; for timeslice in "${timegroups[@]}"; do # floor for the division, push to output if [ ${BC_OK} -eq 1 ]; then output[${#output[*]}]=$(echo "${timestamp}/${timeslice}" | bc); timestamp=$(echo "${timestamp}%${timeslice}" | bc); else output[${#output[*]}]=$(awk "BEGIN {printf \"%d\", ${timestamp}/${timeslice}}"); timestamp=$(awk "BEGIN {printf \"%d\", ${timestamp}%${timeslice}}"); fi; done; for ((i=0; i<${#output[@]}; i++)); do if [ "${output[$i]}" -gt 0 ] || [ -n "$time_string" ]; then if [ -n "${time_string}" ]; then time_string=${time_string}" "; fi; time_string=${time_string}${output[$i]}${timenames[$i]}; fi; done; # milliseconds must be filled, but we also check that they are non "nan" string # that can appear in the original value if [ -n "${ms}" ] && [ "${ms}" != "nan" ]; then if [ "${ms}" -gt 0 ]; then time_string="${time_string} ${ms}ms"; fi; fi; # just in case the time is 0 if [ -z "${time_string}" ]; then time_string="0s"; fi; echo -n "${time_string}"; } # for the auto find, we need to get only the filename, and therefore remove all path info db_file=$(basename "$file"); # if file is set and exist, but no owner or database are given, use the file name data to get user & database if [ -r "$file" ] && { [ ! "$owner" ] || [ ! "$database" ] || [ ! "$encoding" ]; }; then # file name format is # ...-____