From 0342de211fea2ba604cc5e235ce193dd2497770a Mon Sep 17 00:00:00 2001 From: Vipul Vaibhaw Date: Thu, 16 Nov 2017 13:21:39 +0530 Subject: [PATCH 01/36] Update README.md editing mysql < employees.sql as code --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eaf21f7b..dafd1829 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ untouched, and use these issues as data cleaning exercises. 1. Download the repository 2. Change directory to the repository -3. run + +Then run mysql < employees.sql From 1026501c503626e8c69a602e2d76d59f09ab5f05 Mon Sep 17 00:00:00 2001 From: Taylor Edwards <37377759+tedwa5@users.noreply.github.com> Date: Thu, 5 Apr 2018 18:02:27 -0600 Subject: [PATCH 02/36] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dafd1829..76f5f018 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ For example: ## DISCLAIMER -To the best of my knowledge, this data is fabricated, and +To the best of my knowledge, this data is fabricated and it does not correspond to real people. Any similarity to existing people is purely coincidental. From 0b66c2338736779e3b150c7d125b1012d95a961f Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Tue, 9 Apr 2019 21:13:53 +0200 Subject: [PATCH 03/36] Update README.md with user prerequisites --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 76f5f018..bb89fc22 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,16 @@ The data was generated, and as such there are inconsistencies and subtle problems. Rather than removing them, we decided to leave the contents untouched, and use these issues as data cleaning exercises. +## Prerequisites + +You need a MySQL database server (5.0+) and run the commands below through a +user that has the following privileges: + + SELECT, INSERT, UPDATE, DELETE, + CREATE, DROP, RELOAD, REFERENCES, + INDEX, ALTER, SHOW DATABASES, + CREATE TEMPORARY TABLES, + LOCK TABLES, EXECUTE, CREATE VIEW ## Installation: From f5748c4fc890b7d429b251eb66226b20afaca1e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Fri, 4 Sep 2020 14:39:53 +0200 Subject: [PATCH 04/36] Remove hard-coded engine in MD5/SHA tests --- test_employees_md5.sql | 4 +--- test_employees_sha.sql | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/test_employees_md5.sql b/test_employees_md5.sql index 04c72c26..c3379baa 100644 --- a/test_employees_md5.sql +++ b/test_employees_md5.sql @@ -32,10 +32,8 @@ CREATE TABLE expected_values ( recs int not null, crc_sha varchar(100) not null, crc_md5 varchar(100) not null -) ENGINE=MyISAM; +); --- In MySQL 5.0, the creation and update time for memory tables is not recorded -/*!50130 ALTER TABLE expected_values engine=memory */; CREATE TABLE found_values LIKE expected_values; diff --git a/test_employees_sha.sql b/test_employees_sha.sql index 77927a7a..3664a2bd 100644 --- a/test_employees_sha.sql +++ b/test_employees_sha.sql @@ -32,10 +32,8 @@ CREATE TABLE expected_values ( recs int not null, crc_sha varchar(100) not null, crc_md5 varchar(100) not null -) ENGINE=MyISAM; +); --- In MySQL 5.0, the creation and update time for memory tables is not recorded -/*!50130 ALTER TABLE expected_values engine=memory */; CREATE TABLE found_values LIKE expected_values; From 00623f334886bd417e8a8c242b32f3691098d853 Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Fri, 4 Sep 2020 14:40:37 +0200 Subject: [PATCH 05/36] Add compatibility test --- test_versions.sh | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 test_versions.sh diff --git a/test_versions.sh b/test_versions.sh new file mode 100755 index 00000000..12235f3a --- /dev/null +++ b/test_versions.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# Tests that the employees database can work with all versions of MySQL +# +# Requires dbdeployer to be installed and configured (https://www.dbdeployer.com) + +function found_in_path { + name=$1 + for dir in $(echo $PATH | tr ':' ' ') + do + if [ -x $dir/$name ] + then + echo $dir/$name + return + fi + done +} + + +function check_exit_code { + exit_code=$? + if [ "$exit_code" != "0" ] + then + echo "Execution error" + exit $exit_code + fi +} + +dbdeployer=$(found_in_path dbdeployer) + +if [ -z "$dbdeployer" ] +then + echo "dbdeployer not found in \$PATH" + exit 1 +fi + +[ -z "$SANDBOX_HOME" ] && SANDBOX_HOME=$HOME/sandboxes + +for short_version in 5.0 5.1 5.5 5.6 5.7 8.0 +do + version=$($dbdeployer info version $short_version) + if [ -z "$version" ] + then + continue + fi + echo "### -------------------" + echo "### $version" + echo "### -------------------" + ver_name=$(echo $version | tr '.' '_') + + $dbdeployer deploy single $version + check_exit_code + + msb=$SANDBOX_HOME/msb_$ver_name + + if [ ! -d $msb ] + then + echo "'$msb' not found or not a directory - Halting test" + exit 1 + fi + + $msb/use < employees.sql + check_exit_code + echo "Testing MD5" + $msb/use -t < test_employees_md5.sql > /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 OK - $md5_ok" + else + echo "MD5 FAIL - expected 8 - found $md5_ok" + cat /tmp/test_md5.txt + exit 1 + fi + + echo "Testing SHA" + $msb/use -t < test_employees_sha.sql > /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 OK - $sha_ok" + else + echo "SHA FAIL - expected 8 - found $sha_ok" + cat /tmp/test_sha.txt + exit 1 + fi + + $dbdeployer delete msb_$ver_name + check_exit_code + +done + From e5f310ac7786a2a181a7fc124973725d7aa4ce7c Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Mon, 7 Sep 2020 06:24:50 +0200 Subject: [PATCH 06/36] Remove blackhole engine from tests --- test_employees_md5.sql | 2 +- test_employees_sha.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_employees_md5.sql b/test_employees_md5.sql index c3379baa..bf0e2338 100644 --- a/test_employees_md5.sql +++ b/test_employees_md5.sql @@ -53,7 +53,7 @@ INSERT INTO `expected_values` VALUES SELECT table_name, recs AS expected_records, crc_md5 AS expected_crc FROM expected_values; DROP TABLE IF EXISTS tchecksum; -CREATE TABLE tchecksum (chk char(100)) ENGINE=blackhole; +CREATE TABLE tchecksum (chk char(100)); SET @crc= ''; diff --git a/test_employees_sha.sql b/test_employees_sha.sql index 3664a2bd..793ace98 100644 --- a/test_employees_sha.sql +++ b/test_employees_sha.sql @@ -53,7 +53,7 @@ INSERT INTO `expected_values` VALUES SELECT table_name, recs AS expected_records, crc_sha AS expected_crc FROM expected_values; DROP TABLE IF EXISTS tchecksum; -CREATE TABLE tchecksum (chk char(100)) ENGINE=blackhole; +CREATE TABLE tchecksum (chk char(100)); SET @crc= ''; From d9039f4a17862b529bb6c4b8e8000f0f6a871b32 Mon Sep 17 00:00:00 2001 From: Corrigible <105511262+Corrigible@users.noreply.github.com> Date: Sat, 14 May 2022 19:45:31 +0530 Subject: [PATCH 07/36] Update objects.sql --- objects.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objects.sql b/objects.sql index 06cb884c..887d8c9e 100644 --- a/objects.sql +++ b/objects.sql @@ -3,7 +3,7 @@ use employees; delimiter // drop function if exists emp_dept_id // drop function if exists emp_dept_name // -drop function if exists emp_name // +drop function if exists emp_name /// drop function if exists current_manager // drop procedure if exists show_departments // From 3c7fa05e04b4c339d91a43b7029a210212d48e6c Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Sat, 26 Aug 2023 20:46:51 +0200 Subject: [PATCH 08/36] Update objects.sql --- objects.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objects.sql b/objects.sql index 887d8c9e..06cb884c 100644 --- a/objects.sql +++ b/objects.sql @@ -3,7 +3,7 @@ use employees; delimiter // drop function if exists emp_dept_id // drop function if exists emp_dept_name // -drop function if exists emp_name /// +drop function if exists emp_name // drop function if exists current_manager // drop procedure if exists show_departments // From 97cc68a936c2907d8526627fa29af71794982d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Descamps?= Date: Fri, 13 Feb 2026 08:54:22 +0100 Subject: [PATCH 09/36] Update README with MySQL client command note Added important note about MySQL client commands. fixing issue #47 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bb89fc22..d6b59b4c 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ user that has the following privileges: 1. Download the repository 2. Change directory to the repository +> [!IMPORTANT] +> If you are using MySQL client >= 9.x, the new default value of the [`--commands`](https://dev.mysql.com/doc/refman/9.6/en/mysql-command-options.html#option_mysql_commands) parameter is set to `FALSE`. Therefore, the `SOURCE` command won't work. You need to happen `--commands` to each `mysql` command line call. + Then run mysql < employees.sql From 7fcfdc2f2bf4fdd20f6932ab1be8985a7d63cc21 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Wed, 8 Apr 2026 12:02:42 +0000 Subject: [PATCH 10/36] 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 11/36] 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 12/36] 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 13/36] 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 14/36] 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 15/36] 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 16/36] 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 17/36] 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 18/36] 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 19/36] 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 20/36] 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 21/36] 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 22/36] 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 23/36] 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 24/36] 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 25/36] 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 26/36] 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 27/36] 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 28/36] 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 29/36] 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 30/36] 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 31/36] 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 32/36] 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 33/36] 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 34/36] 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 35/36] 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 36/36] Trigger CI