diff --git a/packaging/coldfront-duckdb-extensions/rpm/coldfront-duckdb-extensions.spec b/packaging/coldfront-duckdb-extensions/rpm/coldfront-duckdb-extensions.spec index 61b4081..f5ddb7d 100644 --- a/packaging/coldfront-duckdb-extensions/rpm/coldfront-duckdb-extensions.spec +++ b/packaging/coldfront-duckdb-extensions/rpm/coldfront-duckdb-extensions.spec @@ -66,5 +66,5 @@ install -D -m 0644 %{sname}-sbom.json.asc %{buildroot}%{_datadir}/pgedge-%{sname %{_datadir}/pgedge-%{sname}/%{sname}-sbom.json.asc %changelog -* Tue Jul 01 2026 Muhammad Aqeel - 1.5.4-1 +* Wed Jul 01 2026 Muhammad Aqeel - 1.5.4-1 - Initial build of the ColdFront patched DuckDB extensions (DuckDB 1.5.4) diff --git a/packaging/coldfront/rpm/coldfront.spec b/packaging/coldfront/rpm/coldfront.spec index b8c7129..c862d6b 100644 --- a/packaging/coldfront/rpm/coldfront.spec +++ b/packaging/coldfront/rpm/coldfront.spec @@ -58,5 +58,5 @@ install -p -m 0644 %{_builddir}/%{sname}-%{version}/%{sname}-sbom.json.asc %{bui %{pginstdir}/sbom/%{sname}-sbom.json.asc %changelog -* Mon Jun 30 2026 Muhammad Aqeel - 1.0.0-1 +* Tue Jun 30 2026 Muhammad Aqeel - 1.0.0-1 - Initial build of the pgEdge ColdFront extension diff --git a/packaging/pg_duckdb/build-deb.sh b/packaging/pg_duckdb/build-deb.sh index e16870d..1994c97 100644 --- a/packaging/pg_duckdb/build-deb.sh +++ b/packaging/pg_duckdb/build-deb.sh @@ -9,6 +9,23 @@ CWD="$(pwd)" export DEBIAN_FRONTEND=noninteractive +# Only the owner major (the latest — see common.sh) packages the shared DuckDB +# engine as pgedge-libduckdb; every other major deletes its identical copy. +# debian/rules reads both of these from the environment. +if [ "${PG_MAJOR_VERSION}" = "${LIBDUCKDB_OWNER_PG_MAJOR}" ]; then + export LIBDUCKDB_OWNER=yes +else + export LIBDUCKDB_OWNER=no +fi + +# LIBDUCKDB_DIR comes from common.sh and is used for both the RPATH (below) and +# the install location (debian/rules reads it from the environment). + +# Upper bound for the engine dependency — DuckDB's C++ ABI is tied to the +# upstream version, so 1.5.4 must never resolve against 1.5.5. Bumps the last +# component: 1.5.4 -> 1.5.5. +LIBDUCKDB_ENGINE_NEXT="$(awk -F. '{ $NF = $NF + 1; print }' OFS=. <<< "${PG_DUCKDB_ENGINE_VERSION}")" + prepare() { setup_apt_build_env @@ -35,13 +52,32 @@ prepare() { # pgEdge propagates -fexcess-precision=standard into CXXFLAGS, which gcc # rejects for C++. Strip it after PGXS is included (no-op if absent). printf '\noverride CXXFLAGS := $(filter-out -fexcess-precision=standard,$(CXXFLAGS))\n' >> Makefile.global + # Teach pg_duckdb.so where the shared engine lives. Upstream links it with a + # plain -lduckdb plus -Wl,-rpath,$(PG_LIB), which only worked while + # libduckdb.so sat in the PG libdir; it now ships in LIBDUCKDB_DIR, so add + # that to the RPATH. Makefile.global is included AFTER $(PGXS), and PGXS + # expands SHLIB_LINK when it links, so appending here still lands. + printf '\nSHLIB_LINK += -Wl,-rpath,%s\n' "${LIBDUCKDB_DIR}" >> Makefile.global ) echo "Moving Debian packaging into source directory..." cp -rp "${CWD}/${COMPONENT_NAME}/deb/debian" "$SRC_DIR/" cd "$SRC_DIR" cp debian/control.in debian/control + # The shared-engine stanza is appended ONLY for the owner major; the other + # majors must not declare pgedge-libduckdb at all, or the release would push + # several same-version builds of it and reprepro would reject the second. + if [ "${LIBDUCKDB_OWNER}" = "yes" ]; then + echo "PG ${PG_MAJOR_VERSION} is the libduckdb owner — building pgedge-libduckdb here" + cat debian/control.libduckdb.in >> debian/control + else + echo "PG ${PG_MAJOR_VERSION} is not the libduckdb owner (${LIBDUCKDB_OWNER_PG_MAJOR} is) — engine comes from pgedge-libduckdb" + rm -f debian/pgedge-libduckdb.install + fi + rm -f debian/control.in debian/control.libduckdb.in sed -i "s|PG_MAJOR_VERSION|${PG_MAJOR_VERSION}|g" debian/control + sed -i "s|DUCKDB_ENGINE_VERSION|${PG_DUCKDB_ENGINE_VERSION}|g;s|DUCKDB_ENGINE_NEXT|${LIBDUCKDB_ENGINE_NEXT}|g" \ + debian/control mv debian/pgedge-postgresql-pg-duckdb.install \ debian/pgedge-postgresql-${PG_MAJOR_VERSION}-pg-duckdb.install sed -i "s|PG_MAJOR_VERSION|${PG_MAJOR_VERSION}|g" \ diff --git a/packaging/pg_duckdb/build-rpm.sh b/packaging/pg_duckdb/build-rpm.sh index 0c5952a..d601458 100644 --- a/packaging/pg_duckdb/build-rpm.sh +++ b/packaging/pg_duckdb/build-rpm.sh @@ -3,6 +3,16 @@ set -euo pipefail RHEL="$(rpm --eval %rhel)" +# Only the owner major (the latest — see common.sh) packages the shared DuckDB +# engine; every other major deletes its identical copy and links against it. +# The comparison lives here, not in the spec, so RPM and DEB decide it the same +# way from the same variable. +if [ "${PG_MAJOR_VERSION}" = "${LIBDUCKDB_OWNER_PG_MAJOR}" ]; then + WITH_LIBDUCKDB=1 +else + WITH_LIBDUCKDB=0 +fi + prepare() { setup_dnf_build_env @@ -38,6 +48,8 @@ prepare() { # This function is for debugging purpose if you have your own keys. GH workflow does not need it. #import_gpg_keys + # with_libduckdb must be defined here too — builddep parses the same spec, and + # an undefined macro in the %if would abort the parse. echo "🔧 Installing RPM build dependencies..." dnf builddep -y \ --define "pgmajorversion ${PG_MAJOR_VERSION}" \ @@ -45,17 +57,26 @@ prepare() { --define "pg_duckdb_version ${PG_DUCKDB_VERSION}" \ --define "pg_duckdb_buildnum ${PG_DUCKDB_BUILDNUM}" \ --define "curl_version ${CURL_VERSION}" \ + --define "with_libduckdb ${WITH_LIBDUCKDB}" \ + --define "libduckdb_dir ${LIBDUCKDB_DIR}" \ ~/rpmbuild/SPECS/pg_duckdb.spec } build() { echo "Building RPM and SRPM..." + if [ "${WITH_LIBDUCKDB}" = "1" ]; then + echo " PG ${PG_MAJOR_VERSION} is the libduckdb owner — this cell also builds pgedge-libduckdb" + else + echo " PG ${PG_MAJOR_VERSION} is not the libduckdb owner (${LIBDUCKDB_OWNER_PG_MAJOR} is) — engine comes from pgedge-libduckdb" + fi QA_RPATHS=$(( 0xffff )) rpmbuild -ba ~/rpmbuild/SPECS/pg_duckdb.spec \ --define "pgmajorversion ${PG_MAJOR_VERSION}" \ --define "pginstdir /usr/pgsql-${PG_MAJOR_VERSION}" \ --define "pg_duckdb_version ${PG_DUCKDB_VERSION}" \ --define "pg_duckdb_buildnum ${PG_DUCKDB_BUILDNUM}" \ - --define "curl_version ${CURL_VERSION}" + --define "curl_version ${CURL_VERSION}" \ + --define "with_libduckdb ${WITH_LIBDUCKDB}" \ + --define "libduckdb_dir ${LIBDUCKDB_DIR}" } post_build() { diff --git a/packaging/pg_duckdb/common.sh b/packaging/pg_duckdb/common.sh index 39b647d..580fd6c 100644 --- a/packaging/pg_duckdb/common.sh +++ b/packaging/pg_duckdb/common.sh @@ -25,13 +25,84 @@ export PG_DUCKDB_COMMIT="${PG_DUCKDB_COMMIT:-c04e6a2dcf4e999abb921da1ba2f8335dad export PG_DUCKDB_VERSION="${COMPONENT_VERSION:-1.5.4}" export PG_DUCKDB_BUILDNUM="${COMPONENT_BUILDNUM:-1}" -# libcurl built from source and BUNDLED in the package. DuckDB 1.5.4 httpfs links -# CURLSSLOPT_AUTO_CLIENT_CERT (curl >= 7.77); el9 ships 7.76 and debian bullseye -# 7.74, so the system curl can't resolve pg_duckdb.so's symbols at load. 8.12.0 -# also fixes CVE-2025-0665. Installed into the PG libdir with a $ORIGIN RUNPATH -# on pg_duckdb.so so it finds the co-located copy without touching the OS libcurl. +# libcurl built from source at BUILD TIME ONLY on the two distros whose system +# curl is too old to COMPILE against — el9 (7.76) and debian bullseye (7.74) — +# because DuckDB 1.5.4's httpfs uses CURLSSLOPT_AUTO_CLIENT_CERT (curl >= 7.77). +# 8.12.0 also fixes CVE-2025-0665. It is NOT shipped and NOT rpath'd: the built +# .so references only the libcurl.so.4 soname plus symbols the older system curl +# already provides, so at runtime the native system libcurl resolves it. Every +# other distro compiles against its own system libcurl and ignores this. export CURL_VERSION="${CURL_VERSION:-8.12.0}" +# --- Shared DuckDB engine package ---------------------------------------------- +# libduckdb.so is DuckDB itself, built from pg_duckdb's third_party/duckdb +# submodule. Nothing in it is PostgreSQL-specific — its DT_NEEDED set is only +# curl/ssl/crypto/stdc++/m/gcc_s/c — so every per-major build produces the same +# engine (verified: .text and .rodata are bit-identical across PG 16/17/18). +# +# Shipping that identical library from all three per-major packages made them +# mutually UNINSTALLABLE: identical binary -> identical build-id -> each package +# claimed the same /usr/lib/.build-id//, but as a symlink pointing at +# its OWN PG libdir. rpm's transaction test and dpkg both reject that, so +# installing pg16+pg17+pg18 side by side failed. It only appeared to work on +# distros where an accidental per-major RUNPATH perturbed the binary enough to +# change its build-id. +# +# The engine now ships ONCE as `pgedge-libduckdb`, into a pgEdge-private +# directory (not the system libdir — the soname is unversioned, so a public +# install would squat a name any future distro duckdb package would want). +# pg_duckdb.so reaches it through an RPATH added at build time, so the per-major +# packages carry nothing but the extension itself. +# +# PER_PG_VERSION=true gives each major its own rpmbuild/dpkg-buildpackage run, +# so exactly ONE run may emit the shared package. If more than one did, the +# release would push N same-version artifacts built from N different runs: +# reprepro rejects the second outright ("already registered with different +# checksum") and the dnf side would silently replace the file, leaving clients +# with repo metadata pointing at a checksum that no longer matches. +# +# That one run is the LATEST major — it is the major that stays in the matrix +# as older ones age out, so the owner moves forward on its own. The list comes +# from release.yml's detect step for this component, read straight out of the +# workflow so the two can never drift (the duckdb-extensions component reads +# ICEBERG_REF from the Dockerfile the same way). $GITHUB_WORKSPACE is mounted +# into the build container, so the workflow is readable from inside the build. +CWD="${CWD:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +libduckdb_pg_majors="$(awk ' + /component_name: packaging\/pg_duckdb[[:space:]]*$/ { found = 1 } + found && /pg_versions:/ { gsub(/[^0-9,]/, ""); print; exit } +' "${CWD}/.github/workflows/release.yml" 2>/dev/null)" + +# Highest major in that list. Falls back to 18 when the list is unreadable — a +# build outside a repo checkout, or a workflow edit that moves the detect step +# beyond awk's reach. A wrong owner is self-announcing rather than silent: +# pgedge-libduckdb either goes missing (the promote install test fails on an +# unsatisfied dependency) or gets built twice (the apt push fails). +# LIBDUCKDB_OWNER_PG_MAJOR is the single override knob. +export LIBDUCKDB_OWNER_PG_MAJOR="${LIBDUCKDB_OWNER_PG_MAJOR:-$( + printf '%s' "${libduckdb_pg_majors:-18}" | tr ',' '\n' | grep -E '^[0-9]+$' | sort -n | tail -1 +)}" +: "${LIBDUCKDB_OWNER_PG_MAJOR:=18}" +unset libduckdb_pg_majors + +# Where the shared engine installs, and what pg_duckdb.so gets an RPATH to. +# Its own subdirectory alongside the duckdb extensions, under the same +# /usr/lib/pgedge/coldfront root (COLDFRONT_EXTDIR is +# /usr/lib/pgedge/coldfront/duckdb-extensions), so the package owns its +# directory outright and the ColdFront payload stays in one namespace. Same literal path on RPM and DEB: +# deliberately not %{_libdir} and not multiarch-qualified, since nothing here is +# ever co-installed for two architectures and one path keeps the RPATH identical +# across formats. Private rather than a public libdir because the soname is +# unversioned — a public install would squat a name any future distro duckdb +# package would want. +export LIBDUCKDB_DIR="${LIBDUCKDB_DIR:-/usr/lib/pgedge/coldfront/libduckdb}" + +# The clean upstream DuckDB engine version, captured BEFORE the DEB pretag fold +# below can decorate PG_DUCKDB_VERSION (1.5.4 -> 1.5.4~beta2). The per-major +# packages depend on the shared engine by this version, so the dependency stays +# stable across rc/beta rebuilds while still pinning the C++ ABI to one engine. +export PG_DUCKDB_ENGINE_VERSION="${PG_DUCKDB_VERSION}" + # DEB only: move a pre-release pretag (e.g. BUILDNUM='rc1_1') into the upstream # VERSION with a leading '~' (1.5.4~rc1, BUILDNUM=1) so '~' sorts pre-releases # BELOW stable in dpkg/reprepro. Gated on apt-get so RPM keeps the pretag in diff --git a/packaging/pg_duckdb/deb/debian/control.in b/packaging/pg_duckdb/deb/debian/control.in index ce6d2cd..202935e 100644 --- a/packaging/pg_duckdb/deb/debian/control.in +++ b/packaging/pg_duckdb/deb/debian/control.in @@ -29,6 +29,8 @@ Depends: ${misc:Depends}, ${shlibs:Depends}, pgedge-postgresql-PG_MAJOR_VERSION, + pgedge-libduckdb (>= DUCKDB_ENGINE_VERSION~), + pgedge-libduckdb (<< DUCKDB_ENGINE_NEXT~), libcurl4, Conflicts: postgresql-PG_MAJOR_VERSION-pg-duckdb, diff --git a/packaging/pg_duckdb/deb/debian/control.libduckdb.in b/packaging/pg_duckdb/deb/debian/control.libduckdb.in new file mode 100644 index 0000000..f00e299 --- /dev/null +++ b/packaging/pg_duckdb/deb/debian/control.libduckdb.in @@ -0,0 +1,15 @@ + +Package: pgedge-libduckdb +Architecture: any +Section: libs +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: DuckDB engine shared library for pgEdge pg_duckdb + The DuckDB columnar-vectorized analytics engine, built from the + third_party/duckdb submodule pinned by pgEdge's pg_duckdb and shared by the + pg_duckdb extension for every PostgreSQL major version. + . + This library contains no PostgreSQL-specific code; the per-major + pgedge-postgresql-N-pg-duckdb packages link against it and find it at runtime + through an RPATH, so they ship nothing but the extension itself. diff --git a/packaging/pg_duckdb/deb/debian/pgedge-libduckdb.install b/packaging/pg_duckdb/deb/debian/pgedge-libduckdb.install new file mode 100644 index 0000000..908a1dd --- /dev/null +++ b/packaging/pg_duckdb/deb/debian/pgedge-libduckdb.install @@ -0,0 +1 @@ +usr/lib/pgedge/coldfront/libduckdb/libduckdb.so diff --git a/packaging/pg_duckdb/deb/debian/pgedge-postgresql-pg-duckdb.install b/packaging/pg_duckdb/deb/debian/pgedge-postgresql-pg-duckdb.install index 19f2a62..da2a6e8 100644 --- a/packaging/pg_duckdb/deb/debian/pgedge-postgresql-pg-duckdb.install +++ b/packaging/pg_duckdb/deb/debian/pgedge-postgresql-pg-duckdb.install @@ -1 +1,9 @@ debian/tmp/sbom/* usr/lib/postgresql/PG_MAJOR_VERSION/sbom/ +# Everything `make install` produced, minus libduckdb.so: debian/rules has +# already moved that to the shared pgedge dir (owner build, where it becomes +# pgedge-libduckdb) or deleted it (every other major). Listed explicitly because +# dh_install only falls back to "install whatever is left in debian/tmp" when +# the source builds a SINGLE binary package, which stops being true in the +# owner build. +usr/lib/postgresql/ +usr/share/postgresql/ diff --git a/packaging/pg_duckdb/deb/debian/rules b/packaging/pg_duckdb/deb/debian/rules index 4376178..e2de74f 100644 --- a/packaging/pg_duckdb/deb/debian/rules +++ b/packaging/pg_duckdb/deb/debian/rules @@ -4,6 +4,25 @@ export DUCKDB_GEN = ninja export CMAKE_BUILD_PARALLEL_LEVEL = 4 +# --- Shared DuckDB engine (rationale in packaging/pg_duckdb/common.sh) ---------- +# libduckdb.so is PG-major independent, so exactly one major — the owner, which +# build-deb.sh exports here from LIBDUCKDB_OWNER_PG_MAJOR — ships it as +# pgedge-libduckdb; every other major drops its identical copy. Shipping it from +# all of them gave every per-major dbgsym the same +# /usr/lib/debug/.build-id//.debug path, so they refused to install +# side by side. pg_duckdb.so reaches the engine in its new home through an RPATH +# that build-deb.sh adds at build time, so no per-major link has to be managed. +LIBDUCKDB_OWNER ?= no +# Exported by build-deb.sh, which uses the same value for the RPATH; the default +# keeps a standalone `dpkg-buildpackage` working. +LIBDUCKDB_DIR ?= /usr/lib/pgedge/coldfront/libduckdb +# build-deb.sh puts the target major's bin dir first on PATH, so this resolves to +# that major's libdir — where `make install` drops both artifacts. +PG_PKGLIBDIR := $(shell pg_config --pkglibdir) +# dh_shlibdeps needs the engine to resolve pg_duckdb.so's NEEDED entry in the +# builds that no longer stage libduckdb.so themselves. +DUCKDB_BUILD_LIBDIR := $(CURDIR)/third_party/duckdb/build/release/src + %: dh $@ @@ -14,7 +33,34 @@ override_dh_auto_build: USE_PGXS=1 dh_auto_build -- -j4 with_llvm=no override_dh_auto_install: - USE_PGXS=1 dh_auto_install -- with_llvm=no + # --destdir is pinned so the layout does not change between the owner build + # (two binary packages -> debian/tmp) and the others (one -> debian/); + # the .install files depend on it being debian/tmp either way. + USE_PGXS=1 dh_auto_install --destdir=debian/tmp -- with_llvm=no + # The owner relocates the engine to the shared directory already on + # pg_duckdb.so's RPATH; everyone else drops their identical copy. Nothing is + # left in the PG libdir, so there is no per-major link to keep in step. + if [ "$(LIBDUCKDB_OWNER)" = "yes" ]; then \ + mkdir -p debian/tmp$(LIBDUCKDB_DIR); \ + mv debian/tmp$(PG_PKGLIBDIR)/libduckdb.so debian/tmp$(LIBDUCKDB_DIR)/libduckdb.so; \ + else \ + rm -f debian/tmp$(PG_PKGLIBDIR)/libduckdb.so; \ + fi + +override_dh_shlibdeps: + # -l: libduckdb.so is no longer beside pg_duckdb.so in the staging tree, so + # point dpkg-shlibdeps at the build tree to resolve the NEEDED entry. + # --ignore-missing-info: it comes from this build rather than an installed + # package, so it has no shlibs/symbols data. The dependency on the engine is + # declared explicitly in debian/control instead. + dh_shlibdeps -l$(DUCKDB_BUILD_LIBDIR) -- --ignore-missing-info + +override_dh_missing: + # The .install globs above are the only thing routing files into packages in + # the owner build (dh_install's "install whatever is left" fallback applies + # to single-binary sources only), so fail loudly rather than silently + # shipping a package with a file missing. + dh_missing --fail-missing override_dh_auto_test: # pg_duckdb's `make check` drives a full regression harness that needs a diff --git a/packaging/pg_duckdb/rpm/pg_duckdb.spec b/packaging/pg_duckdb/rpm/pg_duckdb.spec index 2ca0f89..a2a5e5e 100644 --- a/packaging/pg_duckdb/rpm/pg_duckdb.spec +++ b/packaging/pg_duckdb/rpm/pg_duckdb.spec @@ -4,6 +4,30 @@ # installed artifacts (pg_duckdb.so, pg_duckdb.control, tarball dir). %global pkgname pg-duckdb +# --- Shared DuckDB engine ------------------------------------------------------ +# libduckdb.so is PG-major independent (identical .text/.rodata in every +# per-major build), so it ships ONCE from the owner major's build as +# pgedge-libduckdb, into a pgEdge-private directory rather than the system +# libdir — the soname is unversioned, so a public install would squat a name any +# future distro duckdb package would want. pg_duckdb.so reaches it through an +# RPATH added in %%build. See packaging/pg_duckdb/common.sh for the full +# rationale — in short, shipping the identical library from all three packages +# gave them all the same build-id and therefore colliding /usr/lib/.build-id/ +# entries, which made them refuse to install side by side. +# +# build-rpm.sh passes with_libduckdb=1 for exactly one major (the latest, from +# LIBDUCKDB_OWNER_PG_MAJOR in common.sh) and 0 for the rest — the comparison +# lives there rather than here so the owner is decided in one place for both +# RPM and DEB. Defaulting to 1 keeps a bare `rpmbuild` of a single major +# self-contained and installable. +%{!?with_libduckdb: %global with_libduckdb 1} +# libduckdb_dir is passed by build-rpm.sh from LIBDUCKDB_DIR in common.sh, so +# RPM and DEB install the engine to the same path and share one RPATH value; the +# fallback matches that default. Not %{_libdir}: this sits alongside the duckdb +# extensions under the same /usr/lib/pgedge/coldfront root (COLDFRONT_EXTDIR), +# and the literal path is identical on RPM and DEB. +%{!?libduckdb_dir: %global libduckdb_dir /usr/lib/pgedge/coldfront/libduckdb} + Name: pgedge-%{pkgname}_%{pgmajorversion} Version: %{pg_duckdb_version} Release: %{pg_duckdb_buildnum}%{?dist} @@ -39,6 +63,13 @@ BuildRequires: git BuildRequires: libcurl-devel %endif Requires: pgedge-postgresql%{pgmajorversion}-server +# The DuckDB engine this pg_duckdb.so was linked against. Pinned by VERSION and +# not by NVR on purpose: DuckDB's C++ ABI is tied to the engine version, so +# 1.5.4 must never be paired with 1.5.5, but a rebuild of one major at a higher +# buildnum must still resolve against the engine already in the repo. Omitting +# the release is what buys that — rpm ignores the release when the requirement +# does not name one. +Requires: pgedge-libduckdb = %{version} # Native system libcurl at runtime on every EL (the curl >= 7.77 need is # compile-time only; the curl HTTP path is unused — ColdFront forces httplib). Requires: libcurl @@ -51,6 +82,23 @@ enabling high-performance analytical queries and direct access to data lakes and external file formats (Parquet, CSV, Iceberg, object storage) from within PostgreSQL. +%if %{with_libduckdb} +%package -n pgedge-libduckdb +Summary: DuckDB engine shared library for pgEdge pg_duckdb +# Intentionally NO PostgreSQL dependency — this is plain DuckDB, shared by every +# pgedge-pg-duckdb_NN. Built only by the owner major's cell (see common.sh). +Requires: libcurl + +%description -n pgedge-libduckdb +The DuckDB columnar-vectorized analytics engine, built from the third_party/duckdb +submodule pinned by pgEdge's pg_duckdb and shared by the pg_duckdb extension for +every PostgreSQL major version. + +This library contains no PostgreSQL-specific code; the per-major +pgedge-pg-duckdb_NN packages link against it and find it at runtime through an +RPATH, so they ship nothing but the extension itself. +%endif + %prep %setup -q -n %{sname}-%{version} @@ -59,6 +107,15 @@ PostgreSQL. # for C++. Strip it after PGXS is included (no-op if absent). printf '\noverride CXXFLAGS := $(filter-out -fexcess-precision=standard,$(CXXFLAGS))\n' >> Makefile.global +# Teach pg_duckdb.so where the shared engine lives. Upstream links it with +# -Wl,-rpath,$(PG_LIB) and a plain -lduckdb, which only worked while libduckdb.so +# sat in the PG libdir; it now lives in %{libduckdb_dir}, so add that to the +# RPATH. Appending to Makefile.global is what puts this AFTER `include $(PGXS)` +# (the same reason the CXXFLAGS override above goes here) — PGXS expands +# SHLIB_LINK when it runs the link, so a late append still lands. +# build-rpm.sh sets QA_RPATHS to allow the non-standard directory. +printf '\nSHLIB_LINK += -Wl,-rpath,%{libduckdb_dir}\n' >> Makefile.global + %if 0%{?rhel} && 0%{?rhel} < 10 # --- EL9 only: build curl >= 7.77 from source for the build (NOT shipped) ------ # Installed to a build-local prefix and exposed to find_package(CURL); we do NOT @@ -96,17 +153,37 @@ mkdir -p %{buildroot}/%{pginstdir}/sbom install -p -m 0644 %{_builddir}/%{sname}-%{version}/%{sname}-sbom.json %{buildroot}/%{pginstdir}/sbom/%{sname}-sbom.json install -p -m 0644 %{_builddir}/%{sname}-%{version}/%{sname}-sbom.json.asc %{buildroot}/%{pginstdir}/sbom/%{sname}-sbom.json.asc +# `make install` drops libduckdb.so next to pg_duckdb.so in the PG libdir. Only +# the owner major keeps it, relocated to the shared directory that %build put on +# pg_duckdb.so's RPATH; every other major deletes its identical copy BEFORE +# find-debuginfo runs, so no build-id entry is generated for it and nothing can +# collide. Nothing is left behind in the PG libdir — the loader finds the engine +# by RPATH, so there is no per-major link to keep in step. +%if %{with_libduckdb} +mkdir -p %{buildroot}%{libduckdb_dir} +mv %{buildroot}/%{pginstdir}/lib/libduckdb.so %{buildroot}%{libduckdb_dir}/libduckdb.so +%else +%{__rm} -f %{buildroot}/%{pginstdir}/lib/libduckdb.so +%endif + %files %doc README.md %license LICENSE -# Dynamic build ships the extension and the bundled DuckDB shared library. +# The extension only. The DuckDB engine it links against ships separately, in +# pgedge-libduckdb (see the %%install note above). %{pginstdir}/lib/%{sname}.so -%{pginstdir}/lib/libduckdb.so %{pginstdir}/share/extension/%{sname}.control %{pginstdir}/share/extension/%{sname}*.sql %{pginstdir}/sbom/%{sname}-sbom.json %{pginstdir}/sbom/%{sname}-sbom.json.asc +%if %{with_libduckdb} +%files -n pgedge-libduckdb +%license LICENSE +%dir %{libduckdb_dir} +%{libduckdb_dir}/libduckdb.so +%endif + %changelog -* Mon Jun 30 2026 Muhammad Aqeel - 1.5.4-1 +* Tue Jun 30 2026 Muhammad Aqeel - 1.5.4-1 - ColdFront build of pgEdge pg_duckdb (DuckDB 1.5.4, pg_duckdb PR #1025)