Skip to content

Commit e430828

Browse files
jfrochesamrose
andauthored
feat: support multiple versions of the postgis extension (#1667)
* feat: support multiple versions of the postgis extension Build multiple versions of the postgis extension on different PostgreSQL versions. Add test for the extensions and their upgrade on PostgreSQL 15 and 17. * chore: correct formatting * fix: symbolic linking removal * chore: bump version to retest * fix: This fix ensures that the control files reference $libdir/$ext-$MIN_MAJ_VERSION (e.g., $libdir/postgis-3.3) which matches the actual library names built with the --with-library-minor-version flag. This allows: 1. Multiple PostGIS versions to coexist (3.3.2 and 3.3.7 both use postgis-3.3.so) 2. Extensions to properly find their libraries during upgrades 3. The multi-version functionality to work correctly * chore: bump to test * fix: handling of versions --------- Co-authored-by: Sam Rose <[email protected]>
1 parent 3eb98fd commit e430828

File tree

5 files changed

+351
-82
lines changed

5 files changed

+351
-82
lines changed

ansible/tasks/stage2-setup-postgres.yml

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -217,19 +217,8 @@
217217
recurse: yes
218218
when: stage2_nix
219219

220-
- name: Check psql_version and run postgis linking if not oriole-xx
221-
block:
222-
- name: Check if psql_version is psql_orioledb-17
223-
set_fact:
224-
is_psql_oriole: "{{ psql_version == 'psql_orioledb-17' }}"
225-
226-
- name: Recursively create symbolic links and set permissions for the contrib/postgis-* dir
227-
shell: >
228-
sudo mkdir -p /usr/lib/postgresql/share/postgresql/contrib && \
229-
sudo find /var/lib/postgresql/.nix-profile/share/postgresql/contrib/ -mindepth 1 -type d -exec sh -c 'for dir do sudo ln -s "$dir" "/usr/lib/postgresql/share/postgresql/contrib/$(basename "$dir")"; done' sh {} + \
230-
&& chown -R postgres:postgres "/usr/lib/postgresql/share/postgresql/contrib/"
231-
become: yes
232-
when: stage2_nix and not is_psql_oriole
220+
# PostGIS contrib linking removed - PostGIS doesn't install to contrib directory
221+
# It installs extensions to /share/postgresql/extension/ which is already linked above
233222

234223
- name: Create symbolic links from /var/lib/postgresql/.nix-profile/share/postgresql/timezonesets to /usr/lib/postgresql/share/postgresql/timeszonesets
235224
shell: >-

ansible/vars.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ postgres_major:
1010

1111
# Full version strings for each major version
1212
postgres_release:
13-
postgresorioledb-17: 17.5.1.033-orioledb
14-
postgres17: 17.6.1.012
15-
postgres15: 15.14.1.012
13+
postgresorioledb-17: 17.5.1.034-orioledb
14+
postgres17: 17.6.1.013
15+
postgres15: 15.14.1.013
1616

1717
# Non Postgres Extensions
1818
pgbouncer_release: 1.19.0

nix/ext/postgis.nix

Lines changed: 176 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,88 +15,198 @@
1515
pcre2,
1616
nixosTests,
1717
callPackage,
18+
buildEnv,
1819
}:
1920

2021
let
2122
sfcgal = callPackage ./sfcgal/sfcgal.nix { };
2223
gdal = callPackage ./gdal.nix { inherit postgresql; };
23-
in
24-
stdenv.mkDerivation rec {
2524
pname = "postgis";
26-
version = "3.3.7";
2725

28-
outputs = [
29-
"out"
30-
"doc"
31-
];
26+
# Load version configuration from external file
27+
allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).${pname};
3228

33-
src = fetchurl {
34-
url = "https://download.osgeo.org/postgis/source/postgis-${version}.tar.gz";
35-
sha256 = "sha256-UHJKDd5JrcJT5Z4CTYsY/va+ToU0GUPG1eHhuXTkP84=";
36-
};
29+
# Filter versions compatible with current PostgreSQL version
30+
supportedVersions = lib.filterAttrs (
31+
_: value: builtins.elem (lib.versions.major postgresql.version) value.postgresql
32+
) allVersions;
33+
34+
# Derived version information
35+
versions = lib.naturalSort (lib.attrNames supportedVersions);
36+
latestVersion = lib.last versions;
37+
numberOfVersions = builtins.length versions;
38+
packages = builtins.attrValues (
39+
lib.mapAttrs (name: value: build name value.hash) supportedVersions
40+
);
3741

38-
buildInputs = [
39-
libxml2
40-
postgresql
41-
geos
42-
proj
43-
gdal
44-
json_c
45-
protobufc
46-
pcre2.dev
47-
sfcgal
48-
] ++ lib.optional stdenv.isDarwin libiconv;
49-
nativeBuildInputs = [
50-
perl
51-
pkg-config
42+
# List of C extensions to be included in the build
43+
cExtensions = [
44+
"address_standardizer"
45+
"postgis"
46+
"postgis_raster"
47+
"postgis_sfcgal"
48+
"postgis_topology"
5249
];
53-
dontDisableStatic = true;
5450

55-
env.NIX_LDFLAGS = "-L${lib.getLib json_c}/lib";
51+
sqlExtensions = [
52+
"address_standardizer_data_us"
53+
"postgis_tiger_geocoder"
54+
];
5655

57-
preConfigure = ''
58-
sed -i 's@/usr/bin/file@${file}/bin/file@' configure
59-
configureFlags="--datadir=$out/share/postgresql --datarootdir=$out/share/postgresql --bindir=$out/bin --docdir=$doc/share/doc/${pname} --with-gdalconfig=${gdal}/bin/gdal-config --with-jsondir=${json_c.dev} --disable-extension-upgrades-install --with-sfcgal"
56+
# Build function for individual versions
57+
build =
58+
version: hash:
59+
stdenv.mkDerivation rec {
60+
inherit pname version;
6061

61-
makeFlags="PERL=${perl}/bin/perl datadir=$out/share/postgresql pkglibdir=$out/lib bindir=$out/bin docdir=$doc/share/doc/${pname}"
62-
'';
62+
outputs = [
63+
"out"
64+
"doc"
65+
];
6366

64-
postConfigure = ''
65-
sed -i "s|@mkdir -p \$(DESTDIR)\$(PGSQL_BINDIR)||g ;
66-
s|\$(DESTDIR)\$(PGSQL_BINDIR)|$prefix/bin|g
67-
" \
68-
"raster/loader/Makefile";
69-
sed -i "s|\$(DESTDIR)\$(PGSQL_BINDIR)|$prefix/bin|g
70-
" \
71-
"raster/scripts/python/Makefile";
72-
mkdir -p $out/bin
73-
ln -s ${postgresql}/bin/postgres $out/bin/postgres
74-
'';
67+
src = fetchurl {
68+
url = "https://download.osgeo.org/postgis/source/postgis-${version}.tar.gz";
69+
inherit hash;
70+
};
7571

76-
postInstall = ''
77-
rm $out/bin/postgres
78-
for prog in $out/bin/*; do # */
79-
ln -s $prog $prog-${version}
80-
done
81-
# Add function definition and usage to tiger geocoder files
82-
for file in $out/share/postgresql/extension/postgis_tiger_geocoder*--${version}.sql; do
83-
sed -i "/SELECT postgis_extension_AddToSearchPath('tiger');/a SELECT postgis_extension_AddToSearchPath('extensions');" "$file"
84-
done
85-
# Original topology patching
86-
for file in $out/share/postgresql/extension/postgis_topology*--${version}.sql; do
87-
sed -i "/SELECT topology.AddToSearchPath('topology');/i SELECT topology.AddToSearchPath('extensions');" "$file"
88-
done
89-
mkdir -p $doc/share/doc/postgis
90-
mv doc/* $doc/share/doc/postgis/
91-
'';
72+
buildInputs = [
73+
libxml2
74+
postgresql
75+
geos
76+
proj
77+
gdal
78+
json_c
79+
protobufc
80+
pcre2.dev
81+
sfcgal
82+
] ++ lib.optional stdenv.isDarwin libiconv;
83+
nativeBuildInputs = [
84+
perl
85+
pkg-config
86+
];
87+
dontDisableStatic = true;
88+
89+
env.NIX_LDFLAGS = "-L${lib.getLib json_c}/lib";
90+
91+
preConfigure = ''
92+
sed -i 's@/usr/bin/file@${file}/bin/file@' configure
93+
configureFlags="--datadir=$out/share/postgresql --datarootdir=$out/share/postgresql --bindir=$out/bin --docdir=$doc/share/doc/${pname} --with-gdalconfig=${gdal}/bin/gdal-config --with-jsondir=${json_c.dev} --with-sfcgal"
94+
95+
makeFlags="PERL=${perl}/bin/perl datadir=$out/share/postgresql pkglibdir=$out/lib bindir=$out/bin docdir=$doc/share/doc/${pname}"
96+
'';
97+
98+
postConfigure = ''
99+
sed -i "s|@mkdir -p \$(DESTDIR)\$(PGSQL_BINDIR)||g ;
100+
s|\$(DESTDIR)\$(PGSQL_BINDIR)|$prefix/bin|g
101+
" \
102+
"raster/loader/Makefile";
103+
sed -i "s|\$(DESTDIR)\$(PGSQL_BINDIR)|$prefix/bin|g
104+
" \
105+
"raster/scripts/python/Makefile";
106+
mkdir -p $out/bin
107+
ln -s ${postgresql}/bin/postgres $out/bin/postgres
108+
'';
109+
110+
postInstall = ''
111+
MIN_MAJ_VERSION=${lib.concatStringsSep "." (lib.take 2 (builtins.splitVersion version))}
112+
rm $out/bin/postgres
113+
114+
# Rename C extension libraries with full version suffix
115+
for ext in ${lib.concatStringsSep " " cExtensions}; do
116+
if [ -f "$out/lib/$ext-3${postgresql.dlSuffix}" ]; then
117+
mv $out/lib/$ext-3${postgresql.dlSuffix} $out/lib/$ext-${version}${postgresql.dlSuffix}
118+
fi
119+
done
92120
93-
passthru.tests.postgis = nixosTests.postgis;
121+
# Create version-specific control files (without default_version, pointing to unversioned library)
122+
for ext in ${lib.concatStringsSep " " (cExtensions ++ sqlExtensions)}; do
123+
sed -e "/^default_version =/d" \
124+
-e "s|^module_pathname = .*|module_pathname = '\$libdir/$ext-3'|" \
125+
$out/share/postgresql/extension/$ext.control > $out/share/postgresql/extension/$ext--${version}.control
126+
rm $out/share/postgresql/extension/$ext.control
127+
done
128+
129+
# Add function definition and usage to tiger geocoder files
130+
for file in $out/share/postgresql/extension/postgis_tiger_geocoder*--${version}.sql; do
131+
sed -i "/SELECT postgis_extension_AddToSearchPath('tiger');/a SELECT postgis_extension_AddToSearchPath('extensions');" "$file"
132+
done
133+
# Original topology patching
134+
for file in $out/share/postgresql/extension/postgis_topology*--${version}.sql; do
135+
sed -i "/SELECT topology.AddToSearchPath('topology');/i SELECT topology.AddToSearchPath('extensions');" "$file"
136+
done
137+
138+
# For the latest version, create default control file and library symlinks
139+
if [[ "${version}" == "${latestVersion}" ]]; then
140+
# Copy all SQL upgrade scripts only for latest version
141+
cp $out/share/postgresql/extension/*.sql $out/share/postgresql/extension/ 2>/dev/null || true
142+
143+
for ext in ${lib.concatStringsSep " " (cExtensions ++ sqlExtensions)}; do
144+
{
145+
echo "default_version = '${version}'"
146+
cat $out/share/postgresql/extension/$ext--${version}.control
147+
} > $out/share/postgresql/extension/$ext.control
148+
done
149+
150+
# Create symlinks for C extension libraries (latest version becomes the default)
151+
for ext in ${lib.concatStringsSep " " cExtensions}; do
152+
ln -sfn $ext-${version}${postgresql.dlSuffix} $out/lib/$ext-3${postgresql.dlSuffix}
153+
done
154+
155+
for prog in $out/bin/*; do # */
156+
ln -s $prog $prog-${version}
157+
done
158+
else
159+
# remove migration scripts for non-latest version
160+
find $out/share/postgresql/extension -regex '.*--.*--.*\.sql' -delete
161+
162+
for prog in $out/bin/*; do # */
163+
mv $prog $prog-${version}
164+
done
165+
fi
166+
167+
mkdir -p $doc/share/doc/postgis
168+
mv doc/* $doc/share/doc/postgis/
169+
'';
170+
171+
passthru.tests.postgis = nixosTests.postgis;
172+
173+
meta = with lib; {
174+
description = "Geographic Objects for PostgreSQL";
175+
homepage = "https://postgis.net/";
176+
changelog = "https://git.osgeo.org/gitea/postgis/postgis/raw/tag/${version}/NEWS";
177+
license = licenses.gpl2;
178+
inherit (postgresql.meta) platforms;
179+
};
180+
};
181+
in
182+
buildEnv {
183+
name = pname;
184+
paths = packages;
185+
186+
pathsToLink = [
187+
"/lib"
188+
"/share/postgresql/extension"
189+
];
190+
postBuild = ''
191+
# Verify all expected library files are present
192+
# We expect: (numberOfVersions * cExtensions) versioned libraries + cExtensions symlinks
193+
expectedFiles=${
194+
toString ((numberOfVersions * builtins.length cExtensions) + builtins.length cExtensions)
195+
}
196+
actualFiles=$(ls -A $out/lib/*${postgresql.dlSuffix} | wc -l)
197+
198+
if [[ "$actualFiles" != "$expectedFiles" ]]; then
199+
echo "Error: Expected $expectedFiles library files, found $actualFiles"
200+
echo "Files found:"
201+
ls -la $out/lib/*${postgresql.dlSuffix} || true
202+
exit 1
203+
fi
204+
'';
94205

95-
meta = with lib; {
96-
description = "Geographic Objects for PostgreSQL";
97-
homepage = "https://postgis.net/";
98-
changelog = "https://git.osgeo.org/gitea/postgis/postgis/raw/tag/${version}/NEWS";
99-
license = licenses.gpl2;
100-
inherit (postgresql.meta) platforms;
206+
passthru = {
207+
inherit versions numberOfVersions;
208+
pname = "${pname}-all";
209+
version =
210+
"multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
101211
};
102212
}

0 commit comments

Comments
 (0)