#!/bin/sh
## WARNING TO MAINTAINERS - DO NOT EDIT this file in debian/.  It will be
## overwritten by debian/rules.  Edit the .in template instead.
set -e
#
# Script postgresql-dump
#
# This script is used to dump and destroy a database when PostgreSQL is updated
# to an incompatible database format.

syntax () {
	help
	exit 1
}

help () {
	echo Usage:
	echo "`basename $0` [options] -t target"
	echo "`basename $0` -h"
	echo "       where target is a file or tape device."
	echo "      -b blksz  Use dd with a blocksize of <blksz> to write the dump"
	echo "      -c        list the ASCII dump to screen for the user to check"
	echo "      -d        destroy the old database framework"
	echo "      -f        fix certain errors in older dumped data"
	echo "      -F        force a reload even if versions are up-to-date"
	echo "      -h        print this help message"
	echo "      -i        create a new database framework with initdb"
	echo "      -l        load the ASCII dump into the new database"
	echo "      -p dir    preserve the old database framework under directory <dir>"
	echo "      -t target create the ASCII dump on file or device <target>"
	echo "      -u        dump only the database structure; use pg_upgrade to fill this"
	echo "                from the existing database"
	echo "      -v        verbose output"
	echo "      -X        do not dump the old database; cancels -c"
	echo "      -x        do not dump the old database; forces -c"
}

add_option () {
	if [ -n "$OPTIONS" ]
	then
		OPTIONS=$OPTIONS\ $1
	else
		OPTIONS=$1
	fi
	shift

	while [ -n "$1" ]
	do
		OPTIONS=$OPTIONS\ $1
		shift
	done
}

checkpath () {
	dp=`dirname $1`
	dir=`(cd $dp; pwd)`   # cope with $PGDATA/../ and so on
	while [ -n "`echo $dir | awk -F/ '{print $2}'`" ]
	do
		if [ $dir = $PGDATA ]
		then
			echo $dp/$1 is in or under PGDATA \($PGDATA\). >&2
			echo It will be destroyed when PGDATA is cleared.  Specify a different path.>&2
			exit 11
		fi
		dir=`dirname $dir`
	done
}

SHELL=/bin/sh
current=6.5          # Current PostgreSQL database version
check=
destroy=
create=
load=
sed_command=cat
preserve=
upgrade=
verbose=
exclude_dump=
no_check=
force_reload=
blksz=

dir=`pwd`
while [ -n "`echo $dir | awk -F/ '{print $2}'`" ]
do
	if [ $dir = $PGDATA ]
	then
		echo The current directory is in or under PGDATA \($PGDATA\). >&2
		echo Please move to a different directory.>&2
		exit 11
	fi
	dir=`dirname $dir`
done



# Read command line
while [ $OPTIND -le $# ]
do
	getopts b:cdFfilp:t:uvXx arg
	case "$arg"
		in
		b)
		   blksz=$OPTARG;;
		c)
			check=TRUE;;
		d)
			destroy=TRUE;;
		F)
			force_reload=TRUE;;
		f)
			sed_command="sed -e '/^CREATE RULE /s/current\./old./g' -e \"/create database /s/with encoding=''/with encoding='SQL_ASCII'/\"" ;;
		h)
			help
			exit 0;;
		i)
			create=TRUE;;
		l)
			load=TRUE;;
		p)
			preserve=$OPTARG;;
		t)
			target=$OPTARG;;
		u)
			upgrade=upgrade;;
		v)
			verbose=TRUE;;
		X)
			no_check=TRUE
			exclude_dump=TRUE;;
		x)
			exclude_dump=TRUE
			check=TRUE;;
		*)
			syntax;;
	esac
done

if [ -z "${exclude_dump}" ]
then
	check=TRUE
fi

if [ -n "${no_check}" ]
then
	check=
fi

if [ -z "${destroy}" ]
then
	preserve=
fi

if [ -z "${target}" ]
then
	echo No target file or device specified >&2
	syntax
else
	checkpath ${target}
fi

if [ -f /etc/postgresql/postmaster.init ]
then
	. /etc/postgresql/postmaster.init
fi

PGDATA=${POSTGRES_DATA:=/var/lib/postgres/data}
PGLIB=/usr/lib/postgresql
PATH=/usr/lib/postgresql/bin:/bin:/usr/bin
ENCODINGFILE=/usr/lib/postgresql/dumpall/default_encoding
export PGLIB PGDATA LANG PATH

# Since this script has to dump the entire database, it must be run by the
# PostgreSQL superuser
if [ `id | cut -d\( -f2 | cut -d\) -f1` != postgres ]
then
	echo $0: this script must be run by the user postgres. >&2
	exit 2
fi

# Make sure either the ${PGDATA} directory or the dump target exists
dumped=
if [ ! -d ${PGDATA:=/var/lib/postgres/data}/base ]
then
	if [ ! -f ${target} ]
	then
		echo The PostgreSQL data directory, ${PGDATA}, and the dump file are missing. >&2
		echo >&2
		echo Please check the settings in /etc/postgresql/postmaster.init >&2
		exit 3
	else
		dumped=TRUE
		destroy=""
		preserve=""
	fi
fi
if [ -n "${preserve}" ]
then
	checkpath ${preserve}
fi

if [ -n "$blksz" ]
then
	writeout="dd of=$target bs=$blksiz"
	readin="dd if=$target bs=$blksiz"
	header="$readin count=1 | head -1"
else
	writeout="cat >$target"
	readin="cat $target"
	header="head -1 $target"
fi

load_only=
if [ -z "${dumped}" ]
then
	# Find out what the current database format version is
	installed=`cat ${PGDATA}/PG_VERSION`
	must_dump=

	if [ -z "${installed}" ]
	then
		echo "There is no existing installation of Postgresql" >&2
		if [ -d ${PGDATA}/template1 ]
		then
			echo However, there is a template1 directory in ${PGDATA}, which >&2
			echo is unexpected. Please analyse and repair the directory by >&2
			echo hand. This script is unable to handle it in its present >&2
			echo state. >&2
			echo >&2
			exit 4
		fi
	else
		# check the current and installed version numbers
		majorc=`echo ${current} | cut -d. -f1`
		minorc=`echo ${current} | cut -d. -f2`
		majori=`echo ${installed} | cut -d. -f1`
		minori=`echo ${installed} | cut -d. -f2`
		ccomp=`expr 100 + $majorc``expr 100 + $minorc`
		icomp=`expr 100 + $majori``expr 100 + $minori`

		if [ ${ccomp} -eq ${icomp} -a -z "${force_reload}" ]
		then
			echo The PostgreSQL database format has not changed\; there is >&2
			echo no need to do a dump and restore with this script. >&2
			load_only=TRUE
		else
			must_dump=TRUE
			if [ ${ccomp} -ge 106105 -a ${icomp} -le 106104 -a -n "$upgrade" ]
			then
				echo "It is not possible to use the -u option to upgrade from 6.4 or lower"
				echo "to 6.5 or higher, because the internal structure of tables changed"
				echo "at 6.5."
				exit 1
			fi
			if [ $icomp -le 106104 ]
			then
				dump_options=-z
			fi
			if [ -n "$upgrade" ]
			then
				dump_options=$dump_options' -s'
			fi
		fi
	fi

	if [ -n "${must_dump}" -a -z "${exclude_dump}" ]
	then
		# Look for existing postgresql binaries, for the previous release
		export BINDIR=/usr/lib/postgresql/dumpall/${installed}

		if [ ! -d ${BINDIR} ]
		then
			echo There are no binaries for dumping database format ${installed} >&2
			exit 5
		fi

		if [ -n "${verbose}" ]
		then
			echo "Stopping and restarting the postmaster" >&2
		fi
		set +e
		pm_pid=`ps axc | grep -s ' postmaster *$' | awk '{print $1}'`
		set -e
		if [ -n "$pm_pid" ]
		then
			kill $pm_pid
		fi

		OPTIONS=
		
		export LANG PGDATESTYLE

		# Check for old-style postmaster.init
		if [ "${PGDATESTYLE}" = American -o "${PGDATESTYLE}" = European ]
		then
			if [ "${PGDATESTYLE}" = American ]
			then
				PGDATESTYLE=US
			else
				PGDATESTYLE=EURO
			fi
		elif [ -z "${PGDATESTYLE}" ]
		then
			PGDATESTYLE=EURO
		fi

		POSTMASTER=${BINDIR}/postmaster
		POSTGRES=${BINDIR}/postgres
		PORT="-p 5431"    # change the port to stop normal users connecting

		${POSTMASTER} -S -D ${PGDATA} ${AUTH} ${PORT} ${OPTIONS}
		sleep 4

		set +e
		new_pm_pid=`ps axc | grep -s ' postmaster *$' | awk '{print $1}'`
		set -e
		if [ A${new_pm_pid} = A ]
		then
			echo "Failed to start the postmaster" 2>&1
			exit 7
		fi
		if [ A${new_pm_pid} = A${pm_pid} ]
		then
			echo "Failed to stop the running postmaster" 2>&1
			exit 6
		fi

		if [ -n "${verbose}" ]
		then
			if [ -n "$upgrade" ]
			then
				echo "Dumping the database structure to ${target}" >&2
			else
				echo "Dumping the database to ${target}" >&2
			fi
		fi
		(
			echo "-- postgresql-dump ${upgrade} on `date` from version ${installed}"
			/usr/lib/postgresql/dumpall/pg_dumpall $dump_options
			echo "-- postgresql-dump ${upgrade} completed on `date`"
		) | eval $writeout

		if [ -n "${verbose}" ]
		then
			echo "Finding the default encoding" >&2
		fi
		if ${BINDIR}/psql -q $PORT -d template1 -c "select * from pg_database where datname = 'template1'" | head -1 | grep -q encoding
		then
			/usr/lib/postgresql/bin/pg_encoding `${BINDIR}/psql $PORT -q -t -d template1 -c "select encoding from pg_database where datname = 'template1'"` > $ENCODINGFILE
		else
			echo SQL_ASCII > $ENCODINGFILE
		fi

		if [ -n "${verbose}" ]
		then
			echo "Killing the postmaster" >&2
		fi
		kill $new_pm_pid

	fi
fi

if [ -f "$target" ]
then
	dumptype=`eval $header | cut -f3 -d\ `
	if [ "$dumptype" = upgrade ]
	then
		unset destroy	# We must not destroy the database, because the data
				# is not in the dump
	else
		dumptype=full
	fi
fi

if [ -n "${check}" ]
then
	if echo ${target} | grep -q '^/dev/'
	then
		mt -f ${target} rewind
	else
		if [ ! -f ${target} ]
		then
			echo ASCII dump file ${target} does not exist.
			exit 13
		fi
	fi
	(echo This is the ASCII output of the dump for you to check:
	 echo
	 eval ${readin} | eval ${sed_command}) | ${PAGER:-more}
	if [ -n "${destroy}" ]
	then
		echo -n On the basis of this dump, is it OK to delete the old database? [y/n]\ 
		read x
		case $x
			in
			y|Y|Yes|yes|YES)
				echo Destroying old database...
				;;
			*)
				exit 11;;
		esac
	else
		echo -n Is this dump suitable for loading into the new database? [y/n]\ 
		read x
		case $x
			in
			y|Y|Yes|yes|YES)
				;;
			*)
				exit 11;;
		esac
	fi
fi
if echo ${target} | grep -q '^/dev/'
then
	mt -f ${target} rewind
fi

if [ -n "${destroy}" -o -n "$upgrade" ]
then
	if [ -n "${preserve}" ]
	then
		# Copy the database tree to ${preserve} -- just in case...
		if [ -n "${verbose}" ]
		then
			echo "Copying ${PGDATA} to ${preserve}" >&2
		fi

		if [ ! -d ${preserve} ]
		then
			mkdir ${preserve} || exit 8
		fi

		# Use cp rather than mv in case we are being asked
		# to move between filesystems.
		cp -a $PGDATA/* ${preserve} ||
			{ echo Could not copy ${PGDATA}/\* to ${preserve} >&2; exit 9; }
	fi
	if [ -n "${verbose}" -a -n "${destroy}" ]
	then
		echo "Deleting ${PGDATA}" >&2
	fi

	if [ -n "$upgrade" ]
	then
		old_database=${PGDATA}.old
		i=0
		while [ -d $old_database ]
		do
			i=`expr $i + 1`
			old_database=${PGDATA}.old.$i
		done
		
		if [ -n "${verbose}" ]
		then
			echo "Moving ${PGDATA} to $old_database" >&2
		fi
		mv ${PGDATA} $old_database
		mkdir $PGDATA
		chmod 700 $PGDATA
	else
		rm -rf ${PGDATA}/* ||
			{ echo Could not delete ${PGDATA}/\* >&2
			exit 10; }
	fi
fi

set +e
unset POSTGRES
unset POSTMASTER
unset PGLIB
unset PGDATA
unset PORT
unset BINDIR
. /etc/postgresql/postgresql.env
set -e

if [ -n "${create}" ]
then
	if [ -n "${verbose}" ]
	then
		echo "Creating the new database structure" >&2
	fi

	failed=
	ENCODING=`cat $ENCODINGFILE 2>/dev/null`
	if [ -n "$ENCODING" ]
	then
		INITOPTS=" --pgencoding=$ENCODING"
	fi
	USER=postgres initdb $INITOPTS || failed=TRUE
	if [ -n "$failed" ]
	then
		echo initdb failed
		exit 2
	fi
fi

if [ -n "${load}" ]
then
	if [ -n "${verbose}" ]
	then
		echo "Checking that the postmaster is running" >&2
	fi
	set +e
	x=`ps axc | grep -s ' postmaster *$'`
	set -e
	if [ -z "$x" ]
	then
		# start up the postmaster
		. /etc/postgresql/postmaster.init
		PGDATA=${POSTGRES_DATA:=/var/lib/postgres/data}
		PGLIB=/usr/lib/postgresql
		export PGLIB PGDATA PGPORT LANG
		POSTMASTER=${PGLIB}/bin/postmaster
		POSTGRES=${PGLIB}/bin/postgres
		OPTIONS=

		if [ -n "${verbose}" ]
		then
			echo "Starting the postmaster" >&2
		fi

		# Shared-memory buffers
		if [ ! -z "${PGBUFFERS}" ]
		then
		    BUFFERS="-B ${PGBUFFERS}"
		fi

		# Debugging level
		if [ ! -z "${PGDEBUG}" ]
		then
		    DEBUGLEVEL="-d ${PGDEBUG}"
		fi

		# TCP port
		PORT="-p ${PGPORT:=5432}"
		
		# Backend options
		# Query echoing
		if [ "${PGECHO}" = yes ]
		then
		    add_option -E
		fi

		# Timing stats
		if [ "${PGSTATS}" = yes ]
		then
		    add_option -s
		fi

		# Fsync()
		if [ "${PGFSYNC}" = no ]
		then
		    add_option -F
		fi

		# Sort memory
		if [ ! -z "${PGSORTMEM}" ]
		then
		    add_option -S ${PGSORTMEM}
		fi

		# American or European date format
		if [ "${PGDATESTYLE}" != American ]
		then
		    add_option -e
		fi

		# Make sure that we don't get started if there is no options file.
		# We certainly don't want to get started if the executable is missing.
		if [ ! -x ${POSTMASTER} ]
		then
		    echo No postmaster executable for postgresql
		    exit 3
		fi

		LOGFILE=${POSTGRES_LOG:-/var/log/postgres.log}
		# Ready to go: stand clear...
		echo Starting PostgreSQL postmaster
        	echo ${POSTMASTER} -i -b ${POSTGRES} ${BUFFERS} \
		    -D ${PGDATA} ${DEBUGLEVEL} ${PORT} ${OPTIONS:+-o \'${OPTIONS}\'} \>$LOGFILE 2\>\&1 \&
        	nohup ${POSTMASTER} -i -b ${POSTGRES} ${BUFFERS} \
		    -D ${PGDATA} ${DEBUGLEVEL} ${PORT} ${OPTIONS:+-o "${OPTIONS}"} >$LOGFILE 2>&1 &
	fi

	if [ -n "${verbose}" ]
	then
		if [ -n "$upgrade" ]
		then
			echo "Loading the database structure from ${target}" >&2
		else
			echo "Loading the database from ${target}" >&2
		fi
	fi

	# Give the postmaster a chance to start up
	sleep 5
	echo Reloading databases...
	if [ -n "$upgrade" ]
	then
		cd ${PGDATA}/..
		echo Current directory now `pwd`
		rm -f ${target}.fixed
		eval $readin | eval ${sed_command} > ${target}.fixed
		echo Running pg_upgrade
		pg_upgrade -f $target.fixed `basename $old_database`
		rm ${target}.fixed
	else
		eval $readin | eval ${sed_command} |
			psql -e template1 &&
			echo Reload succeeded ||
			echo Reload failed
	fi
fi

exit 0
