#!/bin/bash
# repro_mdev41903.sh
#
# Reproduces MDEV-41903: HA_xOPTION_SYSVAR table-option resolver lacks
# GET_DOUBLE (and signed integer) cases, causing a crash in release builds
# when a storage engine exposes a MYSQL_SYSVAR_DOUBLE as a CREATE TABLE
# option via HA_TOPTION_SYSVAR.
#
# Usage:
#   ./repro_mdev41903.sh <SOURCE_DIR> <BUILD_DIR>
#
#   SOURCE_DIR - path to a MariaDB git checkout (e.g. .../10.11_plugin)
#   BUILD_DIR  - path to its matching out-of-source cmake build dir
#                (e.g. .../10.11_plugin_release), already configured
#                and built at least once (so "cmake ." will reconfigure
#                in place rather than from scratch).
#
# Confirmed reproducing a server crash on:
#   - MariaDB 10.11.20 (commit 931ebfe9104ecf57ea2116e26f6e17eae8a16725)
#   - MariaDB 13.1.2   (commit 45cac7b67705967a21e44210c22da0e2a28a53c6)

set -e

if [ $# -ne 2 ]; then
  echo "Usage: $0 <SOURCE_DIR> <BUILD_DIR>"
  exit 1
fi

SRC="$(realpath "$1")"
BLD="$(realpath "$2")"
PLUGIN_DIR_NAME="example_double"
PLUGIN_SRC="$SRC/storage/$PLUGIN_DIR_NAME"

echo "=== MDEV-41903 repro ==="
echo "Source dir: $SRC"
echo "Build dir:  $BLD"
echo

# ---------------------------------------------------------------------
# 1. Create the test engine by copying storage/example
# ---------------------------------------------------------------------
if [ -d "$PLUGIN_SRC" ]; then
  echo "--- Removing existing $PLUGIN_SRC ---"
  rm -rf "$PLUGIN_SRC"
fi

echo "--- Copying storage/example -> storage/$PLUGIN_DIR_NAME ---"
cp -r "$SRC/storage/example" "$PLUGIN_SRC"
cd "$PLUGIN_SRC"
mv ha_example.cc ha_example_double.cc
mv ha_example.h ha_example_double.h

echo "--- Renaming example -> example_double throughout ---"
sed -i 's/ha_example\.h/ha_example_double.h/' ha_example_double.cc
sed -i 's/\bexample\b/example_double/g; s/\bEXAMPLE\b/EXAMPLE_DOUBLE/g' \
  ha_example_double.cc ha_example_double.h CMakeLists.txt
sed -i 's/ha_example\.cc)/ha_example_double.cc)/' CMakeLists.txt

# ---------------------------------------------------------------------
# 2. Add a double sysvar exposed as a table option (HA_TOPTION_SYSVAR)
# ---------------------------------------------------------------------
echo "--- Adding MYSQL_SYSVAR_DOUBLE + HA_TOPTION_SYSVAR(DOUBLE_OPT) ---"

# Declare the new sysvar right after the existing varopt_default sysvar.
# Order matters: this must appear before ha_table_option_struct and
# example_table_option_list[], since HA_TOPTION_SYSVAR's macro expansion
# needs MYSQL_SYSVAR_NAME(topt_double_var) already declared.
sed -i '/"default value of the VAROPT table option", NULL, NULL, 5, 0, 100, 0);/a\
\
static double srv_topt_double_var;\
static MYSQL_SYSVAR_DOUBLE(topt_double_var, srv_topt_double_var, PLUGIN_VAR_RQCMDARG,\
  "default value of the DOUBLE_OPT table option", NULL, NULL, 8.5, 0.5, 1000.5, 0);' \
  ha_example_double.cc

# Add a field to hold the table-option value.
sed -i 's/ulonglong varparam;/ulonglong varparam;\n  double double_opt;/' \
  ha_example_double.cc

# Expose it as a table option, right next to the existing VAROPT example.
sed -i 's/HA_TOPTION_SYSVAR("VAROPT", varparam, varopt_default),/HA_TOPTION_SYSVAR("VAROPT", varparam, varopt_default),\n  HA_TOPTION_SYSVAR("DOUBLE_OPT", double_opt, topt_double_var),/' \
  ha_example_double.cc

# Register the sysvar with the plugin.
sed -i 's/MYSQL_SYSVAR(varopt_default),/MYSQL_SYSVAR(varopt_default),\n  MYSQL_SYSVAR(topt_double_var),/' \
  ha_example_double.cc

echo "--- Verifying edits ---"
grep -n "topt_double_var\|DOUBLE_OPT\|double_opt" ha_example_double.cc
echo

# ---------------------------------------------------------------------
# 3. Build just this plugin against the existing build tree
# ---------------------------------------------------------------------
echo "--- Reconfiguring and building example_double ---"
cd "$BLD"
cmake . > /tmp/mdev41903_cmake.log 2>&1 || {
  echo "cmake reconfigure failed, see /tmp/mdev41903_cmake.log"; exit 1;
}
make example_double -j"$(nproc)" 2>&1 | tee /tmp/mdev41903_build.log

SO_PATH=$(find "$BLD/storage/$PLUGIN_DIR_NAME" -iname "ha_example_double.so" | head -1)
if [ -z "$SO_PATH" ]; then
  echo "Build failed to produce ha_example_double.so, see /tmp/mdev41903_build.log"
  exit 1
fi
echo "Built: $SO_PATH"

# ---------------------------------------------------------------------
# 4. Find the installed server tree and plugin directory
# ---------------------------------------------------------------------
INSTALL_DIR=$(find "$BLD/data/Server_bin" -maxdepth 1 -mindepth 1 -type d | head -1)
if [ -z "$INSTALL_DIR" ]; then
  echo "Could not find installed server under $BLD/data/Server_bin"
  exit 1
fi
PLUGIN_DIR="$INSTALL_DIR/lib/plugin"
mkdir -p "$PLUGIN_DIR"
if [ "$(realpath "$SO_PATH")" != "$(realpath "$PLUGIN_DIR/ha_example_double.so" 2>/dev/null)" ]; then
  cp "$SO_PATH" "$PLUGIN_DIR/"
fi
echo "Installed plugin to: $PLUGIN_DIR/ha_example_double.so"
echo

# ---------------------------------------------------------------------
# 5. Start a throwaway server instance
# ---------------------------------------------------------------------
TEST_DATADIR="/tmp/mdev41903_data_$$"
TEST_SOCK="/tmp/mdev41903_$$.sock"
TEST_PORT=$(( (RANDOM % 5000) + 15000 ))
SERVER_LOG="/tmp/mdev41903_server_$$.log"

echo "--- Setting up fresh datadir: $TEST_DATADIR ---"
mkdir -p "$TEST_DATADIR"
cd "$INSTALL_DIR"
scripts/mariadb-install-db --datadir="$TEST_DATADIR" \
  --auth-root-authentication-method=normal --basedir=. \
  > /tmp/mdev41903_install_db.log 2>&1

echo "--- Starting server (port $TEST_PORT) ---"
ulimit -c unlimited
bin/mariadbd --no-defaults \
  --datadir="$TEST_DATADIR" \
  --socket="$TEST_SOCK" \
  --port="$TEST_PORT" \
  --plugin-maturity=experimental \
  --core-file \
  > "$SERVER_LOG" 2>&1 &
SERVER_PID=$!

echo "Waiting for server to be ready..."
for i in $(seq 1 30); do
  if grep -q "ready for connections" "$SERVER_LOG" 2>/dev/null; then
    break
  fi
  sleep 1
done

if ! grep -q "ready for connections" "$SERVER_LOG" 2>/dev/null; then
  echo "Server did not come up, see $SERVER_LOG"
  exit 1
fi
echo "Server ready (pid $SERVER_PID)."
echo

# ---------------------------------------------------------------------
# 6. Run the actual repro
# ---------------------------------------------------------------------
echo "=== Running repro ==="
echo "+ INSTALL SONAME 'ha_example_double';"
echo "+ CREATE TABLE t1 (i INT) ENGINE=EXAMPLE_DOUBLE DOUBLE_OPT='2.5';"
echo

set +e
bin/mariadb -S "$TEST_SOCK" -uroot test -e "
INSTALL SONAME 'ha_example_double';
SHOW VARIABLES LIKE 'example_double_topt_double_var';
CREATE TABLE t1 (i INT) ENGINE=EXAMPLE_DOUBLE DOUBLE_OPT='2.5';
"
RESULT=$?
set -e

echo
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
  echo "=== SERVER CRASHED (as expected for MDEV-41903) ==="
  echo "Server log: $SERVER_LOG"
  CORE=$(find "$TEST_DATADIR" -iname "core*" | head -1)
  if [ -n "$CORE" ]; then
    echo "Core file:  $CORE"
    echo
    echo "To inspect:"
    echo "  gdb $INSTALL_DIR/bin/mariadbd $CORE -batch -ex 'bt full'"
  fi
  echo
  echo "Relevant tail of server log:"
  tail -40 "$SERVER_LOG"
else
  echo "=== Server still running — did not crash (bug may be fixed) ==="
  echo "Stopping test server..."
  bin/mariadb -S "$TEST_SOCK" -uroot -e "SHUTDOWN;" 2>/dev/null || kill "$SERVER_PID"
fi

echo
echo "Test datadir left at: $TEST_DATADIR (remove manually when done)"
echo "Server log left at:   $SERVER_LOG"
