1let
2
3 generic =
4 # dependencies
5 { stdenv, lib, fetchurl, makeWrapper, fetchpatch
6 , glibc, zlib, readline, openssl, icu, lz4, zstd, systemd, libossp_uuid
7 , pkg-config, libxml2, tzdata, libkrb5, substituteAll, darwin
8
9 # This is important to obtain a version of `libpq` that does not depend on systemd.
10 , enableSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd && !stdenv.hostPlatform.isStatic
11 , gssSupport ? with stdenv.hostPlatform; !isWindows && !isStatic
12
13 # for postgresql.pkgs
14 , this, self, newScope, buildEnv
15
16 # source specification
17 , version, hash, psqlSchema
18
19 # for tests
20 , testers, nixosTests, thisAttr
21
22 # JIT
23 , jitSupport ? false
24 , nukeReferences, patchelf, llvmPackages
25 , makeRustPlatform, buildPgxExtension, cargo, rustc
26
27 # detection of crypt fails when using llvm stdenv, so we add it manually
28 # for <13 (where it got removed: https://github.com/postgres/postgres/commit/c45643d618e35ec2fe91438df15abd4f3c0d85ca)
29 , libxcrypt
30 }:
31 let
32 atLeast = lib.versionAtLeast version;
33 olderThan = lib.versionOlder version;
34 lz4Enabled = atLeast "14";
35 zstdEnabled = atLeast "15";
36
37 pname = "postgresql";
38
39 stdenv' = if jitSupport then llvmPackages.stdenv else stdenv;
40 in stdenv'.mkDerivation (finalAttrs: {
41 inherit pname version;
42
43 src = fetchurl {
44 url = "mirror://postgresql/source/v${version}/${pname}-${version}.tar.bz2";
45 inherit hash;
46 };
47
48 hardeningEnable = lib.optionals (!stdenv'.cc.isClang) [ "pie" ];
49
50 outputs = [ "out" "lib" "doc" "man" ];
51 setOutputFlags = false; # $out retains configureFlags :-/
52
53 buildInputs = [
54 zlib
55 readline
56 openssl
57 libxml2
58 icu
59 ]
60 ++ lib.optionals (olderThan "13") [ libxcrypt ]
61 ++ lib.optionals jitSupport [ llvmPackages.llvm ]
62 ++ lib.optionals lz4Enabled [ lz4 ]
63 ++ lib.optionals zstdEnabled [ zstd ]
64 ++ lib.optionals enableSystemd [ systemd ]
65 ++ lib.optionals gssSupport [ libkrb5 ]
66 ++ lib.optionals (!stdenv'.isDarwin) [ libossp_uuid ];
67
68 nativeBuildInputs = [
69 makeWrapper
70 pkg-config
71 ]
72 ++ lib.optionals jitSupport [ llvmPackages.llvm.dev nukeReferences patchelf ];
73
74 enableParallelBuilding = !stdenv'.isDarwin;
75
76 separateDebugInfo = true;
77
78 buildFlags = [ "world" ];
79
80 env.NIX_CFLAGS_COMPILE = "-I${libxml2.dev}/include/libxml2";
81
82 # Otherwise it retains a reference to compiler and fails; see #44767. TODO: better.
83 preConfigure = "CC=${stdenv'.cc.targetPrefix}cc";
84
85 configureFlags = [
86 "--with-openssl"
87 "--with-libxml"
88 "--with-icu"
89 "--sysconfdir=/etc"
90 "--libdir=$(lib)/lib"
91 "--with-system-tzdata=${tzdata}/share/zoneinfo"
92 "--enable-debug"
93 (lib.optionalString enableSystemd "--with-systemd")
94 (if stdenv'.isDarwin then "--with-uuid=e2fs" else "--with-ossp-uuid")
95 ] ++ lib.optionals lz4Enabled [ "--with-lz4" ]
96 ++ lib.optionals zstdEnabled [ "--with-zstd" ]
97 ++ lib.optionals gssSupport [ "--with-gssapi" ]
98 ++ lib.optionals stdenv'.hostPlatform.isRiscV [ "--disable-spinlocks" ]
99 ++ lib.optionals jitSupport [ "--with-llvm" ];
100
101 patches = [
102 (if atLeast "16" then ./patches/disable-normalize_exec_path.patch
103 else ./patches/disable-resolve_symlinks.patch)
104 ./patches/less-is-more.patch
105 ./patches/hardcode-pgxs-path.patch
106 ./patches/specify_pkglibdir_at_runtime.patch
107 ./patches/findstring.patch
108
109 (substituteAll {
110 src = ./locale-binary-path.patch;
111 locale = "${if stdenv.isDarwin then darwin.adv_cmds else lib.getBin stdenv.cc.libc}/bin/locale";
112 })
113
114 ] ++ lib.optionals stdenv'.hostPlatform.isMusl (
115 let
116 self = {
117 "12" = {
118 icu-collations-hack = fetchurl {
119 url = "https://git.alpinelinux.org/aports/plain/testing/postgresql12/icu-collations-hack.patch?id=d5227c91adda59d4e7f55f13468f0314e8869174";
120 hash = "sha256-wuwjvGHArkRNwFo40g3p43W32OrJohretlt6iSRlJKg=";
121 };
122 };
123 "13" = {
124 inherit (self."14") icu-collations-hack;
125 disable-test-collate-icu-utf8 = fetchurl {
126 url = "https://git.alpinelinux.org/aports/plain/main/postgresql13/disable-test-collate.icu.utf8.patch?id=69faa146ec9fff3b981511068f17f9e629d4688b";
127 hash = "sha256-jS/qxezaiaKhkWeMCXwpz1SDJwUWn9tzN0uKaZ3Ph2Y=";
128 };
129 };
130 "14" = {
131 icu-collations-hack = fetchurl {
132 url = "https://git.alpinelinux.org/aports/plain/main/postgresql14/icu-collations-hack.patch?id=56999e6d0265ceff5c5239f85fdd33e146f06cb7";
133 hash = "sha256-wuwjvGHArkRNwFo40g3p43W32OrJohretlt6iSRlJKg=";
134 };
135 disable-test-collate-icu-utf8 = fetchurl {
136 url = "https://git.alpinelinux.org/aports/plain/main/postgresql14/disable-test-collate.icu.utf8.patch?id=56999e6d0265ceff5c5239f85fdd33e146f06cb7";
137 hash = "sha256-jXe23AxnFjEl+TZQm4R7rStk2Leo08ctxMNmu1xr5zM=";
138 };
139 };
140 "15" = {
141 icu-collations-hack = fetchurl {
142 url = "https://git.alpinelinux.org/aports/plain/main/postgresql15/icu-collations-hack.patch?id=f424e934e6d076c4ae065ce45e734aa283eecb9c";
143 hash = "sha256-HgtmhF4OJYU9macGJbTB9PjQi/yW7c3Akm3U0niWs8I=";
144 };
145 };
146 "16" = {
147 icu-collations-hack = fetchurl {
148 url = "https://git.alpinelinux.org/aports/plain/main/postgresql16/icu-collations-hack.patch?id=08a24be262339fd093e641860680944c3590238e";
149 hash = "sha256-+urQdVIlADLdDPeT68XYv5rljhbK8M/7mPZn/cF+FT0=";
150 };
151 };
152 };
153
154 patchesForVersion = self.${lib.versions.major version} or (throw "no musl patches for postgresql ${version}");
155 in
156 lib.attrValues patchesForVersion
157 ) ++ lib.optionals stdenv'.isLinux [
158 (if atLeast "13" then ./patches/socketdir-in-run-13.patch else ./patches/socketdir-in-run.patch)
159 ];
160
161 installTargets = [ "install-world" ];
162
163 LC_ALL = "C";
164
165 postPatch = ''
166 # Hardcode the path to pgxs so pg_config returns the path in $out
167 substituteInPlace "src/common/config_info.c" --replace HARDCODED_PGXS_PATH "$out/lib"
168 '' + lib.optionalString jitSupport ''
169 # Force lookup of jit stuff in $out instead of $lib
170 substituteInPlace src/backend/jit/jit.c --replace pkglib_path \"$out/lib\"
171 substituteInPlace src/backend/jit/llvm/llvmjit.c --replace pkglib_path \"$out/lib\"
172 substituteInPlace src/backend/jit/llvm/llvmjit_inline.cpp --replace pkglib_path \"$out/lib\"
173 '';
174
175 postInstall =
176 ''
177 moveToOutput "lib/pgxs" "$out" # looks strange, but not deleting it
178 moveToOutput "lib/libpgcommon*.a" "$out"
179 moveToOutput "lib/libpgport*.a" "$out"
180 moveToOutput "lib/libecpg*" "$out"
181
182 # Prevent a retained dependency on gcc-wrapper.
183 substituteInPlace "$out/lib/pgxs/src/Makefile.global" --replace ${stdenv'.cc}/bin/ld ld
184
185 if [ -z "''${dontDisableStatic:-}" ]; then
186 # Remove static libraries in case dynamic are available.
187 for i in $out/lib/*.a $lib/lib/*.a; do
188 name="$(basename "$i")"
189 ext="${stdenv'.hostPlatform.extensions.sharedLibrary}"
190 if [ -e "$lib/lib/''${name%.a}$ext" ] || [ -e "''${i%.a}$ext" ]; then
191 rm "$i"
192 fi
193 done
194 fi
195 '' + lib.optionalString jitSupport ''
196 # Move the bitcode and libllvmjit.so library out of $lib; otherwise, every client that
197 # depends on libpq.so will also have libLLVM.so in its closure too, bloating it
198 moveToOutput "lib/bitcode" "$out"
199 moveToOutput "lib/llvmjit*" "$out"
200
201 # In the case of JIT support, prevent a retained dependency on clang-wrapper
202 substituteInPlace "$out/lib/pgxs/src/Makefile.global" --replace ${self.llvmPackages.stdenv.cc}/bin/clang clang
203 nuke-refs $out/lib/llvmjit_types.bc $(find $out/lib/bitcode -type f)
204
205 # Stop out depending on the default output of llvm
206 substituteInPlace $out/lib/pgxs/src/Makefile.global \
207 --replace ${self.llvmPackages.llvm.out}/bin "" \
208 --replace '$(LLVM_BINPATH)/' ""
209
210 # Stop out depending on the -dev output of llvm
211 substituteInPlace $out/lib/pgxs/src/Makefile.global \
212 --replace ${self.llvmPackages.llvm.dev}/bin/llvm-config llvm-config \
213 --replace -I${self.llvmPackages.llvm.dev}/include ""
214
215 ${lib.optionalString (!stdenv'.isDarwin) ''
216 # Stop lib depending on the -dev output of llvm
217 rpath=$(patchelf --print-rpath $out/lib/llvmjit.so)
218 nuke-refs -e $out $out/lib/llvmjit.so
219 # Restore the correct rpath
220 patchelf $out/lib/llvmjit.so --set-rpath "$rpath"
221 ''}
222 '';
223
224 postFixup = lib.optionalString (!stdenv'.isDarwin && stdenv'.hostPlatform.libc == "glibc")
225 ''
226 # initdb needs access to "locale" command from glibc.
227 wrapProgram $out/bin/initdb --prefix PATH ":" ${glibc.bin}/bin
228 '';
229
230 doCheck = !stdenv'.isDarwin;
231 # autodetection doesn't seem to able to find this, but it's there.
232 checkTarget = "check";
233
234 preCheck =
235 # On musl, comment skip the following tests, because they break due to
236 # ! ERROR: could not load library "/build/postgresql-11.5/tmp_install/nix/store/...-postgresql-11.5-lib/lib/libpqwalreceiver.so": Error loading shared library libpq.so.5: No such file or directory (needed by /build/postgresql-11.5/tmp_install/nix/store/...-postgresql-11.5-lib/lib/libpqwalreceiver.so)
237 # See also here:
238 # https://git.alpinelinux.org/aports/tree/main/postgresql/disable-broken-tests.patch?id=6d7d32c12e073a57a9e5946e55f4c1fbb68bd442
239 if stdenv'.hostPlatform.isMusl then ''
240 substituteInPlace src/test/regress/parallel_schedule \
241 --replace "subscription" "" \
242 --replace "object_address" ""
243 '' else null;
244
245 doInstallCheck = false; # needs a running daemon?
246
247 disallowedReferences = [ stdenv'.cc ];
248
249 passthru = let
250 jitToggle = this.override {
251 jitSupport = !jitSupport;
252 this = jitToggle;
253 };
254 in
255 {
256 inherit readline psqlSchema jitSupport;
257
258 withJIT = if jitSupport then this else jitToggle;
259 withoutJIT = if jitSupport then jitToggle else this;
260
261 dlSuffix = if olderThan "16" then ".so" else stdenv.hostPlatform.extensions.sharedLibrary;
262
263 pkgs = let
264 scope = {
265 postgresql = this;
266 stdenv = stdenv';
267 buildPgxExtension = buildPgxExtension.override {
268 stdenv = stdenv';
269 rustPlatform = makeRustPlatform {
270 stdenv = stdenv';
271 inherit rustc cargo;
272 };
273 };
274 };
275 newSelf = self // scope;
276 newSuper = { callPackage = newScope (scope // this.pkgs); };
277 in import ./packages.nix newSelf newSuper;
278
279 withPackages = postgresqlWithPackages {
280 inherit makeWrapper buildEnv;
281 postgresql = this;
282 }
283 this.pkgs;
284
285 tests = {
286 postgresql = nixosTests.postgresql-wal-receiver.${thisAttr};
287 pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
288 } // lib.optionalAttrs jitSupport {
289 postgresql-jit = nixosTests.postgresql-jit.${thisAttr};
290 };
291 } // lib.optionalAttrs jitSupport {
292 inherit (llvmPackages) llvm;
293 };
294
295 meta = with lib; {
296 homepage = "https://www.postgresql.org";
297 description = "A powerful, open source object-relational database system";
298 license = licenses.postgresql;
299 changelog = "https://www.postgresql.org/docs/release/${finalAttrs.version}/";
300 maintainers = with maintainers; [ thoughtpolice danbst globin marsam ivan ma27 ];
301 pkgConfigModules = [ "libecpg" "libecpg_compat" "libpgtypes" "libpq" ];
302 platforms = platforms.unix;
303
304 # JIT support doesn't work with cross-compilation. It is attempted to build LLVM-bytecode
305 # (`%.bc` is the corresponding `make(1)`-rule) for each sub-directory in `backend/` for
306 # the JIT apparently, but with a $(CLANG) that can produce binaries for the build, not the
307 # host-platform.
308 #
309 # I managed to get a cross-build with JIT support working with
310 # `depsBuildBuild = [ llvmPackages.clang ] ++ buildInputs`, but considering that the
311 # resulting LLVM IR isn't platform-independent this doesn't give you much.
312 # In fact, I tried to test the result in a VM-test, but as soon as JIT was used to optimize
313 # a query, postgres would coredump with `Illegal instruction`.
314 broken = jitSupport && (stdenv.hostPlatform != stdenv.buildPlatform);
315 };
316 });
317
318 postgresqlWithPackages = { postgresql, makeWrapper, buildEnv }: pkgs: f: buildEnv {
319 name = "postgresql-and-plugins-${postgresql.version}";
320 paths = f pkgs ++ [
321 postgresql
322 postgresql.lib
323 postgresql.man # in case user installs this into environment
324 ];
325 nativeBuildInputs = [ makeWrapper ];
326
327
328 # We include /bin to ensure the $out/bin directory is created, which is
329 # needed because we'll be removing the files from that directory in postBuild
330 # below. See #22653
331 pathsToLink = ["/" "/bin"];
332
333 # Note: the duplication of executables is about 4MB size.
334 # So a nicer solution was patching postgresql to allow setting the
335 # libdir explicitly.
336 postBuild = ''
337 mkdir -p $out/bin
338 rm $out/bin/{pg_config,postgres,pg_ctl}
339 cp --target-directory=$out/bin ${postgresql}/bin/{postgres,pg_config,pg_ctl}
340 wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
341 '';
342
343 passthru.version = postgresql.version;
344 passthru.psqlSchema = postgresql.psqlSchema;
345 };
346
347 mkPackages = self: {
348 postgresql_12 = self.callPackage generic {
349 version = "12.17";
350 psqlSchema = "12";
351 hash = "sha256-k+jhsjmB1fA8bFdj93soGEwc5NtxlPpGbi7bZdnBxfY=";
352 this = self.postgresql_12;
353 thisAttr = "postgresql_12";
354 inherit self;
355 };
356
357 postgresql_13 = self.callPackage generic {
358 version = "13.13";
359 psqlSchema = "13";
360 hash = "sha256-ivacJZkEeirSRlZ9aOxBMa7xFpVNjD5GnpeJCAs3pHQ=";
361 this = self.postgresql_13;
362 thisAttr = "postgresql_13";
363 inherit self;
364 };
365
366 postgresql_14 = self.callPackage generic {
367 version = "14.10";
368 psqlSchema = "14";
369 hash = "sha256-yZQxxI6dRwsNCrlG6yFBo80ZEwwvtNxLMoSnd07Mg5k=";
370 this = self.postgresql_14;
371 thisAttr = "postgresql_14";
372 inherit self;
373 };
374
375 postgresql_15 = self.callPackage generic {
376 version = "15.5";
377 psqlSchema = "15";
378 hash = "sha256-j1OqldeOuOglNupGtoGHeTtCu6O09lqjQvVAsjybEKY=";
379 this = self.postgresql_15;
380 thisAttr = "postgresql_15";
381 inherit self;
382 };
383
384 postgresql_16 = self.callPackage generic {
385 version = "16.1";
386 psqlSchema = "16";
387 hash = "sha256-zjxNhdGbASH+DT+O8fpgH3GYnob4pm99w61UbdVWT+w=";
388 this = self.postgresql_16;
389 thisAttr = "postgresql_16";
390 inherit self;
391 };
392 };
393
394in self:
395 let packages = mkPackages self; in
396 packages
397 // self.lib.mapAttrs'
398 (attrName: postgres: self.lib.nameValuePair "${attrName}_jit" (postgres.override rec {
399 jitSupport = true;
400 thisAttr = "${attrName}_jit";
401 this = self.${thisAttr};
402 }))
403 packages