test_bug19520.txt

text/plain

#!/bin/bash
#
# Reproducer for BUG #19520: PANIC when concurrently manipulating
# stored procedures with track_functions = all (or pl).
#
# On an unfixed build this PANICs. On a fixed build the server survives.

set -e

PGPORT="${1:-5432}"
PGDATABASE="postgres"
PGUSER="${USER:-postgres}"
N_DDL=10          # number of DROP/CREATE/CALL workers
N_CALL=20         # number of CALL-only workers
ITERATIONS=3000   # iterations per worker

export PGPORT PGDATABASE PGUSER

echo "=== BUG #19520 reproducer ==="
echo "Port: $PGPORT | DDL workers: $N_DDL | CALL workers: $N_CALL"
echo "Iterations per worker: $ITERATIONS"
echo ""

# Verify the server is reachable
if ! psql -Xc "SELECT 1" > /dev/null 2>&1; then
    echo "ERROR: Cannot connect to PostgreSQL on port $PGPORT."
    echo "Make sure the server is running with:"
    echo "  track_functions = 'all'  (or 'pl')"
    exit 1
fi

# Check track_functions setting
TF=$(psql -XtAc "SHOW track_functions")
if [ "$TF" = "none" ]; then
    echo "ERROR: track_functions = 'none'. Must be 'pl' or 'all'."
    exit 1
fi
echo "track_functions = '$TF'"

# Create the initial procedure
psql -Xc "CREATE OR REPLACE PROCEDURE proc_race_test()
           LANGUAGE plpgsql AS \$\$ BEGIN END; \$\$;" 2>/dev/null || true

PIDS=()

# Generate a SQL script for DDL workers (persistent connection, tight loop)
DDL_SQL=$(mktemp)
for j in $(seq 1 $ITERATIONS); do
    cat >> "$DDL_SQL" <<'EOF'
DROP PROCEDURE IF EXISTS proc_race_test;
CREATE OR REPLACE PROCEDURE proc_race_test() LANGUAGE plpgsql AS $$ BEGIN END; $$;
CALL proc_race_test();
EOF
done

# Generate a SQL script for CALL workers (persistent connection, tight loop)
CALL_SQL=$(mktemp)
for j in $(seq 1 $ITERATIONS); do
    echo "CALL proc_race_test();" >> "$CALL_SQL"
done

# Launch DDL workers using persistent psql sessions
for i in $(seq 1 $N_DDL); do
    psql -X -f "$DDL_SQL" > /dev/null 2>&1 &
    PIDS+=($!)
done

# Launch CALL-only workers using persistent psql sessions
for i in $(seq 1 $N_CALL); do
    psql -X -f "$CALL_SQL" > /dev/null 2>&1 &
    PIDS+=($!)
done

echo "Launched ${#PIDS[@]} background workers (persistent connections). Waiting..."
echo "(On an unfixed build the server will PANIC; on a fixed build it survives.)"
echo ""

# Wait for all workers
for pid in "${PIDS[@]}"; do
    wait "$pid" 2>/dev/null || true
done

# Cleanup temp files
rm -f "$DDL_SQL" "$CALL_SQL"

echo ""
echo "All workers finished."

# Check if the server is still alive
if psql -Xc "SELECT 1" > /dev/null 2>&1; then
    echo "PASS: Server is still running - no PANIC occurred."
    exit 0
else
    echo "FAIL: Server is down - likely hit the PANIC."
    echo "Check the server log for:"
    echo '  PANIC: cannot abort transaction xxx, it was already committed'
    exit 1
fi