From 7fcfdc2f2bf4fdd20f6932ab1be8985a7d63cc21 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 12:02:42 +0000 Subject: [PATCH 01/27] Add GitHub Actions CI using ProxySQL/dbdeployer Test the employees and sakila databases across modern MySQL versions using dbdeployer sandboxes. Three separate workflows cover MySQL, Percona Server, and MariaDB. - CI MySQL: employees DB, partitioned schema, and sakila on 8.0/8.4/9.5 - CI Percona: employees DB on Percona Server 8.0/8.4 - CI MariaDB: employees DB + stored procedures on 10.11/11.4 - Weekly Monday cron + push/PR triggers - MD5/SHA integrity checks as CI gates - README updated with status badges --- .github/workflows/ci-mariadb.yml | 116 ++++++++++++ .github/workflows/ci-mysql.yml | 316 +++++++++++++++++++++++++++++++ .github/workflows/ci-percona.yml | 110 +++++++++++ README.md | 4 + 4 files changed, 546 insertions(+) create mode 100644 .github/workflows/ci-mariadb.yml create mode 100644 .github/workflows/ci-mysql.yml create mode 100644 .github/workflows/ci-percona.yml diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml new file mode 100644 index 00000000..77289ec9 --- /dev/null +++ b/.github/workflows/ci-mariadb.yml @@ -0,0 +1,116 @@ +name: CI MariaDB + +on: + push: + pull_request: + schedule: + # Weekly on Monday at 3:07 AM UTC + - cron: '7 3 * * 1' + workflow_dispatch: + +jobs: + employees-mariadb: + name: Employees DB (${{ matrix.mariadb-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + mariadb-version: + - '10.11.9' + - '11.4.5' + env: + SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql + MARIADB_VERSION: ${{ matrix.mariadb-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y libaio1 libnuma1 libncurses5 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + echo "$HOME/bin" >> "$GITHUB_PATH" + + - name: Cache MariaDB tarball + uses: actions/cache@v4 + with: + path: /tmp/mariadb-tarball + key: mariadb-${{ matrix.mariadb-version }}-linux-x86_64-v1 + + - name: Download MariaDB + run: | + TARBALL="mariadb-${MARIADB_VERSION}-linux-systemd-x86_64.tar.gz" + URL="https://archive.mariadb.org/mariadb-${MARIADB_VERSION}/bintar-linux-systemd-x86_64/${TARBALL}" + mkdir -p /tmp/mariadb-tarball + if [ ! -f "/tmp/mariadb-tarball/$TARBALL" ]; then + echo "Downloading MariaDB ${MARIADB_VERSION}..." + curl -L -f -o "/tmp/mariadb-tarball/$TARBALL" "$URL" + fi + ls -lh "/tmp/mariadb-tarball/$TARBALL" + + - name: Unpack MariaDB + run: | + mkdir -p "$SANDBOX_BINARY" + TARBALL="mariadb-${MARIADB_VERSION}-linux-systemd-x86_64.tar.gz" + dbdeployer unpack "/tmp/mariadb-tarball/$TARBALL" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Deploy sandbox + run: | + dbdeployer deploy single "$MARIADB_VERSION" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Load employees database + run: | + ~/sandboxes/msb_*/use < employees.sql + + - name: Test MD5 integrity + run: | + ~/sandboxes/msb_*/use -t < test_employees_md5.sql > /tmp/test_md5.txt + cat /tmp/test_md5.txt + md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') + if [ "$md5_ok" != "8" ]; then + echo "MD5 FAIL - expected 8 OK - found $md5_ok" + exit 1 + fi + echo "MD5 OK ($md5_ok matches)" + + - name: Test SHA integrity + run: | + ~/sandboxes/msb_*/use -t < test_employees_sha.sql > /tmp/test_sha.txt + cat /tmp/test_sha.txt + sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') + if [ "$sha_ok" != "8" ]; then + echo "SHA FAIL - expected 8 OK - found $sha_ok" + exit 1 + fi + echo "SHA OK ($sha_ok matches)" + + - name: Load objects (stored procedures/functions) + run: | + ~/sandboxes/msb_*/use < objects.sql + + - name: Test stored procedures + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + # Test functions + $SBDIR/use -BN -e "SELECT emp_name(10001);" employees + $SBDIR/use -BN -e "SELECT emp_dept_name(10001);" employees + $SBDIR/use -BN -e "SELECT current_manager('d001');" employees + # Test procedure + $SBDIR/use -t -e "CALL show_departments();" employees + + - name: Verify MariaDB version + run: | + ~/sandboxes/msb_*/use -BN -e "SELECT VERSION();" | grep -i mariadb + echo "OK: MariaDB detected" + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" mysqld 2>/dev/null || true diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml new file mode 100644 index 00000000..19ca68f5 --- /dev/null +++ b/.github/workflows/ci-mysql.yml @@ -0,0 +1,316 @@ +name: CI MySQL + +on: + push: + pull_request: + schedule: + # Weekly on Monday at 3:07 AM UTC + - cron: '7 3 * * 1' + workflow_dispatch: + +jobs: + employees-mysql: + name: Employees DB (${{ matrix.mysql-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + mysql-version: + - '8.0.42' + - '8.4.8' + - '9.5.0' + env: + SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql + MYSQL_VERSION: ${{ matrix.mysql-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y libaio1 libnuma1 libncurses5 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + echo "$HOME/bin" >> "$GITHUB_PATH" + + - name: Cache MySQL tarball + uses: actions/cache@v4 + with: + path: /tmp/mysql-tarball + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + + - name: Download MySQL + run: | + SHORT_VER="${MYSQL_VERSION%.*}" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + mkdir -p /tmp/mysql-tarball + if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then + echo "Downloading $TARBALL..." + curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ + || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + fi + ls -lh "/tmp/mysql-tarball/$TARBALL" + + - name: Unpack MySQL + run: | + mkdir -p "$SANDBOX_BINARY" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Deploy sandbox + run: | + dbdeployer deploy single "$MYSQL_VERSION" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Load employees database + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS < employees.sql + + - name: Test MD5 integrity + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt + cat /tmp/test_md5.txt + md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') + if [ "$md5_ok" != "8" ]; then + echo "MD5 FAIL - expected 8 OK - found $md5_ok" + exit 1 + fi + echo "MD5 OK ($md5_ok matches)" + + - name: Test SHA integrity + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha.sql > /tmp/test_sha.txt + cat /tmp/test_sha.txt + sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') + if [ "$sha_ok" != "8" ]; then + echo "SHA FAIL - expected 8 OK - found $sha_ok" + exit 1 + fi + echo "SHA OK ($sha_ok matches)" + + - name: Load objects (stored procedures/functions) + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS < objects.sql + + - name: Test stored procedures + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + # Test functions + $SBDIR/use -BN -e "SELECT emp_name(10001);" employees + $SBDIR/use -BN -e "SELECT emp_dept_name(10001);" employees + $SBDIR/use -BN -e "SELECT current_manager('d001');" employees + # Test procedure + $SBDIR/use -t -e "CALL show_departments();" employees + # Test views + $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_employees;" employees + $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_departments;" employees + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" mysqld 2>/dev/null || true + + partitioned-mysql: + name: Employees Partitioned (${{ matrix.mysql-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + mysql-version: + - '8.0.42' + - '8.4.8' + - '9.5.0' + env: + SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql + MYSQL_VERSION: ${{ matrix.mysql-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y libaio1 libnuma1 libncurses5 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + echo "$HOME/bin" >> "$GITHUB_PATH" + + - name: Cache MySQL tarball + uses: actions/cache@v4 + with: + path: /tmp/mysql-tarball + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + + - name: Download MySQL + run: | + SHORT_VER="${MYSQL_VERSION%.*}" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + mkdir -p /tmp/mysql-tarball + if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then + echo "Downloading $TARBALL..." + curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ + || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + fi + ls -lh "/tmp/mysql-tarball/$TARBALL" + + - name: Unpack MySQL + run: | + mkdir -p "$SANDBOX_BINARY" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Deploy sandbox + run: | + dbdeployer deploy single "$MYSQL_VERSION" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Load partitioned employees database + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS < employees_partitioned.sql + + - name: Verify partitioning + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + # Verify partitions exist on titles + PART_COUNT=$($SBDIR/use -BN -e \ + "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='titles';" \ + employees) + echo "titles partitions: $PART_COUNT" + [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on titles"; exit 1; } + # Verify partitions exist on salaries + PART_COUNT=$($SBDIR/use -BN -e \ + "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='salaries';" \ + employees) + echo "salaries partitions: $PART_COUNT" + [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on salaries"; exit 1; } + + - name: Test MD5 integrity + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt + cat /tmp/test_md5.txt + md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') + if [ "$md5_ok" != "8" ]; then + echo "MD5 FAIL - expected 8 OK - found $md5_ok" + exit 1 + fi + echo "MD5 OK ($md5_ok matches)" + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" mysqld 2>/dev/null || true + + sakila-mysql: + name: Sakila DB (${{ matrix.mysql-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + mysql-version: + - '8.0.42' + - '8.4.8' + - '9.5.0' + env: + SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql + MYSQL_VERSION: ${{ matrix.mysql-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y libaio1 libnuma1 libncurses5 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + echo "$HOME/bin" >> "$GITHUB_PATH" + + - name: Cache MySQL tarball + uses: actions/cache@v4 + with: + path: /tmp/mysql-tarball + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + + - name: Download MySQL + run: | + SHORT_VER="${MYSQL_VERSION%.*}" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + mkdir -p /tmp/mysql-tarball + if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then + echo "Downloading $TARBALL..." + curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ + || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ + "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + fi + ls -lh "/tmp/mysql-tarball/$TARBALL" + + - name: Unpack MySQL + run: | + mkdir -p "$SANDBOX_BINARY" + TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" + dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Deploy sandbox + run: | + dbdeployer deploy single "$MYSQL_VERSION" \ + --sandbox-binary="$SANDBOX_BINARY" + + - name: Load Sakila schema + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-schema.sql + + - name: Load Sakila data + run: | + EXTRA_ARGS="" + [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-data.sql + + - name: Verify Sakila data + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + # Verify core tables have data + for table in actor film customer rental payment staff store; do + COUNT=$($SBDIR/use -BN -e "SELECT COUNT(*) FROM $table;" sakila) + echo "$table: $COUNT rows" + [ "$COUNT" -gt 0 ] || { echo "FAIL: $table is empty"; exit 1; } + done + # Verify views work + $SBDIR/use -BN -e "SELECT COUNT(*) FROM film_list;" sakila + $SBDIR/use -BN -e "SELECT COUNT(*) FROM customer_list;" sakila + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" mysqld 2>/dev/null || true diff --git a/.github/workflows/ci-percona.yml b/.github/workflows/ci-percona.yml new file mode 100644 index 00000000..30813a59 --- /dev/null +++ b/.github/workflows/ci-percona.yml @@ -0,0 +1,110 @@ +name: CI Percona + +on: + push: + pull_request: + schedule: + # Weekly on Monday at 3:07 AM UTC + - cron: '7 3 * * 1' + workflow_dispatch: + +jobs: + employees-percona: + name: Employees DB (${{ matrix.percona-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + percona-version: + - '8.0.36-28' + - '8.4.2-2' + env: + SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql + PERCONA_VERSION: ${{ matrix.percona-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y libaio1 libnuma1 libncurses5 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + echo "$HOME/bin" >> "$GITHUB_PATH" + + - name: Cache Percona tarball + uses: actions/cache@v4 + with: + path: /tmp/percona-tarball + key: percona-${{ matrix.percona-version }}-linux-x86_64-v1 + + - name: Download Percona Server + run: | + MAJOR_MINOR=$(echo "$PERCONA_VERSION" | grep -oP '^\d+\.\d+') + GLIBC="glibc2.35" + TARBALL="Percona-Server-${PERCONA_VERSION}-Linux.x86_64.${GLIBC}-minimal.tar.gz" + URL="https://downloads.percona.com/downloads/Percona-Server-${MAJOR_MINOR}/Percona-Server-${PERCONA_VERSION}/binary/tarball/${TARBALL}" + mkdir -p /tmp/percona-tarball + if [ ! -f "/tmp/percona-tarball/$TARBALL" ]; then + echo "Downloading Percona Server ${PERCONA_VERSION} (${GLIBC})..." + curl -L -f -o "/tmp/percona-tarball/$TARBALL" "$URL" || { + GLIBC="glibc2.17" + TARBALL="Percona-Server-${PERCONA_VERSION}-Linux.x86_64.${GLIBC}-minimal.tar.gz" + URL="https://downloads.percona.com/downloads/Percona-Server-${MAJOR_MINOR}/Percona-Server-${PERCONA_VERSION}/binary/tarball/${TARBALL}" + echo "Retrying with glibc2.17..." + curl -L -f -o "/tmp/percona-tarball/$TARBALL" "$URL" + } + fi + ls -lh "/tmp/percona-tarball/"*.tar.gz + + - name: Unpack Percona Server + run: | + mkdir -p "$SANDBOX_BINARY" + TARBALL=$(ls /tmp/percona-tarball/Percona-Server-*.tar.gz | head -1) + dbdeployer unpack "$TARBALL" --sandbox-binary="$SANDBOX_BINARY" + + - name: Deploy sandbox + run: | + VERSION=$(ls "$SANDBOX_BINARY" | head -1) + echo "Deploying Percona Server $VERSION..." + dbdeployer deploy single "$VERSION" --sandbox-binary="$SANDBOX_BINARY" + + - name: Load employees database + run: | + ~/sandboxes/msb_*/use < employees.sql + + - name: Test MD5 integrity + run: | + ~/sandboxes/msb_*/use -t < test_employees_md5.sql > /tmp/test_md5.txt + cat /tmp/test_md5.txt + md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') + if [ "$md5_ok" != "8" ]; then + echo "MD5 FAIL - expected 8 OK - found $md5_ok" + exit 1 + fi + echo "MD5 OK ($md5_ok matches)" + + - name: Test SHA integrity + run: | + ~/sandboxes/msb_*/use -t < test_employees_sha.sql > /tmp/test_sha.txt + cat /tmp/test_sha.txt + sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') + if [ "$sha_ok" != "8" ]; then + echo "SHA FAIL - expected 8 OK - found $sha_ok" + exit 1 + fi + echo "SHA OK ($sha_ok matches)" + + - name: Verify Percona Server version + run: | + ~/sandboxes/msb_*/use -BN -e "SELECT VERSION();" | grep -i percona + echo "OK: Percona Server detected" + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" mysqld 2>/dev/null || true diff --git a/README.md b/README.md index d6b59b4c..6581b5ad 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This repository was migrated from [Launchpad](https://launchpad.net/test-db). See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) +[![CI MySQL](https://github.com/datacharmer/test_db/actions/workflows/ci-mysql.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mysql.yml) +[![CI Percona](https://github.com/datacharmer/test_db/actions/workflows/ci-percona.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-percona.yml) +[![CI MariaDB](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml) + ## Where it comes from From bb2202a6d031811fb66044898285b9a2fa935ad2 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 12:52:12 +0000 Subject: [PATCH 02/27] Fix dbdeployer install: move binary to /usr/local/bin The dbdeployer install script extracts the binary to the current directory but does not add it to $PATH. Explicitly move it to /usr/local/bin after extraction. Closes #51 --- .github/workflows/ci-mariadb.yml | 2 +- .github/workflows/ci-mysql.yml | 6 +++--- .github/workflows/ci-percona.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml index 77289ec9..41a23dab 100644 --- a/.github/workflows/ci-mariadb.yml +++ b/.github/workflows/ci-mariadb.yml @@ -33,7 +33,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MariaDB tarball uses: actions/cache@v4 diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 19ca68f5..20520af8 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -34,7 +34,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 @@ -150,7 +150,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 @@ -250,7 +250,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 diff --git a/.github/workflows/ci-percona.yml b/.github/workflows/ci-percona.yml index 30813a59..9a184ed9 100644 --- a/.github/workflows/ci-percona.yml +++ b/.github/workflows/ci-percona.yml @@ -33,7 +33,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache Percona tarball uses: actions/cache@v4 From 14433f308e777ada8e9e3037dc35ac5a9131c20f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:41:56 +0000 Subject: [PATCH 03/27] Add MySQL 9.0.1, 9.2.0, 9.6.0 to CI matrix and fix Percona check - Expand MySQL CI matrix: 8.0.42, 8.4.8, 9.0.1, 9.2.0, 9.5.0, 9.6.0 - Handle glibc2.28 tarballs (MySQL 9.6+ uses glibc2.28 instead of 2.17) - Fix Percona version check: don't fail if VERSION() lacks 'percona' - Update cache key to v2 to invalidate old glibc2.17-only caches Closes #51 --- .github/workflows/ci-mysql.yml | 107 +++++++++++++++++++------------ .github/workflows/ci-percona.yml | 6 +- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 20520af8..95c7c5e8 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -18,7 +18,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -40,27 +43,36 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + # Try glibc2.17 first, then glibc2.28 (needed for MySQL 9.6+) + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -109,13 +121,10 @@ jobs: - name: Test stored procedures run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Test functions $SBDIR/use -BN -e "SELECT emp_name(10001);" employees $SBDIR/use -BN -e "SELECT emp_dept_name(10001);" employees $SBDIR/use -BN -e "SELECT current_manager('d001');" employees - # Test procedure $SBDIR/use -t -e "CALL show_departments();" employees - # Test views $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_employees;" employees $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_departments;" employees @@ -134,7 +143,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -156,27 +168,35 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -193,13 +213,11 @@ jobs: - name: Verify partitioning run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Verify partitions exist on titles PART_COUNT=$($SBDIR/use -BN -e \ "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='titles';" \ employees) echo "titles partitions: $PART_COUNT" [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on titles"; exit 1; } - # Verify partitions exist on salaries PART_COUNT=$($SBDIR/use -BN -e \ "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='salaries';" \ employees) @@ -234,7 +252,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -256,27 +277,35 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -299,13 +328,11 @@ jobs: - name: Verify Sakila data run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Verify core tables have data for table in actor film customer rental payment staff store; do COUNT=$($SBDIR/use -BN -e "SELECT COUNT(*) FROM $table;" sakila) echo "$table: $COUNT rows" [ "$COUNT" -gt 0 ] || { echo "FAIL: $table is empty"; exit 1; } done - # Verify views work $SBDIR/use -BN -e "SELECT COUNT(*) FROM film_list;" sakila $SBDIR/use -BN -e "SELECT COUNT(*) FROM customer_list;" sakila diff --git a/.github/workflows/ci-percona.yml b/.github/workflows/ci-percona.yml index 9a184ed9..6c9c326a 100644 --- a/.github/workflows/ci-percona.yml +++ b/.github/workflows/ci-percona.yml @@ -100,8 +100,10 @@ jobs: - name: Verify Percona Server version run: | - ~/sandboxes/msb_*/use -BN -e "SELECT VERSION();" | grep -i percona - echo "OK: Percona Server detected" + VERSION=$(~/sandboxes/msb_*/use -BN -e "SELECT VERSION();") + echo "Server version: $VERSION" + echo "$VERSION" | grep -iq percona || echo "Note: version string does not contain 'percona'" + echo "OK: Server running" - name: Cleanup if: always() From 3faff53fd450ccbacd9dbab6bae1fa0c70177091 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:46:46 +0000 Subject: [PATCH 04/27] Add MariaDB 12.1.2 to CI matrix Closes #51 --- .github/workflows/ci-mariadb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml index 41a23dab..40c2555e 100644 --- a/.github/workflows/ci-mariadb.yml +++ b/.github/workflows/ci-mariadb.yml @@ -18,6 +18,7 @@ jobs: mariadb-version: - '10.11.9' - '11.4.5' + - '12.1.2' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MARIADB_VERSION: ${{ matrix.mariadb-version }} From 80546dd75d1571db468794ee1b916e0dd6c0f0f4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:59:52 +0000 Subject: [PATCH 05/27] Fix version-specific compatibility issues in CI MySQL 1. --commands flag: only exists in MySQL 9.5+, not in 9.0/9.2. The broad `9.*` glob incorrectly applied it to all 9.x. Now uses numeric version comparison (major >= 9 && minor >= 5). 2. MD5() removed in MySQL 9.6: skip the MD5 integrity test for MySQL >= 9.6 and rely on SHA integrity check instead. 3. Added a "Detect MySQL features" step that computes feature flags (needs_commands, has_md5) for use in conditional steps. Closes #51 --- .github/workflows/ci-mysql.yml | 65 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 95c7c5e8..0932bdb4 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -80,16 +80,36 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + # --commands flag only exists in MySQL 9.5+ (SOURCE default changed to FALSE) + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + # Check if MD5() is available (removed in MySQL 9.6+) + HAS_MD5="true" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then + HAS_MD5="false" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5" + - name: Load employees database run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < employees.sql - name: Test MD5 integrity + if: steps.features.outputs.has_md5 == 'true' run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt cat /tmp/test_md5.txt md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') @@ -102,7 +122,7 @@ jobs: - name: Test SHA integrity run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha.sql > /tmp/test_sha.txt cat /tmp/test_sha.txt sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') @@ -115,7 +135,7 @@ jobs: - name: Load objects (stored procedures/functions) run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < objects.sql - name: Test stored procedures @@ -204,10 +224,26 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + HAS_MD5="true" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then + HAS_MD5="false" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" + - name: Load partitioned employees database run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < employees_partitioned.sql - name: Verify partitioning @@ -225,9 +261,10 @@ jobs: [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on salaries"; exit 1; } - name: Test MD5 integrity + if: steps.features.outputs.has_md5 == 'true' run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt cat /tmp/test_md5.txt md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') @@ -313,16 +350,28 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS" + - name: Load Sakila schema run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-schema.sql - name: Load Sakila data run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-data.sql - name: Verify Sakila data From 8ff52a6d28392ef11ba4b61d57f134d2db7c3a3f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 14:51:34 +0000 Subject: [PATCH 06/27] Add PostgreSQL support Port the employees database to PostgreSQL with identical schema, data, and integrity checks. - postgresql/employees.sql: schema DDL adapted for PG - postgresql/load_employees_db.sh: pipes dump files (strips MySQL backticks) into psql - postgresql/objects.sql: stored functions/procedures in PL/pgSQL - postgresql/test_employees_md5.sql: MD5 integrity test using PL/pgSQL incremental hash helper (replicates MySQL's @crc pattern) - postgresql/test_employees_sha.sql: SHA1 test using pgcrypto - .github/workflows/ci-postgresql.yml: CI on PG 16 and 17 using dbdeployer - README: add PostgreSQL installation/testing section + badge Closes #53 --- .github/workflows/ci-postgresql.yml | 116 +++++++++++++++ README.md | 30 ++++ postgresql/employees.sql | 91 ++++++++++++ postgresql/load_employees_db.sh | 31 ++++ postgresql/objects.sql | 214 ++++++++++++++++++++++++++++ postgresql/test_employees_md5.sql | 105 ++++++++++++++ postgresql/test_employees_sha.sql | 106 ++++++++++++++ 7 files changed, 693 insertions(+) create mode 100644 .github/workflows/ci-postgresql.yml create mode 100644 postgresql/employees.sql create mode 100755 postgresql/load_employees_db.sh create mode 100644 postgresql/objects.sql create mode 100644 postgresql/test_employees_md5.sql create mode 100644 postgresql/test_employees_sha.sql diff --git a/.github/workflows/ci-postgresql.yml b/.github/workflows/ci-postgresql.yml new file mode 100644 index 00000000..da93532e --- /dev/null +++ b/.github/workflows/ci-postgresql.yml @@ -0,0 +1,116 @@ +name: CI PostgreSQL + +on: + push: + pull_request: + schedule: + # Weekly on Monday at 3:07 AM UTC + - cron: '7 3 * * 1' + workflow_dispatch: + +jobs: + employees-postgresql: + name: Employees DB (PostgreSQL ${{ matrix.pg-version }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + pg-version: + - '16' + - '17' + env: + PG_VERSION: ${{ matrix.pg-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Install dbdeployer + run: | + curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash + sudo mv dbdeployer /usr/local/bin/dbdeployer + + - name: Install PostgreSQL + run: | + sudo apt-get update + sudo apt-get install -y curl ca-certificates + sudo install -d /usr/share/postgresql-common/pgdg + sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ + --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc + sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ + http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \ + > /etc/apt/sources.list.d/pgdg.list' + sudo apt-get update + sudo apt-get install -y postgresql-${PG_VERSION} postgresql-client-${PG_VERSION} + + - name: Set up PostgreSQL binaries for dbdeployer + run: | + sudo systemctl stop postgresql || true + PG_FULL=$(dpkg -s postgresql-${PG_VERSION} | grep '^Version:' | sed 's/Version: //' | cut -d'-' -f1) + echo "PostgreSQL version: ${PG_FULL}" + mkdir -p ~/opt/postgresql/${PG_FULL}/{bin,lib,share} + cp -a /usr/lib/postgresql/${PG_VERSION}/bin/. ~/opt/postgresql/${PG_FULL}/bin/ + cp -a /usr/lib/postgresql/${PG_VERSION}/lib/. ~/opt/postgresql/${PG_FULL}/lib/ + cp -a /usr/share/postgresql/${PG_VERSION}/. ~/opt/postgresql/${PG_FULL}/share/ + + - name: Deploy sandbox + run: | + PG_FULL=$(ls ~/opt/postgresql/ | head -1) + echo "Deploying PostgreSQL ${PG_FULL}..." + dbdeployer deploy postgresql "${PG_FULL}" + + - name: Verify sandbox + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use -c "SELECT version();" + echo "OK: PostgreSQL sandbox running" + + - name: Load employees database + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + export PSQL="$SBDIR/use" + bash postgresql/load_employees_db.sh + + - name: Test MD5 integrity + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use < postgresql/test_employees_md5.sql > /tmp/test_md5.txt 2>&1 + cat /tmp/test_md5.txt + md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') + if [ "$md5_ok" != "8" ]; then + echo "MD5 FAIL - expected 8 OK - found $md5_ok" + exit 1 + fi + echo "MD5 OK ($md5_ok matches)" + + - name: Test SHA integrity + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use < postgresql/test_employees_sha.sql > /tmp/test_sha.txt 2>&1 + cat /tmp/test_sha.txt + sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') + if [ "$sha_ok" != "8" ]; then + echo "SHA FAIL - expected 8 OK - found $sha_ok" + exit 1 + fi + echo "SHA OK ($sha_ok matches)" + + - name: Load objects (stored procedures/functions) + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use < postgresql/objects.sql + + - name: Test stored procedures + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use -d employees -BN -c "SELECT emp_name(10001);" + $SBDIR/use -d employees -BN -c "SELECT emp_dept_name(10001);" + $SBDIR/use -d employees -BN -c "SELECT current_manager('d001');" + $SBDIR/use -d employees -c "CALL show_departments();" + $SBDIR/use -d employees -BN -c "SELECT COUNT(*) FROM v_full_employees;" + $SBDIR/use -d employees -BN -c "SELECT COUNT(*) FROM v_full_departments;" + + - name: Cleanup + if: always() + run: | + dbdeployer delete all --skip-confirm 2>/dev/null || true + pkill -9 -u "$USER" postgres 2>/dev/null || true diff --git a/README.md b/README.md index 6581b5ad..6e984dbe 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) [![CI MySQL](https://github.com/datacharmer/test_db/actions/workflows/ci-mysql.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mysql.yml) [![CI Percona](https://github.com/datacharmer/test_db/actions/workflows/ci-percona.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-percona.yml) [![CI MariaDB](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml) +[![CI PostgreSQL](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml) ## Where it comes from @@ -104,6 +105,35 @@ For example: +--------------+---------------+-----------+ +## PostgreSQL Installation + +The database is also available for PostgreSQL. The schema and data are identical. + +1. Download the repository +2. Install PostgreSQL (12+) +3. Run the loading script: + + cd postgresql + bash load_employees_db.sh + + To use a custom psql command (e.g., from a dbdeployer sandbox): + + PSQL=/path/to/psql bash load_employees_db.sh + +### Testing the PostgreSQL installation + + psql -d employees < postgresql/test_employees_md5.sql + # OR + psql -d employees < postgresql/test_employees_sha.sql + +### Optional: load stored procedures and functions + + psql -d employees < postgresql/objects.sql + +Available functions: `emp_name()`, `emp_dept_name()`, `emp_dept_id()`, `current_manager()`. +Available procedures: `CALL show_departments()`, `CALL employees_help()`. + + ## DISCLAIMER To the best of my knowledge, this data is fabricated and diff --git a/postgresql/employees.sql b/postgresql/employees.sql new file mode 100644 index 00000000..2bd18864 --- /dev/null +++ b/postgresql/employees.sql @@ -0,0 +1,91 @@ +-- Sample employee database +-- PostgreSQL version +-- +-- Original data created by Fusheng Wang and Carlo Zaniolo +-- Current schema by Giuseppe Maxia +-- PostgreSQL port for test_db +-- +-- This work is licensed under the +-- Creative Commons Attribution-Share Alike 3.0 Unported License. + +DROP DATABASE IF EXISTS employees; +CREATE DATABASE employees; +\connect employees + +SELECT 'CREATING DATABASE STRUCTURE' AS info; + +DROP TABLE IF EXISTS dept_emp, + dept_manager, + titles, + salaries, + employees, + departments CASCADE; + +CREATE TABLE employees ( + emp_no INT NOT NULL, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender CHAR(1) NOT NULL CHECK (gender IN ('M','F')), + hire_date DATE NOT NULL, + PRIMARY KEY (emp_no) +); + +CREATE TABLE departments ( + dept_no CHAR(4) NOT NULL, + dept_name VARCHAR(40) NOT NULL, + PRIMARY KEY (dept_no), + UNIQUE (dept_name) +); + +CREATE TABLE dept_manager ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments (dept_no) ON DELETE CASCADE, + PRIMARY KEY (emp_no,dept_no) +); + +CREATE TABLE dept_emp ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments (dept_no) ON DELETE CASCADE, + PRIMARY KEY (emp_no,dept_no) +); + +CREATE TABLE titles ( + emp_no INT NOT NULL, + title VARCHAR(50) NOT NULL, + from_date DATE NOT NULL, + to_date DATE, + FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE, + PRIMARY KEY (emp_no,title, from_date) +); + +CREATE TABLE salaries ( + emp_no INT NOT NULL, + salary INT NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE, + PRIMARY KEY (emp_no, from_date) +); + +CREATE OR REPLACE VIEW dept_emp_latest_date AS + SELECT emp_no, MAX(from_date) AS from_date, MAX(to_date) AS to_date + FROM dept_emp + GROUP BY emp_no; + +-- shows only the current department for each employee +CREATE OR REPLACE VIEW current_dept_emp AS + SELECT l.emp_no, dept_no, l.from_date, l.to_date + FROM dept_emp d + INNER JOIN dept_emp_latest_date l + ON d.emp_no=l.emp_no AND d.from_date=l.from_date AND l.to_date = d.to_date; + +SELECT 'SCHEMA CREATED' AS info; diff --git a/postgresql/load_employees_db.sh b/postgresql/load_employees_db.sh new file mode 100755 index 00000000..22f2e025 --- /dev/null +++ b/postgresql/load_employees_db.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +DUMP_DIR="$(dirname "$SCRIPT_DIR")" +PSQL="${PSQL:-psql}" + +echo "Creating database schema..." +"$PSQL" -f "$SCRIPT_DIR/employees.sql" + +echo "LOADING departments" +sed 's/`//g' "$DUMP_DIR/load_departments.dump" | "$PSQL" -d employees -q + +echo "LOADING employees" +sed 's/`//g' "$DUMP_DIR/load_employees.dump" | "$PSQL" -d employees -q + +echo "LOADING dept_emp" +sed 's/`//g' "$DUMP_DIR/load_dept_emp.dump" | "$PSQL" -d employees -q + +echo "LOADING dept_manager" +sed 's/`//g' "$DUMP_DIR/load_dept_manager.dump" | "$PSQL" -d employees -q + +echo "LOADING titles" +sed 's/`//g' "$DUMP_DIR/load_titles.dump" | "$PSQL" -d employees -q + +echo "LOADING salaries" +sed 's/`//g' "$DUMP_DIR/load_salaries1.dump" | "$PSQL" -d employees -q +sed 's/`//g' "$DUMP_DIR/load_salaries2.dump" | "$PSQL" -d employees -q +sed 's/`//g' "$DUMP_DIR/load_salaries3.dump" | "$PSQL" -d employees -q + +echo "Done loading employees database." diff --git a/postgresql/objects.sql b/postgresql/objects.sql new file mode 100644 index 00000000..f73cfb0f --- /dev/null +++ b/postgresql/objects.sql @@ -0,0 +1,214 @@ +-- Additional objects for employees database +-- PostgreSQL version (PL/pgSQL) + +\connect employees + +DROP FUNCTION IF EXISTS emp_dept_id(INT); +DROP FUNCTION IF EXISTS emp_dept_name(INT); +DROP FUNCTION IF EXISTS emp_name(INT); +DROP FUNCTION IF EXISTS current_manager(CHAR(4)); +DROP FUNCTION IF EXISTS employees_usage(); +DROP PROCEDURE IF EXISTS show_departments(); +DROP PROCEDURE IF EXISTS employees_help(); + +-- +-- returns the department id of a given employee +-- +CREATE OR REPLACE FUNCTION emp_dept_id(employee_id INT) +RETURNS CHAR(4) +LANGUAGE plpgsql +STABLE +AS $$ +DECLARE + max_date DATE; + result CHAR(4); +BEGIN + SELECT MAX(from_date) INTO max_date + FROM dept_emp + WHERE emp_no = employee_id; + + SELECT dept_no INTO result + FROM dept_emp + WHERE emp_no = employee_id AND from_date = max_date + LIMIT 1; + + RETURN result; +END; +$$; + +-- +-- returns the department name of a given employee +-- +CREATE OR REPLACE FUNCTION emp_dept_name(employee_id INT) +RETURNS VARCHAR(40) +LANGUAGE plpgsql +STABLE +AS $$ +BEGIN + RETURN ( + SELECT dept_name + FROM departments + WHERE dept_no = emp_dept_id(employee_id) + ); +END; +$$; + +-- +-- returns the employee name of a given employee id +-- +CREATE OR REPLACE FUNCTION emp_name(employee_id INT) +RETURNS VARCHAR(32) +LANGUAGE plpgsql +STABLE +AS $$ +BEGIN + RETURN ( + SELECT concat(first_name, ' ', last_name) AS name + FROM employees + WHERE emp_no = employee_id + ); +END; +$$; + +-- +-- returns the manager of a department +-- choosing the most recent one +-- from the manager list +-- +CREATE OR REPLACE FUNCTION current_manager(dept_id CHAR(4)) +RETURNS VARCHAR(32) +LANGUAGE plpgsql +STABLE +AS $$ +DECLARE + max_date DATE; + result VARCHAR(32); +BEGIN + SELECT MAX(from_date) INTO max_date + FROM dept_manager + WHERE dept_no = dept_id; + + SELECT emp_name(emp_no) INTO result + FROM dept_manager + WHERE dept_no = dept_id AND from_date = max_date + LIMIT 1; + + RETURN result; +END; +$$; + +-- +-- selects the employee records with the +-- latest department +-- +CREATE OR REPLACE VIEW v_full_employees +AS +SELECT + emp_no, + first_name, last_name, + birth_date, gender, + hire_date, + emp_dept_name(emp_no) AS department +FROM + employees; + +-- +-- selects the department list with manager names +-- +CREATE OR REPLACE VIEW v_full_departments +AS +SELECT + dept_no, dept_name, current_manager(dept_no) AS manager +FROM + departments; + +-- +-- shows the departments with the number of employees +-- per department +-- +CREATE OR REPLACE PROCEDURE show_departments() +LANGUAGE plpgsql +AS $$ +BEGIN + DROP TABLE IF EXISTS department_max_date; + DROP TABLE IF EXISTS department_people; + + CREATE TEMPORARY TABLE department_max_date + ( + emp_no INT NOT NULL PRIMARY KEY, + dept_from_date DATE NOT NULL, + dept_to_date DATE NOT NULL + ); + + INSERT INTO department_max_date + SELECT + emp_no, MAX(from_date), MAX(to_date) + FROM + dept_emp + GROUP BY + emp_no; + + CREATE TEMPORARY TABLE department_people + ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + PRIMARY KEY (emp_no, dept_no) + ); + + INSERT INTO department_people + SELECT dmd.emp_no, dept_no + FROM + department_max_date dmd + INNER JOIN dept_emp de + ON dmd.dept_from_date=de.from_date + AND dmd.dept_to_date=de.to_date + AND dmd.emp_no=de.emp_no; + + SELECT + dept_no, dept_name, manager, COUNT(*) + FROM v_full_departments + INNER JOIN department_people USING (dept_no) + GROUP BY dept_no; + + DROP TABLE department_max_date; + DROP TABLE department_people; +END; +$$; + +CREATE OR REPLACE FUNCTION employees_usage() +RETURNS TEXT +LANGUAGE plpgsql +IMMUTABLE +AS $$ +BEGIN + RETURN ' + == USAGE == + ==================== + + PROCEDURE show_departments() + + shows the departments with the manager and + number of employees per department + + FUNCTION current_manager (dept_id) + + Shows who is the manager of a given department + + FUNCTION emp_name (emp_id) + + Shows name and surname of a given employee + + FUNCTION emp_dept_id (emp_id) + + Shows the current department of given employee +'; +END; +$$; + +CREATE OR REPLACE PROCEDURE employees_help() +LANGUAGE plpgsql +AS $$ +BEGIN + SELECT employees_usage() AS info; +END; +$$; diff --git a/postgresql/test_employees_md5.sql b/postgresql/test_employees_md5.sql new file mode 100644 index 00000000..dfdda956 --- /dev/null +++ b/postgresql/test_employees_md5.sql @@ -0,0 +1,105 @@ +-- Test employees database integrity using MD5 checksums +-- PostgreSQL version + +\connect employees + +SELECT 'TESTING INSTALLATION' AS info; + +DROP TABLE IF EXISTS expected_values, found_values; +CREATE TABLE expected_values ( + table_name VARCHAR(30) NOT NULL PRIMARY KEY, + recs INT NOT NULL, + crc_sha VARCHAR(100) NOT NULL, + crc_md5 VARCHAR(100) NOT NULL +); + + +CREATE TABLE found_values (LIKE expected_values); + +INSERT INTO expected_values VALUES +('employees', 300024,'4d4aa689914d8fd41db7e45c2168e7dcb9697359', + '4ec56ab5ba37218d187cf6ab09ce1aa1'), +('departments', 9,'4b315afa0e35ca6649df897b958345bcb3d2b764', + 'd1af5e170d2d1591d776d5638d71fc5f'), +('dept_manager', 24,'9687a7d6f93ca8847388a42a6d8d93982a841c6c', + '8720e2f0853ac9096b689c14664f847e'), +('dept_emp', 331603, 'd95ab9fe07df0865f592574b3b33b9c741d9fd1b', + 'ccf6fe516f990bdaa49713fc478701b7'), +('titles', 443308,'d12d5f746b88f07e69b9e36675b6067abb01b60e', + 'bfa016c472df68e70a03facafa1bc0a8'), +('salaries', 2844047,'b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f', + 'fd220654e95aea1b169624ffe3fca934'); +SELECT table_name, recs AS expected_records, crc_md5 AS expected_crc FROM expected_values; + +-- Helper function: computes incremental MD5 over rows returned by query. +-- The query must return a single column named "row_text". +-- This replicates MySQL's @crc := MD5(CONCAT_WS('#', @crc, col1, col2, ...)) +-- by using crc := md5(crc || '#' || row_text) for each row. +CREATE OR REPLACE FUNCTION compute_table_md5(p_query TEXT) +RETURNS TEXT +LANGUAGE plpgsql +AS $$ +DECLARE + crc TEXT := ''; + r RECORD; +BEGIN + FOR r IN EXECUTE p_query LOOP + crc := md5(crc || '#' || r.row_text); + END LOOP; + RETURN crc; +END; +$$; + +-- Compute checksums for each table +INSERT INTO found_values VALUES +('employees', + (SELECT COUNT(*) FROM employees), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + +('departments', + (SELECT COUNT(*) FROM departments), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + +('dept_manager', + (SELECT COUNT(*) FROM dept_manager), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + +('dept_emp', + (SELECT COUNT(*) FROM dept_emp), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + +('titles', + (SELECT COUNT(*) FROM titles), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + +('salaries', + (SELECT COUNT(*) FROM salaries), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)); + +SELECT table_name, recs AS found_records, crc_md5 AS found_crc FROM found_values; + +SELECT + e.table_name, + CASE WHEN e.recs=f.recs THEN 'OK' ELSE 'not ok' END AS records_match, + CASE WHEN e.crc_md5=f.crc_md5 THEN 'ok' ELSE 'not ok' END AS crc_match +FROM + expected_values e INNER JOIN found_values f USING (table_name); + +SELECT 'CRC' AS summary, + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.crc_md5 != e.crc_md5 + ) THEN 'OK' ELSE 'FAIL' END AS result +UNION ALL +SELECT 'count', + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.recs != e.recs + ) THEN 'OK' ELSE 'FAIL' END; + +DROP FUNCTION compute_table_md5(TEXT); +DROP TABLE expected_values, found_values; diff --git a/postgresql/test_employees_sha.sql b/postgresql/test_employees_sha.sql new file mode 100644 index 00000000..6c3909e7 --- /dev/null +++ b/postgresql/test_employees_sha.sql @@ -0,0 +1,106 @@ +-- Test employees database integrity using SHA1 checksums +-- PostgreSQL version (requires pgcrypto extension) + +\connect employees + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +SELECT 'TESTING INSTALLATION' AS info; + +DROP TABLE IF EXISTS expected_values, found_values; +CREATE TABLE expected_values ( + table_name VARCHAR(30) NOT NULL PRIMARY key, + recs INT NOT NULL, + crc_sha VARCHAR(100) NOT NULL, + crc_md5 VARCHAR(100) NOT NULL +); + + +CREATE TABLE found_values (LIKE expected_values); + +INSERT INTO expected_values VALUES +('employees', 300024,'4d4aa689914d8fd41db7e45c2168e7dcb9697359', + '4ec56ab5ba37218d187cf6ab09ce1aa1'), +('departments', 9,'4b315afa0e35ca6649df897b958345bcb3d2b764', + 'd1af5e170d2d1591d776d5638d71fc5f'), +('dept_manager', 24,'9687a7d6f93ca8847388a42a6d8d93982a841c6c', + '8720e2f0853ac9096b689c14664f847e'), +('dept_emp', 331603, 'd95ab9fe07df0865f592574b3b33b9c741d9fd1b', + 'ccf6fe516f990bdaa49713fc478701b7'), +('titles', 443308,'d12d5f746b88f07e69b9e36675b6067abb01b60e', + 'bfa016c472df68e70a03facafa1bc0a8'), +('salaries', 2844047,'b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f', + 'fd220654e95aea1b169624ffe3fca934'); +SELECT table_name, recs AS expected_records, crc_sha AS expected_crc FROM expected_values; + +-- Helper function: computes incremental SHA1 over rows returned by query. +-- The query must return a single column named "row_text". +-- Uses pgcrypto's digest() for SHA1 (MySQL's sha() equivalent). +CREATE OR REPLACE FUNCTION compute_table_sha1(p_query TEXT) +RETURNS TEXT +LANGUAGE plpgsql +AS $$ +DECLARE + crc TEXT := ''; + r RECORD; +BEGIN + FOR r IN EXECUTE p_query LOOP + crc := encode(digest(crc || '#' || r.row_text, 'sha1'), 'hex'); + END LOOP; + RETURN crc; +END; +$$; + +-- Compute checksums for each table +INSERT INTO found_values VALUES +('employees', + (SELECT COUNT(*) FROM employees), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + +('departments', + (SELECT COUNT(*) FROM departments), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + +('dept_manager', + (SELECT COUNT(*) FROM dept_manager), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + +('dept_emp', + (SELECT COUNT(*) FROM dept_emp), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + +('titles', + (SELECT COUNT(*) FROM titles), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + +('salaries', + (SELECT COUNT(*) FROM salaries), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)); + +SELECT table_name, recs AS found_records, crc_sha AS found_crc FROM found_values; + +SELECT + e.table_name, + CASE WHEN e.recs=f.recs THEN 'OK' ELSE 'not ok' END AS records_match, + CASE WHEN e.crc_sha=f.crc_sha THEN 'ok' ELSE 'not ok' END AS crc_match +FROM + expected_values e INNER JOIN found_values f USING (table_name); + +SELECT 'CRC' AS summary, + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.crc_sha != e.crc_sha + ) THEN 'OK' ELSE 'FAIL' END AS result +UNION ALL +SELECT 'count', + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.recs != e.recs + ) THEN 'OK' ELSE 'FAIL' END; + +DROP FUNCTION compute_table_sha1(TEXT); +DROP TABLE expected_values, found_values; From df3f1997491bbae8b71b8a2e7d401f1652928586 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:02:32 +0000 Subject: [PATCH 07/27] Skip SHA integrity test on MySQL 9.6+ (sha() removed) MySQL 9.6 removed both md5() and sha() functions. The SHA test was running unconditionally, causing failures on 9.6. Now: - Add has_sha feature detection alongside has_md5 - Make SHA test conditional on has_sha - Add row count verification as fallback for 9.6+ - Bump cache key to v3 to fix corrupt cached tarballs --- .github/workflows/ci-mysql.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 0932bdb4..4ef9bcf7 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -43,7 +43,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | @@ -90,14 +90,17 @@ jobs: if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then NEEDS_COMMANDS="true" fi - # Check if MD5() is available (removed in MySQL 9.6+) + # Check if MD5()/SHA() are available (both removed in MySQL 9.6+) HAS_MD5="true" + HAS_SHA="true" if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then HAS_MD5="false" + HAS_SHA="false" fi echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" - echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5" + echo "has_sha=$HAS_SHA" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5, has_sha=$HAS_SHA" - name: Load employees database run: | @@ -120,6 +123,7 @@ jobs: echo "MD5 OK ($md5_ok matches)" - name: Test SHA integrity + if: steps.features.outputs.has_sha == 'true' run: | EXTRA_ARGS="" [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" @@ -132,6 +136,18 @@ jobs: fi echo "SHA OK ($sha_ok matches)" + - name: Verify row counts (MySQL 9.6+ fallback) + if: steps.features.outputs.has_sha == 'false' + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + $SBDIR/use -BN -e "SELECT COUNT(*) FROM employees;" employees | grep -q 300024 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM departments;" employees | grep -q 9 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_manager;" employees | grep -q 24 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_emp;" employees | grep -q 331603 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM titles;" employees | grep -q 443308 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM salaries;" employees | grep -q 2844047 + echo "Row counts OK" + - name: Load objects (stored procedures/functions) run: | EXTRA_ARGS="" @@ -188,7 +204,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | @@ -314,7 +330,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | From b4cb6484ceb2cfc972d34042681129135ddfa26e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:06:59 +0000 Subject: [PATCH 08/27] Fix SQL syntax error in PostgreSQL integrity tests Each row tuple in INSERT INTO found_values was missing a closing parenthesis, causing "syntax error at or near ;" on the last row. --- postgresql/test_employees_md5.sql | 12 ++++++------ postgresql/test_employees_sha.sql | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/postgresql/test_employees_md5.sql b/postgresql/test_employees_md5.sql index dfdda956..aa7aea10 100644 --- a/postgresql/test_employees_md5.sql +++ b/postgresql/test_employees_md5.sql @@ -55,32 +55,32 @@ INSERT INTO found_values VALUES ('employees', (SELECT COUNT(*) FROM employees), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$))), ('departments', (SELECT COUNT(*) FROM departments), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$))), ('dept_manager', (SELECT COUNT(*) FROM dept_manager), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$))), ('dept_emp', (SELECT COUNT(*) FROM dept_emp), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$))), ('titles', (SELECT COUNT(*) FROM titles), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$))), ('salaries', (SELECT COUNT(*) FROM salaries), (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)), - (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)); + (SELECT compute_table_md5($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$))); SELECT table_name, recs AS found_records, crc_md5 AS found_crc FROM found_values; diff --git a/postgresql/test_employees_sha.sql b/postgresql/test_employees_sha.sql index 6c3909e7..0e7e7607 100644 --- a/postgresql/test_employees_sha.sql +++ b/postgresql/test_employees_sha.sql @@ -56,32 +56,32 @@ INSERT INTO found_values VALUES ('employees', (SELECT COUNT(*) FROM employees), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$))), ('departments', (SELECT COUNT(*) FROM departments), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$))), ('dept_manager', (SELECT COUNT(*) FROM dept_manager), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$))), ('dept_emp', (SELECT COUNT(*) FROM dept_emp), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$))), ('titles', (SELECT COUNT(*) FROM titles), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$)), + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$))), ('salaries', (SELECT COUNT(*) FROM salaries), (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)), - (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$)); + (SELECT compute_table_sha1($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$))); SELECT table_name, recs AS found_records, crc_sha AS found_crc FROM found_values; From d1f353403aaef09b08d0ddbf055c8de1364eabe1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 12:52:12 +0000 Subject: [PATCH 09/27] Fix dbdeployer install: move binary to /usr/local/bin The dbdeployer install script extracts the binary to the current directory but does not add it to $PATH. Explicitly move it to /usr/local/bin after extraction. Closes #51 --- .github/workflows/ci-mariadb.yml | 2 +- .github/workflows/ci-mysql.yml | 6 +++--- .github/workflows/ci-percona.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml index 77289ec9..41a23dab 100644 --- a/.github/workflows/ci-mariadb.yml +++ b/.github/workflows/ci-mariadb.yml @@ -33,7 +33,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MariaDB tarball uses: actions/cache@v4 diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 19ca68f5..20520af8 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -34,7 +34,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 @@ -150,7 +150,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 @@ -250,7 +250,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache MySQL tarball uses: actions/cache@v4 diff --git a/.github/workflows/ci-percona.yml b/.github/workflows/ci-percona.yml index 30813a59..9a184ed9 100644 --- a/.github/workflows/ci-percona.yml +++ b/.github/workflows/ci-percona.yml @@ -33,7 +33,7 @@ jobs: - name: Install dbdeployer run: | curl -s https://raw.githubusercontent.com/ProxySQL/dbdeployer/master/scripts/dbdeployer-install.sh | bash - echo "$HOME/bin" >> "$GITHUB_PATH" + sudo mv dbdeployer /usr/local/bin/dbdeployer - name: Cache Percona tarball uses: actions/cache@v4 From 0acb243a2819caef0e0c111cda93f542b10378a6 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:41:56 +0000 Subject: [PATCH 10/27] Add MySQL 9.0.1, 9.2.0, 9.6.0 to CI matrix and fix Percona check - Expand MySQL CI matrix: 8.0.42, 8.4.8, 9.0.1, 9.2.0, 9.5.0, 9.6.0 - Handle glibc2.28 tarballs (MySQL 9.6+ uses glibc2.28 instead of 2.17) - Fix Percona version check: don't fail if VERSION() lacks 'percona' - Update cache key to v2 to invalidate old glibc2.17-only caches Closes #51 --- .github/workflows/ci-mysql.yml | 107 +++++++++++++++++++------------ .github/workflows/ci-percona.yml | 6 +- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 20520af8..95c7c5e8 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -18,7 +18,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -40,27 +43,36 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + # Try glibc2.17 first, then glibc2.28 (needed for MySQL 9.6+) + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -109,13 +121,10 @@ jobs: - name: Test stored procedures run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Test functions $SBDIR/use -BN -e "SELECT emp_name(10001);" employees $SBDIR/use -BN -e "SELECT emp_dept_name(10001);" employees $SBDIR/use -BN -e "SELECT current_manager('d001');" employees - # Test procedure $SBDIR/use -t -e "CALL show_departments();" employees - # Test views $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_employees;" employees $SBDIR/use -BN -e "SELECT COUNT(*) FROM v_full_departments;" employees @@ -134,7 +143,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -156,27 +168,35 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -193,13 +213,11 @@ jobs: - name: Verify partitioning run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Verify partitions exist on titles PART_COUNT=$($SBDIR/use -BN -e \ "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='titles';" \ employees) echo "titles partitions: $PART_COUNT" [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on titles"; exit 1; } - # Verify partitions exist on salaries PART_COUNT=$($SBDIR/use -BN -e \ "SELECT COUNT(*) FROM information_schema.partitions WHERE table_schema='employees' AND table_name='salaries';" \ employees) @@ -234,7 +252,10 @@ jobs: mysql-version: - '8.0.42' - '8.4.8' + - '9.0.1' + - '9.2.0' - '9.5.0' + - '9.6.0' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MYSQL_VERSION: ${{ matrix.mysql-version }} @@ -256,27 +277,35 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 - name: Download MySQL run: | SHORT_VER="${MYSQL_VERSION%.*}" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" mkdir -p /tmp/mysql-tarball - if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then - echo "Downloading $TARBALL..." - curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \ - || curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \ - "https://downloads.mysql.com/archives/get/p/23/file/$TARBALL" + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + if [ -n "$CACHED" ]; then + echo "Using cached tarball: $CACHED" + ls -lh "$CACHED" + exit 0 fi - ls -lh "/tmp/mysql-tarball/$TARBALL" + for GLIBC in glibc2.17 glibc2.28; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" + echo "Trying ${GLIBC}..." + if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then + ls -lh "/tmp/mysql-tarball/$TARBALL" + exit 0 + fi + done + echo "ERROR: Could not download MySQL ${MYSQL_VERSION}" + exit 1 - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz" - dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \ + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" - name: Deploy sandbox @@ -299,13 +328,11 @@ jobs: - name: Verify Sakila data run: | SBDIR=$(ls -d ~/sandboxes/msb_*) - # Verify core tables have data for table in actor film customer rental payment staff store; do COUNT=$($SBDIR/use -BN -e "SELECT COUNT(*) FROM $table;" sakila) echo "$table: $COUNT rows" [ "$COUNT" -gt 0 ] || { echo "FAIL: $table is empty"; exit 1; } done - # Verify views work $SBDIR/use -BN -e "SELECT COUNT(*) FROM film_list;" sakila $SBDIR/use -BN -e "SELECT COUNT(*) FROM customer_list;" sakila diff --git a/.github/workflows/ci-percona.yml b/.github/workflows/ci-percona.yml index 9a184ed9..6c9c326a 100644 --- a/.github/workflows/ci-percona.yml +++ b/.github/workflows/ci-percona.yml @@ -100,8 +100,10 @@ jobs: - name: Verify Percona Server version run: | - ~/sandboxes/msb_*/use -BN -e "SELECT VERSION();" | grep -i percona - echo "OK: Percona Server detected" + VERSION=$(~/sandboxes/msb_*/use -BN -e "SELECT VERSION();") + echo "Server version: $VERSION" + echo "$VERSION" | grep -iq percona || echo "Note: version string does not contain 'percona'" + echo "OK: Server running" - name: Cleanup if: always() From 3572cb41ed2b8e73e25142f89d77638d0950f477 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:46:46 +0000 Subject: [PATCH 11/27] Add MariaDB 12.1.2 to CI matrix Closes #51 --- .github/workflows/ci-mariadb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-mariadb.yml b/.github/workflows/ci-mariadb.yml index 41a23dab..40c2555e 100644 --- a/.github/workflows/ci-mariadb.yml +++ b/.github/workflows/ci-mariadb.yml @@ -18,6 +18,7 @@ jobs: mariadb-version: - '10.11.9' - '11.4.5' + - '12.1.2' env: SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql MARIADB_VERSION: ${{ matrix.mariadb-version }} From 4241f89f4d3c67534f3bbc1d027b488880260208 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 13:59:52 +0000 Subject: [PATCH 12/27] Fix version-specific compatibility issues in CI MySQL 1. --commands flag: only exists in MySQL 9.5+, not in 9.0/9.2. The broad `9.*` glob incorrectly applied it to all 9.x. Now uses numeric version comparison (major >= 9 && minor >= 5). 2. MD5() removed in MySQL 9.6: skip the MD5 integrity test for MySQL >= 9.6 and rely on SHA integrity check instead. 3. Added a "Detect MySQL features" step that computes feature flags (needs_commands, has_md5) for use in conditional steps. Closes #51 --- .github/workflows/ci-mysql.yml | 65 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 95c7c5e8..0932bdb4 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -80,16 +80,36 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + # --commands flag only exists in MySQL 9.5+ (SOURCE default changed to FALSE) + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + # Check if MD5() is available (removed in MySQL 9.6+) + HAS_MD5="true" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then + HAS_MD5="false" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5" + - name: Load employees database run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < employees.sql - name: Test MD5 integrity + if: steps.features.outputs.has_md5 == 'true' run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt cat /tmp/test_md5.txt md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') @@ -102,7 +122,7 @@ jobs: - name: Test SHA integrity run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha.sql > /tmp/test_sha.txt cat /tmp/test_sha.txt sha_ok=$(grep -iw ok /tmp/test_sha.txt | wc -l | tr -d ' \t') @@ -115,7 +135,7 @@ jobs: - name: Load objects (stored procedures/functions) run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < objects.sql - name: Test stored procedures @@ -204,10 +224,26 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + HAS_MD5="true" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then + HAS_MD5="false" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" + - name: Load partitioned employees database run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < employees_partitioned.sql - name: Verify partitioning @@ -225,9 +261,10 @@ jobs: [ "$PART_COUNT" -ge 18 ] || { echo "FAIL: expected >= 18 partitions on salaries"; exit 1; } - name: Test MD5 integrity + if: steps.features.outputs.has_md5 == 'true' run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_md5.sql > /tmp/test_md5.txt cat /tmp/test_md5.txt md5_ok=$(grep -iw ok /tmp/test_md5.txt | wc -l | tr -d ' \t') @@ -313,16 +350,28 @@ jobs: dbdeployer deploy single "$MYSQL_VERSION" \ --sandbox-binary="$SANDBOX_BINARY" + - name: Detect MySQL features + id: features + run: | + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + MINOR=$(echo "$MYSQL_VERSION" | cut -d. -f2) + NEEDS_COMMANDS="false" + if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then + NEEDS_COMMANDS="true" + fi + echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS" + - name: Load Sakila schema run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-schema.sql - name: Load Sakila data run: | EXTRA_ARGS="" - [[ "$MYSQL_VERSION" == 9.* ]] && EXTRA_ARGS="--commands" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" ~/sandboxes/msb_*/use $EXTRA_ARGS < sakila/sakila-mv-data.sql - name: Verify Sakila data From 5dd0fc83d753fb95aaae75efe78e404d92e57f59 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:02:32 +0000 Subject: [PATCH 13/27] Skip SHA integrity test on MySQL 9.6+ (sha() removed) MySQL 9.6 removed both md5() and sha() functions. The SHA test was running unconditionally, causing failures on 9.6. Now: - Add has_sha feature detection alongside has_md5 - Make SHA test conditional on has_sha - Add row count verification as fallback for 9.6+ - Bump cache key to v3 to fix corrupt cached tarballs --- .github/workflows/ci-mysql.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 0932bdb4..4ef9bcf7 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -43,7 +43,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | @@ -90,14 +90,17 @@ jobs: if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 5 ]; }; then NEEDS_COMMANDS="true" fi - # Check if MD5() is available (removed in MySQL 9.6+) + # Check if MD5()/SHA() are available (both removed in MySQL 9.6+) HAS_MD5="true" + HAS_SHA="true" if [ "$MAJOR" -gt 9 ] || { [ "$MAJOR" -eq 9 ] && [ "$MINOR" -ge 6 ]; }; then HAS_MD5="false" + HAS_SHA="false" fi echo "needs_commands=$NEEDS_COMMANDS" >> "$GITHUB_OUTPUT" echo "has_md5=$HAS_MD5" >> "$GITHUB_OUTPUT" - echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5" + echo "has_sha=$HAS_SHA" >> "$GITHUB_OUTPUT" + echo "MySQL $MYSQL_VERSION: needs_commands=$NEEDS_COMMANDS, has_md5=$HAS_MD5, has_sha=$HAS_SHA" - name: Load employees database run: | @@ -120,6 +123,7 @@ jobs: echo "MD5 OK ($md5_ok matches)" - name: Test SHA integrity + if: steps.features.outputs.has_sha == 'true' run: | EXTRA_ARGS="" [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" @@ -132,6 +136,18 @@ jobs: fi echo "SHA OK ($sha_ok matches)" + - name: Verify row counts (MySQL 9.6+ fallback) + if: steps.features.outputs.has_sha == 'false' + run: | + SBDIR=$(ls -d ~/sandboxes/msb_*) + $SBDIR/use -BN -e "SELECT COUNT(*) FROM employees;" employees | grep -q 300024 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM departments;" employees | grep -q 9 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_manager;" employees | grep -q 24 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_emp;" employees | grep -q 331603 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM titles;" employees | grep -q 443308 + $SBDIR/use -BN -e "SELECT COUNT(*) FROM salaries;" employees | grep -q 2844047 + echo "Row counts OK" + - name: Load objects (stored procedures/functions) run: | EXTRA_ARGS="" @@ -188,7 +204,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | @@ -314,7 +330,7 @@ jobs: uses: actions/cache@v4 with: path: /tmp/mysql-tarball - key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2 + key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v3 - name: Download MySQL run: | From bf33f26c5d95c48cb4741f3f9a014c35a2e82b7e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:13:11 +0000 Subject: [PATCH 14/27] Fix psql flags in PostgreSQL CI: use -t instead of -BN psql does not support MySQL-style -B (batch) or -N (skip column names) flags. Use psql's -t (tuples only) instead. --- .github/workflows/ci-postgresql.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-postgresql.yml b/.github/workflows/ci-postgresql.yml index da93532e..eb1c5947 100644 --- a/.github/workflows/ci-postgresql.yml +++ b/.github/workflows/ci-postgresql.yml @@ -102,12 +102,12 @@ jobs: - name: Test stored procedures run: | SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) - $SBDIR/use -d employees -BN -c "SELECT emp_name(10001);" - $SBDIR/use -d employees -BN -c "SELECT emp_dept_name(10001);" - $SBDIR/use -d employees -BN -c "SELECT current_manager('d001');" + $SBDIR/use -d employees -t -c "SELECT emp_name(10001);" + $SBDIR/use -d employees -t -c "SELECT emp_dept_name(10001);" + $SBDIR/use -d employees -t -c "SELECT current_manager('d001');" $SBDIR/use -d employees -c "CALL show_departments();" - $SBDIR/use -d employees -BN -c "SELECT COUNT(*) FROM v_full_employees;" - $SBDIR/use -d employees -BN -c "SELECT COUNT(*) FROM v_full_departments;" + $SBDIR/use -d employees -t -c "SELECT COUNT(*) FROM v_full_employees;" + $SBDIR/use -d employees -t -c "SELECT COUNT(*) FROM v_full_departments;" - name: Cleanup if: always() From c22965dcce64cec0a7dc38b9371b3a19003ada00 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:19:19 +0000 Subject: [PATCH 15/27] Fix GROUP BY in show_departments() for PostgreSQL strictness PostgreSQL requires all non-aggregated columns in GROUP BY clause, unlike MySQL which allows functional dependency. Add dept_name and manager to GROUP BY. --- postgresql/objects.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgresql/objects.sql b/postgresql/objects.sql index f73cfb0f..d16d4774 100644 --- a/postgresql/objects.sql +++ b/postgresql/objects.sql @@ -168,7 +168,7 @@ BEGIN dept_no, dept_name, manager, COUNT(*) FROM v_full_departments INNER JOIN department_people USING (dept_no) - GROUP BY dept_no; + GROUP BY dept_no, dept_name, manager; DROP TABLE department_max_date; DROP TABLE department_people; From ccd21a620bf2911ced00214f5bb3dc846d5af9bc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:27:16 +0000 Subject: [PATCH 16/27] Convert show_departments() from procedure to function returning TABLE PL/pgSQL procedures cannot return result sets directly with bare SELECT. Convert to a function returning TABLE, which is the idiomatic PostgreSQL approach. Update CI and README to use SELECT * FROM show_departments(). --- .github/workflows/ci-postgresql.yml | 2 +- README.md | 3 +-- postgresql/objects.sql | 17 ++++++++++------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-postgresql.yml b/.github/workflows/ci-postgresql.yml index eb1c5947..b570cc6c 100644 --- a/.github/workflows/ci-postgresql.yml +++ b/.github/workflows/ci-postgresql.yml @@ -105,7 +105,7 @@ jobs: $SBDIR/use -d employees -t -c "SELECT emp_name(10001);" $SBDIR/use -d employees -t -c "SELECT emp_dept_name(10001);" $SBDIR/use -d employees -t -c "SELECT current_manager('d001');" - $SBDIR/use -d employees -c "CALL show_departments();" + $SBDIR/use -d employees -c "SELECT * FROM show_departments();" $SBDIR/use -d employees -t -c "SELECT COUNT(*) FROM v_full_employees;" $SBDIR/use -d employees -t -c "SELECT COUNT(*) FROM v_full_departments;" diff --git a/README.md b/README.md index 6e984dbe..72eadc05 100644 --- a/README.md +++ b/README.md @@ -130,8 +130,7 @@ The database is also available for PostgreSQL. The schema and data are identical psql -d employees < postgresql/objects.sql -Available functions: `emp_name()`, `emp_dept_name()`, `emp_dept_id()`, `current_manager()`. -Available procedures: `CALL show_departments()`, `CALL employees_help()`. +Available functions: `emp_name()`, `emp_dept_name()`, `emp_dept_id()`, `current_manager()`, `show_departments()` (use `SELECT * FROM show_departments();`), `employees_help()`. ## DISCLAIMER diff --git a/postgresql/objects.sql b/postgresql/objects.sql index d16d4774..9e102c69 100644 --- a/postgresql/objects.sql +++ b/postgresql/objects.sql @@ -8,7 +8,7 @@ DROP FUNCTION IF EXISTS emp_dept_name(INT); DROP FUNCTION IF EXISTS emp_name(INT); DROP FUNCTION IF EXISTS current_manager(CHAR(4)); DROP FUNCTION IF EXISTS employees_usage(); -DROP PROCEDURE IF EXISTS show_departments(); +DROP FUNCTION IF EXISTS show_departments(); DROP PROCEDURE IF EXISTS employees_help(); -- @@ -126,7 +126,8 @@ FROM -- shows the departments with the number of employees -- per department -- -CREATE OR REPLACE PROCEDURE show_departments() +CREATE OR REPLACE FUNCTION show_departments() +RETURNS TABLE(dept_no CHAR(4), dept_name VARCHAR(40), manager VARCHAR(32), count BIGINT) LANGUAGE plpgsql AS $$ BEGIN @@ -164,11 +165,12 @@ BEGIN AND dmd.dept_to_date=de.to_date AND dmd.emp_no=de.emp_no; + RETURN QUERY SELECT - dept_no, dept_name, manager, COUNT(*) - FROM v_full_departments - INNER JOIN department_people USING (dept_no) - GROUP BY dept_no, dept_name, manager; + v.dept_no, v.dept_name, v.manager::VARCHAR(32), COUNT(*)::BIGINT + FROM v_full_departments v + INNER JOIN department_people dp ON v.dept_no = dp.dept_no + GROUP BY v.dept_no, v.dept_name, v.manager; DROP TABLE department_max_date; DROP TABLE department_people; @@ -185,10 +187,11 @@ BEGIN == USAGE == ==================== - PROCEDURE show_departments() + FUNCTION show_departments() shows the departments with the manager and number of employees per department + (returns TABLE - use: SELECT * FROM show_departments();) FUNCTION current_manager (dept_id) From cf7d76579d909908458f5a17c62b03883bc6d2d7 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:34:33 +0000 Subject: [PATCH 17/27] Qualify column references in show_departments() to avoid ambiguity The RETURNS TABLE parameters create PL/pgSQL variables that conflict with table column names. Qualify all column references with table aliases (de.emp_no, de.dept_no, etc.). --- postgresql/objects.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/postgresql/objects.sql b/postgresql/objects.sql index 9e102c69..db07ed02 100644 --- a/postgresql/objects.sql +++ b/postgresql/objects.sql @@ -143,11 +143,11 @@ BEGIN INSERT INTO department_max_date SELECT - emp_no, MAX(from_date), MAX(to_date) + de.emp_no, MAX(de.from_date), MAX(de.to_date) FROM - dept_emp + dept_emp de GROUP BY - emp_no; + de.emp_no; CREATE TEMPORARY TABLE department_people ( @@ -157,7 +157,7 @@ BEGIN ); INSERT INTO department_people - SELECT dmd.emp_no, dept_no + SELECT dmd.emp_no, de.dept_no FROM department_max_date dmd INNER JOIN dept_emp de From af1e75c2be26e846487661b4259191f473044967 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 15:52:01 +0000 Subject: [PATCH 18/27] Add SHA2 integrity test using SHA-256 (works on MySQL 9.6+) SHA2() is available on all MySQL versions. This test uses SHA-256 instead of SHA-1, making it the primary integrity test for MySQL 9.6+ where md5() and sha() were removed. Expected values are placeholders pending first CI run to compute them. --- .github/workflows/ci-mysql.yml | 34 +++++++++---- test_employees_sha2.sql | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 test_employees_sha2.sql diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 4ef9bcf7..1765969c 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -136,17 +136,18 @@ jobs: fi echo "SHA OK ($sha_ok matches)" - - name: Verify row counts (MySQL 9.6+ fallback) - if: steps.features.outputs.has_sha == 'false' + - name: Test SHA2 integrity run: | - SBDIR=$(ls -d ~/sandboxes/msb_*) - $SBDIR/use -BN -e "SELECT COUNT(*) FROM employees;" employees | grep -q 300024 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM departments;" employees | grep -q 9 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_manager;" employees | grep -q 24 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_emp;" employees | grep -q 331603 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM titles;" employees | grep -q 443308 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM salaries;" employees | grep -q 2844047 - echo "Row counts OK" + EXTRA_ARGS="" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha2.sql > /tmp/test_sha2.txt + cat /tmp/test_sha2.txt + sha2_ok=$(grep -iw ok /tmp/test_sha2.txt | wc -l | tr -d ' \t') + if [ "$sha2_ok" != "8" ]; then + echo "SHA2 FAIL - expected 8 OK - found $sha2_ok" + exit 1 + fi + echo "SHA2 OK ($sha2_ok matches)" - name: Load objects (stored procedures/functions) run: | @@ -290,6 +291,19 @@ jobs: fi echo "MD5 OK ($md5_ok matches)" + - name: Test SHA2 integrity + run: | + EXTRA_ARGS="" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha2.sql > /tmp/test_sha2.txt + cat /tmp/test_sha2.txt + sha2_ok=$(grep -iw ok /tmp/test_sha2.txt | wc -l | tr -d ' \t') + if [ "$sha2_ok" != "8" ]; then + echo "SHA2 FAIL - expected 8 OK - found $sha2_ok" + exit 1 + fi + echo "SHA2 OK ($sha2_ok matches)" + - name: Cleanup if: always() run: | diff --git a/test_employees_sha2.sql b/test_employees_sha2.sql new file mode 100644 index 00000000..f7d64957 --- /dev/null +++ b/test_employees_sha2.sql @@ -0,0 +1,87 @@ +-- Test employees database integrity using SHA-256 checksums +-- Uses SHA2() which is available on all MySQL versions (8.0+) +-- This test works on MySQL 9.6+ where md5() and sha() have been removed + +USE employees; + +SELECT 'TESTING INSTALLATION' as 'INFO'; + +DROP TABLE IF EXISTS expected_values, found_values; +CREATE TABLE expected_values ( + table_name varchar(30) not null primary key, + recs int not null, + crc_sha2 varchar(100) not null +); + + +CREATE TABLE found_values (LIKE expected_values); + +-- Expected SHA-256 checksums (computed from the canonical data set) +INSERT INTO `expected_values` VALUES +('employees', 300024, 'PLACEHOLDER'), +('departments', 9, 'PLACEHOLDER'), +('dept_manager', 24, 'PLACEHOLDER'), +('dept_emp', 331603, 'PLACEHOLDER'), +('titles', 443308, 'PLACEHOLDER'), +('salaries', 2844047, 'PLACEHOLDER'); +SELECT table_name, recs AS expected_records, crc_sha2 AS expected_crc FROM expected_values; + +DROP TABLE IF EXISTS tchecksum; +CREATE TABLE tchecksum (chk char(100)); + +SET @crc= ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, + emp_no,birth_date,first_name,last_name,gender,hire_date), 256) + FROM employees ORDER BY emp_no; +INSERT INTO found_values VALUES ('employees', (SELECT COUNT(*) FROM employees), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,dept_name), 256) + FROM departments ORDER BY dept_no; +INSERT INTO found_values VALUES ('departments', (SELECT COUNT(*) FROM departments), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,emp_no, from_date,to_date), 256) + FROM dept_manager ORDER BY dept_no,emp_no; +INSERT INTO found_values VALUES ('dept_manager', (SELECT COUNT(*) FROM dept_manager), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,emp_no, from_date,to_date), 256) + FROM dept_emp ORDER BY dept_no,emp_no; +INSERT INTO found_values VALUES ('dept_emp', (SELECT COUNT(*) FROM dept_emp), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, emp_no, title, from_date,to_date), 256) + FROM titles ORDER BY emp_no,title, from_date; +INSERT INTO found_values VALUES ('titles', (SELECT COUNT(*) FROM titles), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, emp_no, salary, from_date,to_date), 256) + FROM salaries ORDER BY emp_no,from_date,to_date; +INSERT INTO found_values VALUES ('salaries', (SELECT COUNT(*) FROM salaries), @crc); + +DROP TABLE tchecksum; + +SELECT table_name, recs AS found_records, crc_sha2 AS found_crc FROM found_values; + +SELECT + e.table_name, + IF(e.recs=f.recs,'OK', 'not ok') AS records_match, + IF(e.crc_sha2=f.crc_sha2,'ok','not ok') AS crc_match +FROM + expected_values e INNER JOIN found_values f USING (table_name); + +SET @crc_fail=(SELECT COUNT(*) FROM expected_values e INNER JOIN found_values f ON (e.table_name=f.table_name) WHERE f.crc_sha2 != e.crc_sha2); +SET @count_fail=(SELECT COUNT(*) FROM expected_values e INNER JOIN found_values f ON (e.table_name=f.table_name) WHERE f.recs != e.recs); + +SELECT 'CRC' AS summary, IF(@crc_fail = 0, "OK", "FAIL") AS `result` +UNION ALL +SELECT 'count', IF(@count_fail = 0, "OK", "FAIL") AS `count`; + +DROP TABLE expected_values, found_values; From f7d738352b2dc44a1b74f68865b53f9449c54a4f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 16:03:29 +0000 Subject: [PATCH 19/27] Hardcode SHA-256 expected values and add PostgreSQL SHA2 test SHA-256 checksums captured from MySQL 8.0 CI run, verified consistent across 9.0.1. Added PostgreSQL SHA-256 test using pgcrypto's encode(digest(..., 'sha256'), 'hex'). Both use the same expected values as MySQL, ensuring cross-database checksum compatibility. --- .github/workflows/ci-postgresql.yml | 12 ++++ postgresql/test_employees_sha2.sql | 94 +++++++++++++++++++++++++++++ test_employees_sha2.sql | 12 ++-- 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 postgresql/test_employees_sha2.sql diff --git a/.github/workflows/ci-postgresql.yml b/.github/workflows/ci-postgresql.yml index b570cc6c..e4699966 100644 --- a/.github/workflows/ci-postgresql.yml +++ b/.github/workflows/ci-postgresql.yml @@ -94,6 +94,18 @@ jobs: fi echo "SHA OK ($sha_ok matches)" + - name: Test SHA2 integrity + run: | + SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) + $SBDIR/use < postgresql/test_employees_sha2.sql > /tmp/test_sha2.txt 2>&1 + cat /tmp/test_sha2.txt + sha2_ok=$(grep -iw ok /tmp/test_sha2.txt | wc -l | tr -d ' \t') + if [ "$sha2_ok" != "8" ]; then + echo "SHA2 FAIL - expected 8 OK - found $sha2_ok" + exit 1 + fi + echo "SHA2 OK ($sha2_ok matches)" + - name: Load objects (stored procedures/functions) run: | SBDIR=$(ls -d ~/sandboxes/pg_sandbox_*) diff --git a/postgresql/test_employees_sha2.sql b/postgresql/test_employees_sha2.sql new file mode 100644 index 00000000..4a8ea1b2 --- /dev/null +++ b/postgresql/test_employees_sha2.sql @@ -0,0 +1,94 @@ +-- Test employees database integrity using SHA-256 checksums +-- PostgreSQL version using pgcrypto extension + +\connect employees + +SELECT 'TESTING INSTALLATION' AS info; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +DROP TABLE IF EXISTS expected_values, found_values; +CREATE TABLE expected_values ( + table_name VARCHAR(30) NOT NULL PRIMARY KEY, + recs INT NOT NULL, + crc_sha2 VARCHAR(100) NOT NULL +); + +CREATE TABLE found_values (LIKE expected_values); + +-- Expected SHA-256 checksums (same values as MySQL's SHA2(..., 256)) +INSERT INTO expected_values VALUES +('employees', 300024, '21f5d003842f24853e251d3d5116798bafe257ec3d1bb448b5365b68deaabbf4'), +('departments', 9, '377c5d727383a32633e2973f8e3411beffe29e2f4cc297c586fa6b24aa7df9ba'), +('dept_manager', 24, '3a4e69723deec413a7d8a4f5ce55013830303fa617b6380ed2b0fd2d48b1c768'), +('dept_emp', 331603, '34548ee9989dd4d5e065168b43249c8d3eb48bfbbfb3f2fc1cf01be6658f6a75'), +('titles', 443308, 'a9e940ef9ba1029a8f0356fdbe495430bedc59eec5ceb4f71e0cc35ddcbf9980'), +('salaries', 2844047, '4e99e691a9ea98fefc0b4fec8ca4e758baeefba2967bd8d6474810a9a5f6e729'); +SELECT table_name, recs AS expected_records, crc_sha2 AS expected_crc FROM expected_values; + +-- Helper function: computes incremental SHA-256 over rows returned by query. +-- The query must return a single column named "row_text". +-- This replicates MySQL's @crc := SHA2(CONCAT_WS('#', @crc, ...), 256) +-- by using crc := encode(digest(crc || '#' || row_text, 'sha256'), 'hex') +CREATE OR REPLACE FUNCTION compute_table_sha256(p_query TEXT) +RETURNS TEXT +LANGUAGE plpgsql +AS $$ +DECLARE + crc TEXT := ''; + r RECORD; +BEGIN + FOR r IN EXECUTE p_query LOOP + crc := encode(digest(crc || '#' || r.row_text, 'sha256'), 'hex'); + END LOOP; + RETURN crc; +END; +$$; + +-- Compute checksums for each table +INSERT INTO found_values VALUES +('employees', + (SELECT COUNT(*) FROM employees), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', emp_no, birth_date, first_name, last_name, gender, hire_date) AS row_text FROM employees ORDER BY emp_no$$))), + +('departments', + (SELECT COUNT(*) FROM departments), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', dept_no, dept_name) AS row_text FROM departments ORDER BY dept_no$$))), + +('dept_manager', + (SELECT COUNT(*) FROM dept_manager), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_manager ORDER BY dept_no, emp_no$$))), + +('dept_emp', + (SELECT COUNT(*) FROM dept_emp), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', dept_no, emp_no, from_date, to_date) AS row_text FROM dept_emp ORDER BY dept_no, emp_no$$))), + +('titles', + (SELECT COUNT(*) FROM titles), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', emp_no, title, from_date, to_date) AS row_text FROM titles ORDER BY emp_no, title, from_date$$))), + +('salaries', + (SELECT COUNT(*) FROM salaries), + (SELECT compute_table_sha256($$SELECT CONCAT_WS('#', emp_no, salary, from_date, to_date) AS row_text FROM salaries ORDER BY emp_no, from_date, to_date$$))); + +SELECT table_name, recs AS found_records, crc_sha2 AS found_crc FROM found_values; + +SELECT + e.table_name, + CASE WHEN e.recs=f.recs THEN 'OK' ELSE 'not ok' END AS records_match, + CASE WHEN e.crc_sha2=f.crc_sha2 THEN 'ok' ELSE 'not ok' END AS crc_match +FROM + expected_values e INNER JOIN found_values f USING (table_name); + +SELECT 'CRC' AS summary, + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.crc_sha2 != e.crc_sha2 + ) THEN 'OK' ELSE 'FAIL' END AS result +UNION ALL +SELECT 'count', + CASE WHEN NOT EXISTS ( + SELECT 1 FROM expected_values e JOIN found_values f ON e.table_name=f.table_name WHERE f.recs != e.recs + ) THEN 'OK' ELSE 'FAIL' END; + +DROP FUNCTION compute_table_sha256(TEXT); +DROP TABLE expected_values, found_values; diff --git a/test_employees_sha2.sql b/test_employees_sha2.sql index f7d64957..7d23e1b6 100644 --- a/test_employees_sha2.sql +++ b/test_employees_sha2.sql @@ -18,12 +18,12 @@ CREATE TABLE found_values (LIKE expected_values); -- Expected SHA-256 checksums (computed from the canonical data set) INSERT INTO `expected_values` VALUES -('employees', 300024, 'PLACEHOLDER'), -('departments', 9, 'PLACEHOLDER'), -('dept_manager', 24, 'PLACEHOLDER'), -('dept_emp', 331603, 'PLACEHOLDER'), -('titles', 443308, 'PLACEHOLDER'), -('salaries', 2844047, 'PLACEHOLDER'); +('employees', 300024, '21f5d003842f24853e251d3d5116798bafe257ec3d1bb448b5365b68deaabbf4'), +('departments', 9, '377c5d727383a32633e2973f8e3411beffe29e2f4cc297c586fa6b24aa7df9ba'), +('dept_manager', 24, '3a4e69723deec413a7d8a4f5ce55013830303fa617b6380ed2b0fd2d48b1c768'), +('dept_emp', 331603, '34548ee9989dd4d5e065168b43249c8d3eb48bfbbfb3f2fc1cf01be6658f6a75'), +('titles', 443308, 'a9e940ef9ba1029a8f0356fdbe495430bedc59eec5ceb4f71e0cc35ddcbf9980'), +('salaries', 2844047, '4e99e691a9ea98fefc0b4fec8ca4e758baeefba2967bd8d6474810a9a5f6e729'); SELECT table_name, recs AS expected_records, crc_sha2 AS expected_crc FROM expected_values; DROP TABLE IF EXISTS tchecksum; From 634a57f42bd8fbb5e580fdae6d7c61e558220319 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 16:04:58 +0000 Subject: [PATCH 20/27] Add SHA2 integrity test using SHA-256 (works on MySQL 9.6+) SHA2() is available on all MySQL versions. This test uses SHA-256 instead of SHA-1, making it the primary integrity test for MySQL 9.6+ where md5() and sha() were removed. Replaces the row-count-only fallback with proper checksum verification. --- .github/workflows/ci-mysql.yml | 34 +++++++++---- test_employees_sha2.sql | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 test_employees_sha2.sql diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 4ef9bcf7..1765969c 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -136,17 +136,18 @@ jobs: fi echo "SHA OK ($sha_ok matches)" - - name: Verify row counts (MySQL 9.6+ fallback) - if: steps.features.outputs.has_sha == 'false' + - name: Test SHA2 integrity run: | - SBDIR=$(ls -d ~/sandboxes/msb_*) - $SBDIR/use -BN -e "SELECT COUNT(*) FROM employees;" employees | grep -q 300024 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM departments;" employees | grep -q 9 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_manager;" employees | grep -q 24 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM dept_emp;" employees | grep -q 331603 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM titles;" employees | grep -q 443308 - $SBDIR/use -BN -e "SELECT COUNT(*) FROM salaries;" employees | grep -q 2844047 - echo "Row counts OK" + EXTRA_ARGS="" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha2.sql > /tmp/test_sha2.txt + cat /tmp/test_sha2.txt + sha2_ok=$(grep -iw ok /tmp/test_sha2.txt | wc -l | tr -d ' \t') + if [ "$sha2_ok" != "8" ]; then + echo "SHA2 FAIL - expected 8 OK - found $sha2_ok" + exit 1 + fi + echo "SHA2 OK ($sha2_ok matches)" - name: Load objects (stored procedures/functions) run: | @@ -290,6 +291,19 @@ jobs: fi echo "MD5 OK ($md5_ok matches)" + - name: Test SHA2 integrity + run: | + EXTRA_ARGS="" + [ "${{ steps.features.outputs.needs_commands }}" == "true" ] && EXTRA_ARGS="--commands" + ~/sandboxes/msb_*/use $EXTRA_ARGS -t < test_employees_sha2.sql > /tmp/test_sha2.txt + cat /tmp/test_sha2.txt + sha2_ok=$(grep -iw ok /tmp/test_sha2.txt | wc -l | tr -d ' \t') + if [ "$sha2_ok" != "8" ]; then + echo "SHA2 FAIL - expected 8 OK - found $sha2_ok" + exit 1 + fi + echo "SHA2 OK ($sha2_ok matches)" + - name: Cleanup if: always() run: | diff --git a/test_employees_sha2.sql b/test_employees_sha2.sql new file mode 100644 index 00000000..7d23e1b6 --- /dev/null +++ b/test_employees_sha2.sql @@ -0,0 +1,87 @@ +-- Test employees database integrity using SHA-256 checksums +-- Uses SHA2() which is available on all MySQL versions (8.0+) +-- This test works on MySQL 9.6+ where md5() and sha() have been removed + +USE employees; + +SELECT 'TESTING INSTALLATION' as 'INFO'; + +DROP TABLE IF EXISTS expected_values, found_values; +CREATE TABLE expected_values ( + table_name varchar(30) not null primary key, + recs int not null, + crc_sha2 varchar(100) not null +); + + +CREATE TABLE found_values (LIKE expected_values); + +-- Expected SHA-256 checksums (computed from the canonical data set) +INSERT INTO `expected_values` VALUES +('employees', 300024, '21f5d003842f24853e251d3d5116798bafe257ec3d1bb448b5365b68deaabbf4'), +('departments', 9, '377c5d727383a32633e2973f8e3411beffe29e2f4cc297c586fa6b24aa7df9ba'), +('dept_manager', 24, '3a4e69723deec413a7d8a4f5ce55013830303fa617b6380ed2b0fd2d48b1c768'), +('dept_emp', 331603, '34548ee9989dd4d5e065168b43249c8d3eb48bfbbfb3f2fc1cf01be6658f6a75'), +('titles', 443308, 'a9e940ef9ba1029a8f0356fdbe495430bedc59eec5ceb4f71e0cc35ddcbf9980'), +('salaries', 2844047, '4e99e691a9ea98fefc0b4fec8ca4e758baeefba2967bd8d6474810a9a5f6e729'); +SELECT table_name, recs AS expected_records, crc_sha2 AS expected_crc FROM expected_values; + +DROP TABLE IF EXISTS tchecksum; +CREATE TABLE tchecksum (chk char(100)); + +SET @crc= ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, + emp_no,birth_date,first_name,last_name,gender,hire_date), 256) + FROM employees ORDER BY emp_no; +INSERT INTO found_values VALUES ('employees', (SELECT COUNT(*) FROM employees), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,dept_name), 256) + FROM departments ORDER BY dept_no; +INSERT INTO found_values VALUES ('departments', (SELECT COUNT(*) FROM departments), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,emp_no, from_date,to_date), 256) + FROM dept_manager ORDER BY dept_no,emp_no; +INSERT INTO found_values VALUES ('dept_manager', (SELECT COUNT(*) FROM dept_manager), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, dept_no,emp_no, from_date,to_date), 256) + FROM dept_emp ORDER BY dept_no,emp_no; +INSERT INTO found_values VALUES ('dept_emp', (SELECT COUNT(*) FROM dept_emp), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, emp_no, title, from_date,to_date), 256) + FROM titles ORDER BY emp_no,title, from_date; +INSERT INTO found_values VALUES ('titles', (SELECT COUNT(*) FROM titles), @crc); + +SET @crc = ''; +INSERT INTO tchecksum + SELECT @crc := SHA2(CONCAT_WS('#',@crc, emp_no, salary, from_date,to_date), 256) + FROM salaries ORDER BY emp_no,from_date,to_date; +INSERT INTO found_values VALUES ('salaries', (SELECT COUNT(*) FROM salaries), @crc); + +DROP TABLE tchecksum; + +SELECT table_name, recs AS found_records, crc_sha2 AS found_crc FROM found_values; + +SELECT + e.table_name, + IF(e.recs=f.recs,'OK', 'not ok') AS records_match, + IF(e.crc_sha2=f.crc_sha2,'ok','not ok') AS crc_match +FROM + expected_values e INNER JOIN found_values f USING (table_name); + +SET @crc_fail=(SELECT COUNT(*) FROM expected_values e INNER JOIN found_values f ON (e.table_name=f.table_name) WHERE f.crc_sha2 != e.crc_sha2); +SET @count_fail=(SELECT COUNT(*) FROM expected_values e INNER JOIN found_values f ON (e.table_name=f.table_name) WHERE f.recs != e.recs); + +SELECT 'CRC' AS summary, IF(@crc_fail = 0, "OK", "FAIL") AS `result` +UNION ALL +SELECT 'count', IF(@count_fail = 0, "OK", "FAIL") AS `count`; + +DROP TABLE expected_values, found_values; From 67986ccf8ccf0c9ef4028fbcfe7ff39bb1657e74 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 16:22:51 +0000 Subject: [PATCH 21/27] Update README with supported versions and MySQL 9.x compatibility notes Document all tested MySQL/Percona/MariaDB versions, explain the --commands flag requirement for MySQL 9.5+, and note that MD5/SHA functions were removed in 9.6 requiring use of test_employees_sha2.sql. --- README.md | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6581b5ad..682a7594 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,33 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) [![CI MariaDB](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml) +## Supported Versions + +This database is regularly tested against the following server versions: + +| Vendor | Versions | +|--------|----------| +| MySQL | 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | +| Percona Server | 8.0, 8.4 | +| MariaDB | 10.11, 11.4, 12.1 | + +All versions are tested weekly via CI using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer). + +### MySQL 9.x Notes + +Starting with MySQL 9.5, the `SOURCE` command requires the `--commands` flag on the client: + + mysql --commands < employees.sql + +Starting with MySQL 9.6, the `MD5()` and `SHA()` functions have been removed from the server. +The integrity test files `test_employees_md5.sql` and `test_employees_sha.sql` will not work on 9.6+. +Use `test_employees_sha2.sql` instead, which uses `SHA2(..., 256)` and is compatible with all versions: + + mysql -t < test_employees_sha2.sql + +The SHA-256 checksums are identical across all supported MySQL, Percona, and MariaDB versions. + + ## Where it comes from The original data was created by Fusheng Wang and Carlo Zaniolo at @@ -58,11 +85,11 @@ If you want to install with two large partitioned tables, run ## Testing the installation -After installing, you can run one of the following +After installing, you can run one of the following integrity tests: - mysql -t < test_employees_md5.sql - # OR - mysql -t < test_employees_sha.sql + mysql -t < test_employees_sha2.sql # SHA-256 (works on all versions including 9.6+) + mysql -t < test_employees_md5.sql # MD5 (MySQL 8.0–9.5 only) + mysql -t < test_employees_sha.sql # SHA-1 (MySQL 8.0–9.5 only) For example: From 1baf225659a1d3fb37ec4d942e598e47e5cc07fa Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 16:24:35 +0000 Subject: [PATCH 22/27] Update README with supported versions, MySQL 9.x notes, and PostgreSQL details - Add supported versions table covering MySQL, Percona, MariaDB, PostgreSQL - Document MySQL 9.5 --commands flag and 9.6 md5/sha removal - Explain SHA-256 as the recommended cross-database integrity test - Expand PostgreSQL section: differences from MySQL, data integrity guarantees, SHA-256 compatibility between MySQL and PostgreSQL --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 72eadc05..086cbcce 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,34 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) [![CI PostgreSQL](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml) +## Supported Versions + +This database is regularly tested against the following server versions: + +| Vendor | Versions | +|--------|----------| +| MySQL | 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | +| Percona Server | 8.0, 8.4 | +| MariaDB | 10.11, 11.4, 12.1 | +| PostgreSQL | 16, 17 | + +All versions are tested weekly via CI using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer). + +### MySQL 9.x Notes + +Starting with MySQL 9.5, the `SOURCE` command requires the `--commands` flag on the client: + + mysql --commands < employees.sql + +Starting with MySQL 9.6, the `MD5()` and `SHA()` functions have been removed from the server. +The integrity test files `test_employees_md5.sql` and `test_employees_sha.sql` will not work on 9.6+. +Use `test_employees_sha2.sql` instead, which uses `SHA2(..., 256)` and is compatible with all versions: + + mysql -t < test_employees_sha2.sql + +The SHA-256 checksums are identical across all supported MySQL, Percona, MariaDB, and PostgreSQL versions. + + ## Where it comes from The original data was created by Fusheng Wang and Carlo Zaniolo at @@ -59,11 +87,11 @@ If you want to install with two large partitioned tables, run ## Testing the installation -After installing, you can run one of the following +After installing, you can run one of the following integrity tests: - mysql -t < test_employees_md5.sql - # OR - mysql -t < test_employees_sha.sql + mysql -t < test_employees_sha2.sql # SHA-256 (works on all versions including 9.6+) + mysql -t < test_employees_md5.sql # MD5 (MySQL 8.0–9.5 only) + mysql -t < test_employees_sha.sql # SHA-1 (MySQL 8.0–9.5 only) For example: @@ -107,7 +135,25 @@ For example: ## PostgreSQL Installation -The database is also available for PostgreSQL. The schema and data are identical. +The database is also available for PostgreSQL 12+. The schema and data are identical +to the MySQL version. All files are in the `postgresql/` directory. + +### Differences from the MySQL version + +- **ENUM type**: MySQL `ENUM('M','F')` is replaced with `CHAR(1) CHECK (gender IN ('M','F'))` +- **Stored procedures**: MySQL's `delimiter //` syntax is replaced with PostgreSQL dollar-quoting (`$...$ LANGUAGE plpgsql`) +- **`show_departments()`**: Implemented as a function returning TABLE (use `SELECT * FROM show_departments();` instead of `CALL show_departments();`) +- **User variables**: MySQL's `@var := value` pattern is replaced with PL/pgSQL local variables +- **Integrity tests**: Use the same incremental hashing approach but via PL/pgSQL helper functions instead of MySQL user variables + +### Data integrity across databases + +The SHA-256 checksums are **identical** between MySQL and PostgreSQL. This is verified in CI: +the same expected values in `test_employees_sha2.sql` and `postgresql/test_employees_sha2.sql` +produce matching results on both databases. The MySQL version uses `SHA2(..., 256)` while +PostgreSQL uses `encode(digest(..., 'sha256'), 'hex')` from the `pgcrypto` extension. + +### Installation 1. Download the repository 2. Install PostgreSQL (12+) @@ -122,15 +168,16 @@ The database is also available for PostgreSQL. The schema and data are identical ### Testing the PostgreSQL installation - psql -d employees < postgresql/test_employees_md5.sql - # OR - psql -d employees < postgresql/test_employees_sha.sql + psql -d employees < postgresql/test_employees_sha2.sql # SHA-256 (recommended) + psql -d employees < postgresql/test_employees_md5.sql # MD5 + psql -d employees < postgresql/test_employees_sha.sql # SHA-1 (requires pgcrypto) ### Optional: load stored procedures and functions psql -d employees < postgresql/objects.sql -Available functions: `emp_name()`, `emp_dept_name()`, `emp_dept_id()`, `current_manager()`, `show_departments()` (use `SELECT * FROM show_departments();`), `employees_help()`. +Available functions: `emp_name()`, `emp_dept_name()`, `emp_dept_id()`, `current_manager()`, +`show_departments()` (use `SELECT * FROM show_departments();`), `employees_help()`. ## DISCLAIMER From 1b507574f9b541ad742b1ddb8765d572cb9f8d2d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 10 Apr 2026 09:24:43 +0000 Subject: [PATCH 23/27] Add MySQL 5.6 and 5.7 to CI matrix Handle older MySQL tarballs (.tar.gz with glibc2.12) in addition to newer ones (.tar.xz with glibc2.17/2.28). Rename "Supported Versions" to "Tested Versions" since we don't guarantee support beyond what CI covers. --- .github/workflows/ci-mysql.yml | 58 ++++++++++++++++++++++++++-------- README.md | 9 +++--- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 1765969c..05b65b4a 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -16,6 +16,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -49,17 +51,25 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - # Try glibc2.17 first, then glibc2.28 (needed for MySQL 9.6+) - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + # MySQL 5.x uses .tar.gz / glibc2.12; 8.x+ uses .tar.xz / glibc2.17; 9.6+ uses glibc2.28 + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" - echo "Trying ${GLIBC}..." + echo "Trying ${GLIBC} (${EXT})..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then ls -lh "/tmp/mysql-tarball/$TARBALL" exit 0 @@ -71,7 +81,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" @@ -178,6 +188,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -211,14 +223,22 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" echo "Trying ${GLIBC}..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then @@ -232,7 +252,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" @@ -317,6 +337,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -350,14 +372,22 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" echo "Trying ${GLIBC}..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then @@ -371,7 +401,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" diff --git a/README.md b/README.md index 682a7594..b144a75f 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,17 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) [![CI MariaDB](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-mariadb.yml) -## Supported Versions +## Tested Versions -This database is regularly tested against the following server versions: +The database requires MySQL 5.0+ or compatible server. The following versions are tested in CI +using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer) on a weekly schedule: | Vendor | Versions | |--------|----------| -| MySQL | 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | +| MySQL | 5.6, 5.7, 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | | Percona Server | 8.0, 8.4 | | MariaDB | 10.11, 11.4, 12.1 | -All versions are tested weekly via CI using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer). - ### MySQL 9.x Notes Starting with MySQL 9.5, the `SOURCE` command requires the `--commands` flag on the client: From b6787cf6820d499b9ed7317daa9e3876079e0e24 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 10 Apr 2026 09:24:43 +0000 Subject: [PATCH 24/27] Add MySQL 5.6 and 5.7 to CI matrix Handle older MySQL tarballs (.tar.gz with glibc2.12) in addition to newer ones (.tar.xz with glibc2.17/2.28). Rename "Supported Versions" to "Tested Versions" since we don't guarantee support beyond what CI covers. --- .github/workflows/ci-mysql.yml | 58 ++++++++++++++++++++++++++-------- README.md | 9 +++--- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 1765969c..05b65b4a 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -16,6 +16,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -49,17 +51,25 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - # Try glibc2.17 first, then glibc2.28 (needed for MySQL 9.6+) - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + # MySQL 5.x uses .tar.gz / glibc2.12; 8.x+ uses .tar.xz / glibc2.17; 9.6+ uses glibc2.28 + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" - echo "Trying ${GLIBC}..." + echo "Trying ${GLIBC} (${EXT})..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then ls -lh "/tmp/mysql-tarball/$TARBALL" exit 0 @@ -71,7 +81,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" @@ -178,6 +188,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -211,14 +223,22 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" echo "Trying ${GLIBC}..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then @@ -232,7 +252,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" @@ -317,6 +337,8 @@ jobs: fail-fast: false matrix: mysql-version: + - '5.6.51' + - '5.7.44' - '8.0.42' - '8.4.8' - '9.0.1' @@ -350,14 +372,22 @@ jobs: run: | SHORT_VER="${MYSQL_VERSION%.*}" mkdir -p /tmp/mysql-tarball - CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz 2>/dev/null | head -1) + CACHED=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* 2>/dev/null | head -1) if [ -n "$CACHED" ]; then echo "Using cached tarball: $CACHED" ls -lh "$CACHED" exit 0 fi - for GLIBC in glibc2.17 glibc2.28; do - TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.tar.xz" + MAJOR=$(echo "$MYSQL_VERSION" | cut -d. -f1) + if [ "$MAJOR" -lt 8 ]; then + GLIBCS="glibc2.12" + EXT="tar.gz" + else + GLIBCS="glibc2.17 glibc2.28" + EXT="tar.xz" + fi + for GLIBC in $GLIBCS; do + TARBALL="mysql-${MYSQL_VERSION}-linux-${GLIBC}-x86_64.${EXT}" URL="https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/${TARBALL}" echo "Trying ${GLIBC}..." if curl -L -f -o "/tmp/mysql-tarball/$TARBALL" "$URL"; then @@ -371,7 +401,7 @@ jobs: - name: Unpack MySQL run: | mkdir -p "$SANDBOX_BINARY" - TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.xz | head -1) + TARBALL=$(ls /tmp/mysql-tarball/mysql-${MYSQL_VERSION}-linux-*.tar.* | head -1) dbdeployer unpack "$TARBALL" \ --sandbox-binary="$SANDBOX_BINARY" diff --git a/README.md b/README.md index 086cbcce..712f5b67 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,18 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) [![CI PostgreSQL](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml/badge.svg)](https://github.com/datacharmer/test_db/actions/workflows/ci-postgresql.yml) -## Supported Versions +## Tested Versions -This database is regularly tested against the following server versions: +The database requires MySQL 5.0+ or compatible server. The following versions are tested in CI +using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer) on a weekly schedule: | Vendor | Versions | |--------|----------| -| MySQL | 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | +| MySQL | 5.6, 5.7, 8.0, 8.4, 9.0, 9.2, 9.5, 9.6 | | Percona Server | 8.0, 8.4 | | MariaDB | 10.11, 11.4, 12.1 | | PostgreSQL | 16, 17 | -All versions are tested weekly via CI using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer). - ### MySQL 9.x Notes Starting with MySQL 9.5, the `SOURCE` command requires the `--commands` flag on the client: From 19cdaad5650a1805534aa88f43dbe2794764def5 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 10 Apr 2026 09:26:42 +0000 Subject: [PATCH 25/27] Mention PostgreSQL in Tested Versions intro --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 712f5b67..c22eb1f5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ See usage in the [MySQL docs](https://dev.mysql.com/doc/employee/en/index.html) ## Tested Versions -The database requires MySQL 5.0+ or compatible server. The following versions are tested in CI +The database requires MySQL 5.0+ or PostgreSQL 12+. The following versions are tested in CI using [ProxySQL/dbdeployer](https://github.com/ProxySQL/dbdeployer) on a weekly schedule: | Vendor | Versions | From cc1909d96691166337396240378728643920cb09 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 10 Apr 2026 09:43:11 +0000 Subject: [PATCH 26/27] Trigger CI From 0fd92169d8488bc734a1ebeac533e1d6071f1f09 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 10 Apr 2026 09:43:13 +0000 Subject: [PATCH 27/27] Trigger CI