1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
|
#!/usr/bin/env bash
# Test performing a Major Version Upgrade via pg_upgrade.
#
# MVU can be problematic due to catalog changes. For example, if the extension
# contains a view that references a catalog column that no longer exists,
# pg_upgrade itself will break.
set -E -e -u -o pipefail
BASEDIR=`dirname $0`
if ! . $BASEDIR/../tools/util.sh; then
echo "FATAL: error sourcing $BASEDIR/../tools/util.sh" 1>&2
exit 99
fi
trap err_report ERR
debug 19 "Arguments: $@"
rc=0
byte_len() (
[ $# -eq 1 ] || die 99 "Expected 1 argument, not $# ($@)"
LANG=C LC_ALL=C
debug 99 "byte_len($@) = ${#1}"
echo ${#1}
)
check_bin() {
for f in pg_ctl psql initdb; do
[ -x "$1/$f" ] || die 1 "$1/$f does not exist or is not executable"
done
}
# mktemp on OS X results is a super-long path name that can cause problems, ie:
# connection to database failed: Unix-domain socket path "/private/var/folders/rp/mv0457r17cg0xqyw5j7701892tlc0h/T/test_pgtap_upgrade.upgrade.7W4BLF/.s.PGSQL.50432" is too long (maximum 103 bytes)
#
# This function looks for that condition and replaces the output with something more legible
short_tmpdir() (
[ $# -eq 1 ] || die 99 "Expected 1 argument, not $# ($@)"
[ "$TMPDIR" != "" ] || die 99 '$TMPDIR not set'
out=$(mktemp -p '' -d $1.XXXXXX)
if echo "$out" | egrep -q '^(/private)?/var/folders'; then
newout=$(echo "$out" | sed -e "s#.*/$TMPDIR#$TMPDIR#")
debug 19 "replacing '$out' with '$newout'"
fi
debug 9 "$0($@) = $out"
# Postgres blows up if this is too long. Technically the limit is 103 bytes,
# but need to account for the socket name, plus the fact that OS X might
# prepend '/private' to what we return. :(
[ $(byte_len "$out") -lt 75 ] || die 9 "short_tmpdir($@) returning a value >= 75 bytes ('$out')"
echo "$out"
)
banner() {
echo
echo '###################################'
echo "$@"
echo '###################################'
echo
}
modify_config() (
# See below for definition of ctl_separator
if [ -z "$ctl_separator" ]; then
confDir=$PGDATA
conf=$confDir/postgresql.conf
debug 6 "$0: conf = $conf"
debug 0 "Modifying NATIVE $conf"
echo "port = $PGPORT" >> $conf
else
confDir="/etc/postgresql/$1/$cluster_name"
conf="$confDir/postgresql.conf"
debug 6 "$0: confDir = $confDir conf=$conf"
debug_ls 9 -la $confDir
debug 0 "Modifying DEBIAN $confDir and $PGDATA"
debug 2 ln -s $conf $PGDATA/
ln -s $conf $PGDATA/
# Some versions also have a conf.d ...
if [ -e "$confDir/conf.d" ]; then
debug 2 ln -s $confDir/conf.d $PGDATA/
ln -s $confDir/conf.d $PGDATA/
fi
debug_ls 8 -la $PGDATA
# Shouldn't need to muck with PGPORT...
# GUC changed somewhere between 9.1 and 9.5, so read config to figure out correct value
guc=$(grep unix_socket_director $conf | sed -e 's/^# *//' | cut -d ' ' -f 1)
debug 4 "$0: guc = $guc"
echo "$guc = '/tmp'" >> $conf
fi
echo "synchronous_commit = off" >> $conf
)
#############################
# Argument processing
keep=''
if [ "$1" == "-k" ]; then
debug 1 keeping results after exit
keep=1
shift
fi
sudo=''
if [ "$1" == '-s' ]; then
# Useful error if we can't find sudo
command -v sudo > /dev/null || echo "sudo not found"
sudo=$(command -v sudo)
debug 2 "sudo located at $sudo"
shift
fi
OLD_PORT=$1
NEW_PORT=$2
OLD_VERSION=$3
NEW_VERSION=$4
OLD_PATH="${5:-/usr/lib/postgresql/$OLD_VERSION/bin}"
NEW_PATH="${5:-/usr/lib/postgresql/$NEW_VERSION/bin}"
export PGDATABASE=test_pgtap_upgrade
check_bin "$OLD_PATH"
check_bin "$NEW_PATH"
export TMPDIR=${TMPDIR:-${TEMP:-${TMP:-/tmp}}}
debug 9 "\$TMPDIR=$TMPDIR"
[ $(byte_len "$TMPDIR") -lt 50 ] || die 9 "\$TMPDIR ('$TMPDIR') is too long; please set it" '(or $TEMP, or $TMP) to a value less than 50 bytes'
upgrade_dir=$(short_tmpdir test_pgtap_upgrade.upgrade)
old_dir=$(short_tmpdir test_pgtap_upgrade.old)
new_dir=$(short_tmpdir test_pgtap_upgrade.new)
# Note: if this trap fires and removes the old directories with databases still
# running we'll get a bunch of spew on STDERR. It'd be nice to have a trap that
# knew what databases might actually be running.
exit_trap() {
# No point in stopping on error in here...
set +e
# Force sudo on a debian system (see below)
[ -z "$ctl_separator" ] || sudo=$(command -v sudo)
# Attempt to shut down any running clusters, otherwise we'll get log spew
# when the temporary directories vanish.
$old_pg_ctl stop > /dev/null 2>&1
$new_pg_ctl stop > /dev/null 2>&1
# Do not simply stick this command in the trap command; the quoting gets
# tricky, but the quoting is also damn critical to make sure rm -rf doesn't
# hose you if the temporary directory names have spaces in them!
$sudo rm -rf "$upgrade_dir" "$old_dir" "$new_dir"
}
[ -n "$keep" ] || trap exit_trap EXIT
debug 5 "traps: $(trap -p)"
cluster_name=test_pg_upgrade
if command -v pg_ctlcluster > /dev/null; then
# Looks like we're running in a apt / Debian / Ubuntu environment, so use their tooling
ctl_separator='--'
# Force socket path to normal for pg_upgrade
export PGHOST=/tmp
# And force current user
export PGUSER=${USER:-$(whoami)}
old_initdb="$sudo pg_createcluster $OLD_VERSION $cluster_name -u $PGUSER -p $OLD_PORT -d $old_dir -- -A trust"
old_pg_ctl="$sudo pg_ctlcluster $OLD_VERSION test_pg_upgrade"
new_initdb="$sudo pg_createcluster $NEW_VERSION $cluster_name -u $PGUSER -p $NEW_PORT -d $new_dir -- -A trust"
new_pg_ctl="$sudo pg_ctlcluster $NEW_VERSION test_pg_upgrade"
# See also ../.github/workflows/test.yml
new_pg_upgrade="/usr/lib/postgresql/$NEW_VERSION/bin/pg_upgrade"
else
ctl_separator=''
old_initdb="$(find_at_path "$OLD_PATH" initdb) -D $old_dir -N"
old_pg_ctl=$(find_at_path "$OLD_PATH" pg_ctl)
new_initdb="$(find_at_path "$NEW_PATH" initdb) -D $new_dir -N"
new_pg_ctl=$(find_at_path "$NEW_PATH" pg_ctl)
new_pg_upgrade=$(find_at_path "$NEW_PATH" pg_upgrade)
fi
# Postgres 18 enables checksums by default, so turn them off to be compatible
# with earlier versions, where they're off by default.
if [ "$OLD_VERSION" -ge 18 ]; then
old_initdb+=" --no-data-checksums"
fi
if [ "$NEW_VERSION" -ge 18 ]; then
new_initdb+=" --no-data-checksums"
fi
##################################################################################################
banner "Creating old version temporary installation at $old_dir on port $OLD_PORT (in the background)"
echo "Creating new version temporary installation at $new_dir on port $NEW_PORT (in the background)"
$old_initdb &
$new_initdb &
echo Waiting...
wait
##################################################################################################
banner "Starting OLD $OLD_VERSION postgres via $old_pg_ctl"
export PGDATA=$old_dir
export PGPORT=$OLD_PORT
modify_config $OLD_VERSION
$old_pg_ctl start $ctl_separator -w # older versions don't support --wait
echo "Creating database"
createdb # Note this uses PGPORT, so no need to wrap.
echo "Installing pgtap"
# If user requested sudo then we need to use it for the install step. TODO:
# it'd be nice to move this into the Makefile, if the PGXS make stuff allows
# it...
$sudo make clean install
banner "Loading extension"
psql -c 'CREATE EXTENSION pgtap' # Also uses PGPORT
echo "Stopping OLD postgres via $old_pg_ctl"
$old_pg_ctl stop $ctl_separator -w # older versions don't support --wait
##################################################################################################
banner "Running pg_upgrade"
export PGDATA=$new_dir
export PGPORT=$NEW_PORT
modify_config $NEW_VERSION
(
cd $upgrade_dir
if [ $DEBUG -ge 9 ]; then
echo $old_dir; ls -la $old_dir; egrep 'director|unix|conf' $old_dir/postgresql.conf
echo $new_dir; ls -la $new_dir; egrep 'director|unix|conf' $new_dir/postgresql.conf
fi
echo $new_pg_upgrade -d "$old_dir" -D "$new_dir" -b "$OLD_PATH" -B "$NEW_PATH"
$new_pg_upgrade -d "$old_dir" -D "$new_dir" -b "$OLD_PATH" -B "$NEW_PATH" || rc=$?
if [ $rc -ne 0 ]; then
# Dump log, but only if we're not keeping the directory
if [ -z "$keep" ]; then
for f in `ls *.log`; do
echo; echo; echo; echo; echo; echo
echo "`pwd`/$f:"
cat "$f"
done
ls -la
else
error "pg_upgrade logs are at $upgrade_dir"
fi
die $rc "pg_upgrade returned $rc"
fi
)
##################################################################################################
banner "Testing UPGRADED cluster"
# Run our tests against the upgraded cluster, but first make sure the old
# cluster is still down, to ensure there's no chance of testing it instead.
# Note that some versions of pg_ctl return different exit codes when the server
# isn't running.
echo ensuring OLD cluster is stopped
rc=0
status=$($old_pg_ctl status) || rc=$?
[ "$status" == 'pg_ctl: no server running' ] || die 3 "$old_pg_ctl status returned '$status' and exited with $?"
debug 4 "$old_pg_ctl status exited with $rc"
# TODO: send log output to a file so it doesn't mix in with STDOUT
echo starting NEW cluster
$new_pg_ctl start $ctl_separator -w || die $? "$new_pg_ctl start $ctl_separator -w returned $?"
$new_pg_ctl status # Should error if not running on most versions
psql -E -c '\dx'
psql -E -c 'SELECT pgtap_version(), pg_version_num(), version();'
# We want to make sure to use the NEW pg_config
export PG_CONFIG=$(find_at_path "$NEW_PATH" pg_config)
[ -x "$PG_CONFIG" ] || ( debug_ls 1 "$NEW_PATH"; die 4 "unable to find executable pg_config at $NEW_PATH" )
# When crossing certain upgrade boundaries we need to exclude some tests
# because the test functions are not available in the previous version.
int_ver() {
local ver
ver=$(echo $1 | tr -d .)
# "multiply" versions less than 7.0 by 10 so that version 10.x becomes 100,
# 11 becomes 110, etc.
[ $ver -ge 70 ] || ver="${ver}0"
echo $ver
}
EXCLUDE_TEST_FILES=''
add_exclude() {
local old new
old=$(int_ver $1)
new=$(int_ver $2)
shift 2
if [ $(int_ver $OLD_VERSION) -le $old -a $(int_ver $NEW_VERSION) -ge $new ]; then
EXCLUDE_TEST_FILES="$EXCLUDE_TEST_FILES $@"
fi
}
add_exclude 9.1 9.2 test/sql/resultset.sql
add_exclude 9.1 9.2 test/sql/valueset.sql
add_exclude 9.1 9.2 test/sql/throwtap.sql
add_exclude 9.4 9.5 test/sql/policy.sql test/sql/throwtap.sql
add_exclude 9.6 10 test/sql/partitions.sql
# Use this if there's a single test failing .github/workflows/test.yml that you can't figure out...
#(cd $(dirname $0)/..; pg_prove -v --pset tuples_only=1 test/sql/throwtap.sql)
export EXCLUDE_TEST_FILES
$sudo make clean
make test
if [ -n "$EXCLUDE_TEST_FILES" ]; then
banner "Rerunning test after a reinstall due to version differences"
echo "Excluded tests: $EXCLUDE_TEST_FILES"
export EXCLUDED_TEST_FILES=''
# Need to build with the new version, then install
$sudo make install
psql -E -c 'DROP EXTENSION pgtap; CREATE EXTENSION pgtap;'
make test
fi
|