#!/bin/sh
# run-troc-mdev27992.sh
#
# Reproduce MDEV-27992 (silent lost DELETE) with TROC against a locally built
# server: bootstrap a throwaway datadir, start it, run four set-cases, print each
# TROC verdict, shut down. Builds nothing; reflects the compiled lock0lock.cc.
#
# Cases (isolation x index):
#   rc         READ COMMITTED, clustered (TROC bundled case mariadb27992.txt)
#   rr         REPEATABLE READ, clustered
#   rc_secidx  READ COMMITTED, secondary index
#   rr_secidx  REPEATABLE READ, secondary index
#
# A/B: build at 7bcbcb65ab6 (the fix) and 84a859a6366 (its parent); rerun each.
#
# Usage:    REPO=/path/to/server ./run-troc-mdev27992.sh
# Required: REPO; BLD defaults to $REPO/build/Debug.
# Env:      PORT (13307), PW (txntest), WORK (/tmp/troc-run), SOCK (/tmp/troc.sock),
#           IMAGE, KEEP_SERVER=1.

set -eu

# ---- configuration ----
: "${REPO:?Set REPO to your MariaDB source root, e.g. REPO=/path/to/server (BLD then defaults to \$REPO/build/Debug)}"
BLD="${BLD:-$REPO/build/Debug}"
WORK=${WORK:-/tmp/troc-run}
DATADIR="$WORK/datadir"
SOCK=${SOCK:-/tmp/troc.sock}
PORT=${PORT:-13307}
PW=${PW:-txntest}
IMAGE=${IMAGE:-ghcr.io/arcivanov/txn-tester:0.0.2}
TROC="$WORK/opt/tools/troc/target"
BUNDLED="$WORK/opt/tools/troc/cases/mariadb27992.txt"
CASES="$WORK/cases"
LOGDIR="$WORK/logs"

MARIADBD="$BLD/sql/mariadbd"
MARIADB="$BLD/client/mariadb"
MARIADB_ADMIN="$BLD/client/mariadb-admin"
INSTALL_DB="$BLD/scripts/mariadb-install-db"

SERVER_PID=""

log() { printf '>>> %s\n' "$*"; }

cleanup() {
    if [ "${KEEP_SERVER:-0}" = "1" ]; then
        log "KEEP_SERVER=1: leaving server (pid ${SERVER_PID:-?}) on port $PORT"
        return 0
    fi
    if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then
        log "shutting down server (pid $SERVER_PID)"
        "$MARIADB_ADMIN" --no-defaults --socket="$SOCK" -uroot -p"$PW" shutdown 2>/dev/null \
            || kill "$SERVER_PID" 2>/dev/null || true
        wait "$SERVER_PID" 2>/dev/null || true
    fi
}
trap cleanup EXIT INT TERM

find_jar() {
    for f in "$TROC"/troc-*.jar; do
        [ -e "$f" ] && { printf '%s\n' "$f"; return 0; }
    done
    return 1
}

run_case() {
    label=$1
    cf=$2
    out="$LOGDIR/troc-$label.log"
    timeout 120 java -jar "$TROC_JAR" \
        --dbms mariadb --host 127.0.0.1 --port "$PORT" \
        --username root --password "$PW" --db txntest_troc --table t \
        --set-case --case-file "$cf" > "$out" 2>&1 || true
    result=$(grep -aE '(Error|Ignore): ' "$out" | tail -n 1)
    fs_actual=$(grep -aE 'FinalState:' "$out" | head -n 1)
    fs_oracle=$(grep -aE 'FinalState:' "$out" | head -n 2 | tail -n 1)
    printf '%s:\n  %s\n  actual %s\n  oracle %s\n' "$label" \
        "${result:-(no TROC verdict line; see $out)}" \
        "${fs_actual:-FinalState: ?}" "${fs_oracle:-FinalState: ?}"
}

# ---- sanity: built binaries present ----
for bin in "$MARIADBD" "$MARIADB" "$MARIADB_ADMIN" "$INSTALL_DB"; do
    [ -x "$bin" ] || { echo "missing binary: $bin (build the tree first)" >&2; exit 1; }
done
command -v java >/dev/null 2>&1 || { echo "missing binary: java (a JRE is required to run TROC)" >&2; exit 1; }
case "$PW" in *\'*) echo "PW must not contain a single quote (it is interpolated into the setup SQL)" >&2; exit 1 ;; esac

mkdir -p "$WORK" "$LOGDIR" "$CASES"

# ---- fresh datadir each run ----
case "$DATADIR" in
    "$WORK"/*) : ;;
    *) echo "refusing to delete DATADIR outside WORK: $DATADIR" >&2; exit 1 ;;
esac
log "bootstrapping fresh datadir at $DATADIR"
rm -rf "$DATADIR"
mkdir -p "$DATADIR"
"$INSTALL_DB" --no-defaults --srcdir="$REPO" --builddir="$BLD" \
    --datadir="$DATADIR" --auth-root-authentication-method=normal \
    > "$LOGDIR/install.log" 2>&1

# ---- start server in the background ----
rm -f "$SOCK" "${SOCK%.sock}.pid"
log "starting mariadbd on 127.0.0.1:$PORT (socket $SOCK)"
"$MARIADBD" --no-defaults --datadir="$DATADIR" --socket="$SOCK" \
    --port="$PORT" --bind-address=127.0.0.1 --pid-file="${SOCK%.sock}.pid" \
    --log-error="$DATADIR/err.log" --lc-messages-dir="$BLD/sql/share" &
SERVER_PID=$!

log "waiting for server to accept connections"
if ! "$MARIADB_ADMIN" --no-defaults --socket="$SOCK" -uroot \
        --connect-timeout=2 --wait=30 ping > "$LOGDIR/ping.log" 2>&1; then
    echo "server did not come up; tail of error log:" >&2
    tail -n 20 "$DATADIR/err.log" >&2 || true
    exit 1
fi

# ---- credentials ----
log "setting root TCP password"
"$MARIADB" --no-defaults --socket="$SOCK" -uroot <<SQL
ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED BY '$PW';
ALTER USER IF EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '$PW';
ALTER USER IF EXISTS 'root'@'::1'       IDENTIFIED BY '$PW';
CREATE USER IF NOT EXISTS 'root'@'%'    IDENTIFIED BY '$PW';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SQL

VER=$("$MARIADB" --no-defaults --socket="$SOCK" -uroot -p"$PW" -N -B \
    -e "SELECT @@version" 2>/dev/null || true)
log "server version: ${VER:-unknown}"

# ---- TROC jar + bundled cases ----
if ! find_jar >/dev/null 2>&1 || [ ! -f "$BUNDLED" ]; then
    command -v skopeo >/dev/null 2>&1 || { echo "skopeo not found (needed to fetch TROC)" >&2; exit 1; }
    log "fetching TROC jar + cases from $IMAGE"
    rm -rf "$WORK/img"
    skopeo --insecure-policy copy "docker://$IMAGE" "dir:$WORK/img" > "$LOGDIR/skopeo.log" 2>&1
    for b in "$WORK"/img/*; do
        [ -f "$b" ] || continue
        tar -tzf "$b" 2>/dev/null | grep -q 'opt/tools/troc/target/troc-.*\.jar' || continue
        tar -xzf "$b" -C "$WORK" opt/tools/troc/target
        tar -xzf "$b" -C "$WORK" opt/tools/troc/cases 2>/dev/null || true
        break
    done
    find_jar >/dev/null 2>&1 || { echo "TROC jar not found in image layers" >&2; exit 1; }
    [ -f "$BUNDLED" ] || { echo "bundled case mariadb27992.txt not found in image" >&2; exit 1; }
    rm -rf "$WORK/img"
    log "extracted jar + bundled cases; removed image blobs at $WORK/img"
fi
TROC_JAR=$(find_jar) || { echo "no TROC jar under $TROC" >&2; exit 1; }
log "TROC jar: $TROC_JAR"
log "bundled rc case: $BUNDLED"

# ---- variant case files ----
log "writing variant case files to $CASES"

cat > "$CASES/mdev27992_rr.txt" <<'EOF'
CREATE TABLE t(c1 INT PRIMARY KEY)
INSERT INTO t(c1) VALUES (3)

RR
BEGIN
UPDATE t SET c1 = 2
UPDATE t SET c1 = 1
COMMIT

RR
BEGIN
DELETE FROM t
SELECT * FROM t FOR UPDATE
COMMIT

1-2-1-2-1-1-2-2
END
EOF

cat > "$CASES/mdev27992_rr_secidx.txt" <<'EOF'
CREATE TABLE t(k INT, KEY idx_k(k))
INSERT INTO t(k) VALUES (30)

RR
BEGIN
UPDATE t SET k = 20
UPDATE t SET k = 10
COMMIT

RR
BEGIN
DELETE t FROM t FORCE INDEX(idx_k) WHERE k > 0
SELECT * FROM t FOR UPDATE
COMMIT

1-2-1-2-1-1-2-2
END
EOF

cat > "$CASES/mdev27992_rc_secidx.txt" <<'EOF'
CREATE TABLE t(k INT, KEY idx_k(k))
INSERT INTO t(k) VALUES (30)

RC
BEGIN
UPDATE t SET k = 20
UPDATE t SET k = 10
COMMIT

RC
BEGIN
DELETE t FROM t FORCE INDEX(idx_k) WHERE k > 0
SELECT * FROM t FOR UPDATE
COMMIT

1-2-1-2-1-1-2-2
END
EOF

# ---- run TROC (from target/ for the jar's relative classpath) ----
cd "$TROC" || exit 1
log "running TROC (set-case): rc + rr + rc_secidx + rr_secidx"
printf '\n==== RESULTS (server %s) ====\n' "${VER:-unknown}"
run_case rc        "$BUNDLED"
run_case rr        "$CASES/mdev27992_rr.txt"
run_case rc_secidx "$CASES/mdev27992_rc_secidx.txt"
run_case rr_secidx "$CASES/mdev27992_rr_secidx.txt"
printf '=============================\n\n'
log "per-case logs in $LOGDIR"
