From dd18ac9a648b8ca338419356a47ca64f73a259c0 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Mon, 27 Apr 2026 23:11:09 -0300 Subject: [PATCH] fix(rds): vendor mysql UDF plugin API in fakecloud_udf.c The upstream mysql:8.0 image strips its community release repo after install, so `mysql-community-devel` (the package shipping `mysql.h`) is no longer installable on a fresh build. Re-adding the repo would hard-code a brittle URL. The UDF plugin API surface we touch (UDF_INIT, UDF_ARGS, Item_result) is tiny and ABI-stable across MySQL 5.6 -> 8.x and MariaDB 10.x/11.x. Vendor the struct definitions inline, drop the mysql.h include, and remove libmysqlclient-dev / libmariadb-dev from both Dockerfiles. Plugin dir is probed instead of asked from mysql_config. --- .../fakecloud-rds/assets/mariadb/Dockerfile | 32 ++++++++------- .../assets/mariadb/fakecloud_udf.c | 40 ++++++++++++++++++- crates/fakecloud-rds/assets/mysql/Dockerfile | 38 +++++++++--------- .../assets/mysql/fakecloud_udf.c | 40 ++++++++++++++++++- 4 files changed, 115 insertions(+), 35 deletions(-) diff --git a/crates/fakecloud-rds/assets/mariadb/Dockerfile b/crates/fakecloud-rds/assets/mariadb/Dockerfile index 8fd196c4..7ae186c9 100644 --- a/crates/fakecloud-rds/assets/mariadb/Dockerfile +++ b/crates/fakecloud-rds/assets/mariadb/Dockerfile @@ -9,9 +9,13 @@ ARG MARIADB_VERSION=10.11 FROM mariadb:${MARIADB_VERSION} USER root +# UDF plugin API structs are vendored inline in fakecloud_udf.c so we +# only need a C compiler + libcurl headers. Keeping the dep set +# symmetric with the mysql Dockerfile means a single source of truth +# for the build rule. RUN apt-get update \ && apt-get install -y --no-install-recommends \ - gcc make libcurl4-openssl-dev libmariadb-dev libc6-dev ca-certificates curl \ + gcc make libcurl4-openssl-dev libc6-dev ca-certificates curl \ && rm -rf /var/lib/apt/lists/* COPY fakecloud_udf.c /tmp/fakecloud_udf.c @@ -19,19 +23,19 @@ COPY fakecloud-bootstrap.sh /usr/local/bin/fakecloud-bootstrap.sh COPY 99-fakecloud-bootstrap.sql.tmpl /tmp/99-fakecloud-bootstrap.sql.tmpl RUN chmod +x /usr/local/bin/fakecloud-bootstrap.sh -# MariaDB ships mysql_config-style headers via libmariadb-dev. The -# plugin dir lives at /usr/lib/mysql/plugin (or under the mariadb -# tree on some images); honor whatever mysql_config reports first. -RUN PLUGIN_DIR="$(mariadb_config --plugindir 2>/dev/null \ - || mysql_config --plugindir 2>/dev/null \ - || echo /usr/lib/mysql/plugin)" \ - && mkdir -p "$PLUGIN_DIR" \ - && CFLAGS="$(mariadb_config --cflags 2>/dev/null \ - || mysql_config --cflags 2>/dev/null \ - || echo -I/usr/include/mariadb)" \ - && gcc -O2 -fPIC -shared $CFLAGS -o "$PLUGIN_DIR/fakecloud_udf.so" \ - /tmp/fakecloud_udf.c -lcurl -lpthread \ - && rm /tmp/fakecloud_udf.c +# Compile the UDF and drop it into the server's plugin dir. Mariadb +# images use /usr/lib/mysql/plugin on Debian; probe a few common +# locations for forward compatibility. +RUN set -eux; \ + PLUGIN_DIR=""; \ + for d in /usr/lib/mysql/plugin /usr/lib/x86_64-linux-gnu/mariadb19/plugin /usr/lib/aarch64-linux-gnu/mariadb19/plugin /usr/lib64/mysql/plugin; do \ + if [ -d "$d" ]; then PLUGIN_DIR="$d"; break; fi; \ + done; \ + : "${PLUGIN_DIR:=/usr/lib/mysql/plugin}"; \ + mkdir -p "$PLUGIN_DIR"; \ + gcc -O2 -fPIC -shared -o "$PLUGIN_DIR/fakecloud_udf.so" \ + /tmp/fakecloud_udf.c -lcurl -lpthread; \ + rm /tmp/fakecloud_udf.c ENTRYPOINT ["fakecloud-bootstrap.sh"] CMD ["mariadbd"] diff --git a/crates/fakecloud-rds/assets/mariadb/fakecloud_udf.c b/crates/fakecloud-rds/assets/mariadb/fakecloud_udf.c index 060401ec..c4195a3f 100644 --- a/crates/fakecloud-rds/assets/mariadb/fakecloud_udf.c +++ b/crates/fakecloud-rds/assets/mariadb/fakecloud_udf.c @@ -21,13 +21,51 @@ * 'plugin_dir'`) and `CREATE FUNCTION`. */ -#include #include #include #include #include #include +/* + * Minimal vendored UDF API. The mysql.h plugin headers ship in + * `mysql-community-devel` / `libmysqlclient-dev` / `libmariadb-dev`, + * which are absent or repo-gated on the upstream mysql:8.0 image + * (the official Dockerfile removes the community release repo). The + * struct layout below has been ABI-stable across MySQL 5.6 -> 8.x + * and MariaDB 10.x/11.x — the server casts our exported symbols to + * these shapes regardless of how we obtained them, so vendoring is + * safe and avoids a brittle external repo dependency at build time. + */ +enum Item_result { + INVALID_RESULT = -1, + STRING_RESULT = 0, + REAL_RESULT, + INT_RESULT, + ROW_RESULT, + DECIMAL_RESULT +}; + +typedef struct st_udf_args { + unsigned int arg_count; + enum Item_result *arg_type; + char **args; + unsigned long *lengths; + char *maybe_null; + char **attributes; + unsigned long *attribute_lengths; + void *extension; +} UDF_ARGS; + +typedef struct st_udf_init { + char maybe_null; + unsigned int decimals; + unsigned long max_length; + char *ptr; + char const_item; + void *extension; +} UDF_INIT; + /* ── shared helpers ─────────────────────────────────────────────────── */ struct curl_buf { diff --git a/crates/fakecloud-rds/assets/mysql/Dockerfile b/crates/fakecloud-rds/assets/mysql/Dockerfile index c1bacc2c..7a30e973 100644 --- a/crates/fakecloud-rds/assets/mysql/Dockerfile +++ b/crates/fakecloud-rds/assets/mysql/Dockerfile @@ -14,23 +14,19 @@ ARG MYSQL_VERSION=8.0 FROM mysql:${MYSQL_VERSION} USER root -# mysql 8.0+ defaults to an Oracle Linux base with `microdnf`; older -# tags + the `*-debian` variants ship apt. Pick whichever is present. -# We need build deps (gcc/make), libcurl headers, and the MySQL UDF -# header (`mysql.h`) — the latter ships in `mysql-community-devel`, -# which the image's enabled `mysql-tools-community` repo already -# provides. +# UDF needs only gcc + libcurl headers; mysql plugin API structs are +# vendored inline in fakecloud_udf.c (the upstream mysql:8.0 image +# strips its community release repo so `mysql-community-devel` is not +# installable, and adding it back hard-codes a brittle URL). RUN set -eux; \ if command -v microdnf >/dev/null 2>&1; then \ microdnf install -y \ - gcc make libcurl-devel glibc-devel pkgconf-pkg-config \ - mysql-community-devel; \ + gcc make libcurl-devel glibc-devel; \ microdnf clean all; \ elif command -v apt-get >/dev/null 2>&1; then \ apt-get update; \ apt-get install -y --no-install-recommends \ - gcc make libcurl4-openssl-dev libmysqlclient-dev libc6-dev \ - ca-certificates pkg-config; \ + gcc make libcurl4-openssl-dev libc6-dev ca-certificates; \ rm -rf /var/lib/apt/lists/*; \ else \ echo "no supported package manager found in base image" >&2; \ @@ -43,15 +39,19 @@ COPY fakecloud-bootstrap.sh /usr/local/bin/fakecloud-bootstrap.sh COPY 99-fakecloud-bootstrap.sql.tmpl /tmp/99-fakecloud-bootstrap.sql.tmpl RUN chmod +x /usr/local/bin/fakecloud-bootstrap.sh -# Compile the UDF against the running MySQL's plugin headers and drop -# it into the standard plugin dir. The runtime image ships -# mysql_config (or pkg-config + libmysqlclient-dev when on Debian). -RUN PLUGIN_DIR="$(mysql_config --plugindir 2>/dev/null || echo /usr/lib/mysql/plugin)" \ - && mkdir -p "$PLUGIN_DIR" \ - && CFLAGS="$(mysql_config --cflags 2>/dev/null || echo -I/usr/include/mysql)" \ - && gcc -O2 -fPIC -shared $CFLAGS -o "$PLUGIN_DIR/fakecloud_udf.so" \ - /tmp/fakecloud_udf.c -lcurl -lpthread \ - && rm /tmp/fakecloud_udf.c +# Compile the UDF and drop it into the server's plugin dir. We probe +# common locations because Oracle Linux ships /usr/lib64/mysql/plugin +# while Debian-based mysql images use /usr/lib/mysql/plugin. +RUN set -eux; \ + PLUGIN_DIR=""; \ + for d in /usr/lib64/mysql/plugin /usr/lib/mysql/plugin /usr/lib/x86_64-linux-gnu/mysql/plugin; do \ + if [ -d "$d" ]; then PLUGIN_DIR="$d"; break; fi; \ + done; \ + : "${PLUGIN_DIR:=/usr/lib/mysql/plugin}"; \ + mkdir -p "$PLUGIN_DIR"; \ + gcc -O2 -fPIC -shared -o "$PLUGIN_DIR/fakecloud_udf.so" \ + /tmp/fakecloud_udf.c -lcurl -lpthread; \ + rm /tmp/fakecloud_udf.c # The bootstrap shell script substitutes FAKECLOUD_* env vars into the # template SQL and drops the result into /docker-entrypoint-initdb.d/ diff --git a/crates/fakecloud-rds/assets/mysql/fakecloud_udf.c b/crates/fakecloud-rds/assets/mysql/fakecloud_udf.c index 060401ec..c4195a3f 100644 --- a/crates/fakecloud-rds/assets/mysql/fakecloud_udf.c +++ b/crates/fakecloud-rds/assets/mysql/fakecloud_udf.c @@ -21,13 +21,51 @@ * 'plugin_dir'`) and `CREATE FUNCTION`. */ -#include #include #include #include #include #include +/* + * Minimal vendored UDF API. The mysql.h plugin headers ship in + * `mysql-community-devel` / `libmysqlclient-dev` / `libmariadb-dev`, + * which are absent or repo-gated on the upstream mysql:8.0 image + * (the official Dockerfile removes the community release repo). The + * struct layout below has been ABI-stable across MySQL 5.6 -> 8.x + * and MariaDB 10.x/11.x — the server casts our exported symbols to + * these shapes regardless of how we obtained them, so vendoring is + * safe and avoids a brittle external repo dependency at build time. + */ +enum Item_result { + INVALID_RESULT = -1, + STRING_RESULT = 0, + REAL_RESULT, + INT_RESULT, + ROW_RESULT, + DECIMAL_RESULT +}; + +typedef struct st_udf_args { + unsigned int arg_count; + enum Item_result *arg_type; + char **args; + unsigned long *lengths; + char *maybe_null; + char **attributes; + unsigned long *attribute_lengths; + void *extension; +} UDF_ARGS; + +typedef struct st_udf_init { + char maybe_null; + unsigned int decimals; + unsigned long max_length; + char *ptr; + char const_item; + void *extension; +} UDF_INIT; + /* ── shared helpers ─────────────────────────────────────────────────── */ struct curl_buf {