#!/bin/bash
set -e

# Build examples
# rm -rf /tmp/pg; ./configure --prefix=/tmp/pg --enable-debug --enable-cassert >/dev/null && make -j8 >/dev/null && make -j8 -C contrib >/dev/null && make check-world && make install && make install -C contrib

# CPPFLAGS="-O0 -fsanitize=address -fsanitize=undefined -fstack-protector -fno-sanitize=nonnull-attribute" LDFLAGS='-fsanitize=address -fsanitize=undefined -static-libasan' ./configure --prefix=/tmp/pg --enable-debug --enable-cassert --with-libxml --with-icu --with-lz4 >/dev/null && make -j8 >/dev/null && make -j8 -C contrib >/dev/null && ASAN_OPTIONS=detect_leaks=0 make check && make install && make install -C contrib

# CPPFLAGS="-DUSE_VALGRIND -Og" ./configure --prefix=/tmp/pg --enable-debug --enable-cassert >/dev/null && make -j8 >/dev/null && make -j8 -C contrib >/dev/null && make check && make install && make install -C contrib && /pgpro/+/install-vrunner.sh /tmp/pg

# Choose server id
[ ! -z $1 ] && SID=$1 || SID=01

# Set global variables
[ ! -z $ICX_NUMBER ] && NUM_INSTALLCHECKS=$ICX_NUMBER || NUM_INSTALLCHECKS=10
[ ! -z $ICX_ITERATIONS ] && NUM_ITERATIONS=$ICX_ITERATIONS || NUM_ITERATIONS=10
[ ! -z $ICX_PREFIX ] && PGPREFIX=$ICX_PREFIX || PGPREFIX=/tmp/pg/
[ ! -z $ICX_SRCCOPYDIR ] && SRCCOPYDIR=$ICX_SRCCOPYDIR || SRCCOPYDIR=/tmp/icx/
[ ! -z $ICX_PORT ] && export PGPORT=$ICX_PORT || export PGPORT=5432

SD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

PGDB=`pwd`/tmpdb
PGBIN=$PGPREFIX/bin
export PATH="$PGBIN:$PATH"
export LD_LIBRARY_PATH="$PGPREFIX/lib"

export PGHOST=127.0.0.1,127.0.0.1,127.0.0.1
export PGUSER=postgres
export PGDATABASE=test
export PGCONNECT_TIMEOUT=120

# Stop previous instance in case it is left from a previous run
$PGBIN/pg_ctl -w -t 300 -D "$PGDB" stop || true

# And clear data and logs
rm -rf "$PGDB" server*.log rm -rf installcheck$SID-*.log || true

# delcoredumps || true

export ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:disable_coredump=0:strict_string_checks=1:check_initialization_order=1:strict_init_order=1:detect_odr_violation=0

# Prepare master node
$PGBIN/initdb -k -D "$PGDB" --username=postgres
echo "
port = $PGPORT
shared_buffers = 1024MB
# wal_log_hints = on
max_connections = 1000
max_parallel_workers = 1000
# max_locks_per_transaction = 500

log_min_messages = INFO
log_min_error_statement = log

log_connections = on
log_disconnections = on
log_statement = 'all'
log_line_prefix = '%m|%u|%d|%c|'

autovacuum = on
log_autovacuum_min_duration = 0
#autovacuum_naptime = 1
#autovacuum_vacuum_threshold = 1
#autovacuum_analyze_threshold = 1

fsync = off

# Postgres Pro specifics
in_memory.shared_pool_size = 10240
in_memory.undo_size = 1024
" >> "$PGDB/postgresql.auto.conf"

# Prepare template directory
[ -d $SRCCOPYDIR ] || mkdir $SRCCOPYDIR
rm -rf $SRCCOPYDIR/template || true
mkdir $SRCCOPYDIR/template
cp * $SRCCOPYDIR/template >/dev/null 2>&1 || true
echo "exit 0" > $SRCCOPYDIR/template/config.status
mkdir $SRCCOPYDIR/template/src
cp src/* $SRCCOPYDIR/template/src/ >/dev/null 2>&1 || true
cp -r src/test $SRCCOPYDIR/template/src/
cp -r src/makefiles $SRCCOPYDIR/template/src
sed -e 's/^all:/#all:/' -i $SRCCOPYDIR/template/src/test/regress/GNUmakefile

for m in `find . -name Makefile`; do
  mkdir -p $SRCCOPYDIR/template/`dirname "$m"`
  cp $m $SRCCOPYDIR/template/$m
done

for c in `find contrib -name '*.conf'`; do
    [ "$c" == "contrib/pgpro_scheduler/web/nginx-conf/nginx.conf" ] && continue
    cp $c $SRCCOPYDIR/template/$c
done

cp contrib/contrib-global.mk $SRCCOPYDIR/template/contrib/

# Avoid rebuilding
sed -e "s/all: all-lib/all:/" -i $SRCCOPYDIR/template/src/interfaces/libpq/Makefile
sed -e "s/all:.*\$/all:/" -i $SRCCOPYDIR/template/src/port/Makefile
sed -e "s/all:.*\$/all:/" -i $SRCCOPYDIR/template/src/common/Makefile
sed -e "s/all:.*\$/all:/" -i $SRCCOPYDIR/template/src/test/isolation/Makefile

# Copy whole ecpg test if after check-world (ecpg skipped in case of `make check`)
if [ -f src/interfaces/ecpg/test/pg_regress ]; then
  cp -r src/interfaces/ecpg/* $SRCCOPYDIR/template/src/interfaces/ecpg/
  sed -e "s/\$(EXTRA_REGRESS_OPTS)//" -i $SRCCOPYDIR/template/src/interfaces/ecpg/test/Makefile
else
  sed -e "s/^installcheck:/noinstallcheck:/" -i $SRCCOPYDIR/template/src/interfaces/ecpg/test/Makefile
fi

for d in `find . -type d -name sql -o -name input -o -name specs`; do
  mkdir -p $SRCCOPYDIR/template/`dirname "$d"`
  cp -r $d $SRCCOPYDIR/template/$d
  [ -d $d/../data ] && cp -r $d/../data $SRCCOPYDIR/template/$d/../data
  [ -d $d/../expected ] && cp -r $d/../expected $SRCCOPYDIR/template/$d/../expected
  [ -d $d/../output ] && cp -r $d/../output $SRCCOPYDIR/template/$d/../output
  [ -d $d/../isolation_output ] && cp -r $d/../isolation_output $SRCCOPYDIR/template/$d/../isolation_output # for pg_query_state
done

# Fix tests

sed -e "s/SELECT obj_description(s.oid, 'pg_subscription') FROM pg_subscription s;/SELECT obj_description FROM (SELECT obj_description(s.oid, 'pg_subscription') FROM pg_subscription s) d WHERE obj_description IS NOT NULL LIMIT 1;/" -i \
  $SRCCOPYDIR/template/src/test/regress/sql/subscription.sql \
  $SRCCOPYDIR/template/src/test/regress/expected/subscription.out

sed -e "s/SELECT pg_notification_queue_usage();/SELECT round(pg_notification_queue_usage() * 1000) \\/ 1000 AS pg_notification_queue_usage/" -i \
  $SRCCOPYDIR/template/src/test/regress/sql/async.sql \
  $SRCCOPYDIR/template/src/test/regress/expected/async.out

sed -e "s/deptype NOT IN ('a', 'o', 'p', 'r')/deptype NOT IN ('a', 'o', 'p', 'r', 't')/" -i \
  $SRCCOPYDIR/template/src/test/regress/sql/misc_sanity.sql \
  $SRCCOPYDIR/template/src/test/regress/expected/misc_sanity.out

sed -e "s/WHERE application_name = 'fdw_retry_check';/WHERE application_name = 'fdw_retry_check' AND datname = current_database();/g" -i \
  $SRCCOPYDIR/template/contrib/postgres_fdw/sql/postgres_fdw.sql \
  $SRCCOPYDIR/template/contrib/postgres_fdw/expected/postgres_fdw.out

# Disable test foreign_data as not fixable by a simple sed
sed -e "s/foreign_data //" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/test: foreign_data//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-

# Disable tests select_parallel, stats and advisory_locks as unstable in parallel runs
sed -e "s/test: select_parallel//" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/test: select_parallel//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-
sed -e "s/advisory_lock//" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/test: advisory_lock//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-
sed -e "s/test: stats//" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/^test: stats\$//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-

# Fix test contrib/passwordcheck (remove md5 password that contains the username (regress_user1))
sed -e "/PASSWORD 'md5/d" -e "/ERROR:  password must not equal user name/d" -i \
  $SRCCOPYDIR/template/contrib/passwordcheck/sql/passwordcheck.sql \
  $SRCCOPYDIR/template/contrib/passwordcheck/expected/passwordcheck.out

# Disable test password as not fixable by a simple sed (md5 password contains the username (regress_passwd))
sed -e "s/password //" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/test: password//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-


## Disable some Postgres Pro tests: pg_pathman, pgpro_audit, shared_ispell, pgpro_scheduler
# pg_pathman uses shared bgworker
[ -f $SRCCOPYDIR/template/contrib/pg_pathman/Makefile ] && sed -e 's/^REGRESS\s*= /NOREGRESS = /' -i $SRCCOPYDIR/template/contrib/pg_pathman/Makefile
# contrib/test_partition requires pg_pathman
[ -f $SRCCOPYDIR/template/contrib/test_partition/Makefile ] && sed -e 's/^REGRESS\s*= /NOREGRESS = /' -i $SRCCOPYDIR/template/contrib/test_partition/Makefile
# pg_proaudit stores it's state in a shared memory segment
[ -f $SRCCOPYDIR/template/contrib/pg_proaudit/Makefile ] && sed -e 's/^REGRESS\s*= /NOREGRESS = /' -i $SRCCOPYDIR/template/contrib/pg_proaudit/Makefile
# Shared ispell dictionary stored in a shared segment
[ -f $SRCCOPYDIR/template/contrib/shared_ispell/Makefile ] && sed -e 's/^REGRESS\s*= /NOREGRESS = /' -i $SRCCOPYDIR/template/contrib/shared_ispell/Makefile

## Setup shared_preload_libraries for Postgres Pro tests:
PRELOAD_LIBRARIES=""
[ -d contrib/pg_query_state ] && PRELOAD_LIBRARIES="$PRELOAD_LIBRARIES,pg_query_state"
[ -d contrib/in_memory ] && PRELOAD_LIBRARIES="$PRELOAD_LIBRARIES,in_memory"
[ -d contrib/pg_wait_sampling ] && PRELOAD_LIBRARIES="$PRELOAD_LIBRARIES,pg_wait_sampling"
[ -f contrib/pgpro_scheduler/Makefile ] && PRELOAD_LIBRARIES="$PRELOAD_LIBRARIES,pgpro_scheduler"
PRELOAD_LIBRARIES="${PRELOAD_LIBRARIES#?}"

echo "shared_preload_libraries='$PRELOAD_LIBRARIES'" >> "$PGDB/postgresql.auto.conf"

## Postgres Pro specifics
[ -f $SRCCOPYDIR/template/contrib/pg_query_state/Makefile ] && sed -e 's/EXTRA_REGRESS_OPTS=/EXTRA_REGRESS_OPTS += /' -i $SRCCOPYDIR/template/contrib/pg_query_state/Makefile
[ -f $SRCCOPYDIR/template/contrib/in_memory/Makefile ] && sed -e 's/EXTRA_REGRESS_OPTS\s*=/EXTRA_REGRESS_OPTS += /' -i $SRCCOPYDIR/template/contrib/in_memory/Makefile
[ -f $SRCCOPYDIR/template/contrib/pg_wait_sampling/Makefile ] && sed -e 's/EXTRA_REGRESS_OPTS=/EXTRA_REGRESS_OPTS += /' -i $SRCCOPYDIR/template/contrib/pg_wait_sampling/Makefile
[ -f $SRCCOPYDIR/template/contrib/pg_pathman/Makefile ] && sed -e 's/EXTRA_REGRESS_OPTS=/EXTRA_REGRESS_OPTS += /' -i $SRCCOPYDIR/template/contrib/pg_pathman/Makefile

# Disable btree_traverse test as it depends on shared memory state (calls set_breakpoint())
[ -f $SRCCOPYDIR/template/contrib/in_memory/Makefile ] && sed -e 's/btree_traverse//' -i $SRCCOPYDIR/template/contrib/in_memory/Makefile

# Fix non-repeatable test
[ -d $SRCCOPYDIR/template/contrib/aqo ] && sed -e 's/RESET ROLE;/RESET ROLE; DROP ROLE noadmin;/' -i $SRCCOPYDIR/template/contrib/aqo/sql/gucs.sql \
  $SRCCOPYDIR/template/contrib/aqo/expected/gucs.out

# Disable test profiles (for now)
sed -e "s/profiles//" -i $SRCCOPYDIR/template/src/test/regress/parallel_schedule
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "s/test: profiles//" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for v13-

## Disable atx tests as unstable
# TODO: Enable these tests
[ -f $SRCCOPYDIR/template/src/test/regress/serial_schedule ] && sed -e "/test:\s*atx/d" -i $SRCCOPYDIR/template/src/test/regress/serial_schedule # for ENT 13

# Prepare separate installcheck directories
rm -rf  $SRCCOPYDIR/ic$SID-* || true
for c in `seq $NUM_INSTALLCHECKS`; do
  mkdir $SRCCOPYDIR/ic$SID-$c/
  cp -r $SRCCOPYDIR/template/* $SRCCOPYDIR/ic$SID-$c/

  # Fixing tests
  c03=`printf "%03d" $c`

  sed -e "s/pl_regression/pl_regress$c03/; s/contrib_regression/contrib_regress$c03/; s/isolation_regression/isolation_regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/Makefile.global

  sed -e "s/regression/regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/updatable_views.out
  sed -e "s/regression/regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/sequence.out
  sed -e "s/regression/regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/xmlmap.out

  sed -e "s/ regression/ regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/test/regress/sql/publication.sql \
    $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/publication.out

  sed -e "s/ regression/ regress$c03/" -i $SRCCOPYDIR/ic$SID-$c/src/test/regress/sql/dependency.sql \
    $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/dependency.out

  sed -e "s/test_file/t${c03}_file/g" -i \
    $SRCCOPYDIR/ic$SID-$c/contrib/adminpack/sql/adminpack.sql \
    $SRCCOPYDIR/ic$SID-$c/contrib/adminpack/expected/adminpack.out

  # Postgres Pro specifics
  [ -f $SRCCOPYDIR/ic$SID-$c/src/test/regress/sql/atx.sql ] && \
  sed -e "s/\\\\c regression\$/\\\\c regress$c03/g; s/regression_atx_test_database/regress${c03}_atx_test_database/g" -i \
    $SRCCOPYDIR/ic$SID-$c/src/test/regress/sql/atx.sql \
    $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/atx.out \
    $SRCCOPYDIR/ic$SID-$c/src/test/regress/expected/atx_*.out

  [ -f $SRCCOPYDIR/ic$SID-$c/src/test/regress/sql/atx.sql ] &&
    echo "max_autonomous_transactions = 100" >> "$PGDB/postgresql.auto.conf"

  [ -f $SRCCOPYDIR/ic$SID-$c/contrib/pg_query_state/specs/corner_cases.spec ] && sed -e "s/alice/alice$c03/g; s/bob/bob$c03/g; s/\bsuper\b/super$c03/g" -i $SRCCOPYDIR/ic$SID-$c/contrib/pg_query_state/specs/corner_cases.spec $SRCCOPYDIR/ic$SID-$c/contrib/pg_query_state/expected/*

  [ -d $SRCCOPYDIR/ic$SID-$c/contrib/pgpro_scheduler ] && sed -e "s/__temp_robot/__temp_robot$c03/g; s/__temp_root/__temp_root$c03/g" -i \
    $SRCCOPYDIR/ic$SID-$c/contrib/pgpro_scheduler/sql/cron_string.sql \
    $SRCCOPYDIR/ic$SID-$c/contrib/pgpro_scheduler/expected/cron_string.out

  [ -f $SRCCOPYDIR/ic$SID-$c/contrib/passwordcheck/sql/passwordcheck_n_passwordpolicy.sql ] && sed -e "s/pfl1/pfl1_$c03/g; s/pfl2/pfl2_$c03/g; s/\bu1\b/u1_$c03/g; s/\bu2\b/u2_$c03/g; s/\bu3\b/u3_$c03/g;" -i \
    $SRCCOPYDIR/ic$SID-$c/contrib/passwordcheck/sql/passwordcheck_n_passwordpolicy.sql \
    $SRCCOPYDIR/ic$SID-$c/contrib/passwordcheck/expected/passwordcheck_n_passwordpolicy.out

  # Adjust ecpg tests
  find $SRCCOPYDIR/ic$SID-$c/src/interfaces/ecpg/test \( -name *.stdout -o -name *.stderr -o -name *.pgc -o -name *.c -o -name *.h -o -name Makefile \) -exec sed -e \
"s/regress_ecpg_/regr${c03}_ecpg_/g; "\
"s/\(ecpg[0-9]*\)_regression/\1_regress${c03}/g; " -i {} \;

  # Adjust regress entities
  find  $SRCCOPYDIR/ic$SID-$c/ \( -name *.sql -o -name *.source -o -name *.out -o -name *.spec \) -exec sed -e \
"s/regress_\(tblspace\)/regr${c03}_\1/g; "\
"s/regress_\(vacuum\)/regr${c03}_\1/g; "\
"s/regress_\(other_partitioned_fk_owner\)/regr${c03}_\1/g; "\
"s/regress_\(testsub\)/regr${c03}_\1/g; "\
"s/regress_\(regrole_test\)/regr${c03}_\1/g; "\
"s/regress_\(stats_ext\)/regr${c03}_\1/g; "\
"s/regress_\(constraint_comments\)/regr${c03}_\1/g; "\
"s/regress_\(no_child_access\)/regr${c03}_\1/g; "\
"s/regress_\(rol_lock1\)/regr${c03}_\1/g; "\
"s/regress_\(nosuper\|view_owner\)/regr${c03}_\1/g; "\
"s/regress_\(truncate_conflict\)/regr${c03}_\1/g; "\
"s/regress_\(alice\|bob\|carol\|dave\)/regr${c03}_\1/g; "\
"s/regress_\([a-z_]*user[0-9]*\)/regr${c03}_\1/g; "\
"s/regress_\(bttest_role\)/regr${c03}_\1/g; "\
"s/regress_\(hacker\)/regr${c03}_\1/g; "\
"s/noadmin/noad${c03}/g; "\
"s/regress_\([a-z_]*role\)/regr${c03}_\1/g; "\
"s/regress_\([a-z_]*group\)/regr${c03}_\1/g; "\
"s/regress_\(test_[a-z0-9_]*\)/regr${c03}_\1/g; "\
"s/regress_\(priv_[a-z0-9]\+\)/regr${c03}_\1/g; "\
"s/regress_\(rls_[a-z0-9]\+\)/regr${c03}_\1/g; "\
"s/regress_\(passwd[a-z0-9_]*\)/regr${c03}_\1/g; " -i {} \;
# Postgres Pro specifics: regress_hacker, bttest_role, noadmin

done

# Start server
$PGBIN/pg_ctl -w -t 15 -D "$PGDB" -l server$SID.log start

$PGBIN/createdb -p $PGPORT test

# Run `make installcheck` in a loop
result=0
EXTRA_CPPFLAGS="-I $PGPREFIX/include -I$PGPREFIX/include/postgresql/server/ `$PGBIN/pg_config --cppflags`"
EXTRA_LDFLAGS="-lecpg -lecpg_compat -lpgtypes -L$PGPREFIX/lib `$PGBIN/pg_config --ldflags`"
time for i in `seq $NUM_ITERATIONS`; do
  echo "iteration $i: `date`"
  for c in `seq $NUM_INSTALLCHECKS`; do
    c03=`printf "%03d" $c`
    (cd $SRCCOPYDIR/ic$SID-$c; EXTRA_REGRESS_OPTS="--dbname=regress$c03" time make installcheck -C contrib/pg_visibility NO_GENERATED_HEADERS=1 CPPFLAGS="$EXTRA_CPPFLAGS" LDFLAGS="$EXTRA_LDFLAGS" || echo FAIL) >> installcheck$SID-$c.log 2>&1 &
  done
  wait
  echo "installchecks finished: `date`"

  if grep FAIL installcheck*.log; then
     echo "installchecks FAILED"
     result=1
     break
  fi
  sleep 3
#  coredumpctl --no-pager && result=1
  [ $result -eq 0 ] || break
done

if [ $result -eq 0 ]; then
  # Stop server on success
  $PGBIN/pg_ctl -w -t 90 -D "$PGDB" stop
  echo "ok"
fi
exit $result
