1let
2
3 generic =
4 # utils
5 {
6 stdenv,
7 fetchFromGitHub,
8 fetchurl,
9 lib,
10 replaceVars,
11 writeShellScriptBin,
12
13 # source specification
14 hash,
15 muslPatches ? { },
16 rev,
17 version,
18
19 # runtime dependencies
20 darwin,
21 freebsd,
22 glibc,
23 libuuid,
24 libxml2,
25 lz4,
26 openssl,
27 readline,
28 tzdata,
29 zlib,
30 zstd,
31
32 # build dependencies
33 bison,
34 docbook-xsl-nons,
35 docbook_xml_dtd_45,
36 flex,
37 libxslt,
38 makeBinaryWrapper,
39 pkg-config,
40 removeReferencesTo,
41
42 # passthru
43 buildEnv,
44 buildPackages,
45 newScope,
46 nixosTests,
47 postgresqlTestHook,
48 self,
49 stdenvNoCC,
50 testers,
51
52 # bonjour
53 bonjourSupport ? false,
54
55 # GSSAPI
56 gssSupport ? with stdenv.hostPlatform; !isWindows && !isStatic,
57 libkrb5,
58
59 # icu
60 # Building with icu in pkgsStatic gives tons of "undefined reference" errors like this:
61 # /nix/store/452lkaak37d3mzzn3p9ak7aa3wzhdqaj-icu4c-74.2-x86_64-unknown-linux-musl/lib/libicuuc.a(chariter.ao):
62 # (.data.rel.ro._ZTIN6icu_7417CharacterIteratorE[_ZTIN6icu_7417CharacterIteratorE]+0x0):
63 # undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
64 icuSupport ? !stdenv.hostPlatform.isStatic,
65 icu,
66
67 # JIT
68 jitSupport ?
69 stdenv.hostPlatform.canExecute stdenv.buildPlatform
70 # Building with JIT in pkgsStatic fails like this:
71 # fatal error: 'stdio.h' file not found
72 && !stdenv.hostPlatform.isStatic,
73 llvmPackages,
74 nukeReferences,
75 overrideCC,
76
77 # LDAP
78 ldapSupport ? false,
79 openldap,
80
81 # NLS
82 nlsSupport ? false,
83 gettext,
84
85 # PAM
86 pamSupport ?
87 lib.meta.availableOn stdenv.hostPlatform linux-pam
88 # Building with linux-pam in pkgsStatic gives a few "undefined reference" errors like this:
89 # /nix/store/3s55icpsbc36sgn7sa8q3qq4z6al6rlr-linux-pam-static-x86_64-unknown-linux-musl-1.6.1/lib/libpam.a(pam_audit.o):
90 # in function `pam_modutil_audit_write':(.text+0x571):
91 # undefined reference to `audit_close'
92 && !stdenv.hostPlatform.isStatic,
93 linux-pam,
94
95 # PL/Perl
96 perlSupport ?
97 lib.meta.availableOn stdenv.hostPlatform perl
98 # Building with perl in pkgsStatic gives this error:
99 # configure: error: cannot build PL/Perl because libperl is not a shared library
100 && !stdenv.hostPlatform.isStatic
101 # configure tries to call the perl executable for the version
102 && stdenv.buildPlatform.canExecute stdenv.hostPlatform,
103 perl,
104
105 # PL/Python
106 pythonSupport ?
107 lib.meta.availableOn stdenv.hostPlatform python3
108 # Building with python in pkgsStatic gives this error:
109 # checking how to link an embedded Python application... configure: error: could not find shared library for Python
110 && !stdenv.hostPlatform.isStatic
111 # configure tries to call the python executable
112 && stdenv.buildPlatform.canExecute stdenv.hostPlatform,
113 python3,
114
115 # PL/Tcl
116 tclSupport ?
117 lib.meta.availableOn stdenv.hostPlatform tcl
118 # tcl is broken in pkgsStatic
119 && !stdenv.hostPlatform.isStatic
120 # configure fails with:
121 # configure: error: file 'tclConfig.sh' is required for Tcl
122 && stdenv.buildPlatform.canExecute stdenv.hostPlatform,
123 tcl,
124
125 # SELinux
126 selinuxSupport ? false,
127 libselinux,
128
129 # Systemd
130 systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemdLibs,
131 systemdLibs,
132 }@args:
133 let
134 atLeast = lib.versionAtLeast version;
135 olderThan = lib.versionOlder version;
136 lz4Enabled = atLeast "14";
137 zstdEnabled = atLeast "15";
138
139 dlSuffix = if olderThan "16" then ".so" else stdenv.hostPlatform.extensions.sharedLibrary;
140
141 stdenv' =
142 if !stdenv.cc.isClang then
143 overrideCC llvmPackages.stdenv (
144 llvmPackages.stdenv.cc.override {
145 # LLVM bintools are not used by default, but are needed to make -flto work below.
146 bintools = llvmPackages.bintools;
147 }
148 )
149 else
150 stdenv;
151 in
152 stdenv'.mkDerivation (finalAttrs: {
153 inherit version;
154 pname = "postgresql";
155
156 src = fetchFromGitHub {
157 owner = "postgres";
158 repo = "postgres";
159 # rev, not tag, on purpose: allows updating when new versions
160 # are "stamped" a few days before release (tag).
161 inherit hash rev;
162 };
163
164 __structuredAttrs = true;
165
166 outputs =
167 [
168 "out"
169 "dev"
170 "doc"
171 "lib"
172 "man"
173 ]
174 ++ lib.optionals jitSupport [ "jit" ]
175 ++ lib.optionals perlSupport [ "plperl" ]
176 ++ lib.optionals pythonSupport [ "plpython3" ]
177 ++ lib.optionals tclSupport [ "pltcl" ];
178
179 outputChecks =
180 {
181 out = {
182 disallowedReferences = [
183 "dev"
184 "doc"
185 "man"
186 ] ++ lib.optionals jitSupport [ "jit" ];
187 disallowedRequisites = [
188 stdenv'.cc
189 llvmPackages.llvm.out
190 llvmPackages.llvm.lib
191 ] ++ (map lib.getDev (builtins.filter (drv: drv ? "dev") finalAttrs.buildInputs));
192 };
193
194 lib = {
195 disallowedReferences = [
196 "out"
197 "dev"
198 "doc"
199 "man"
200 ] ++ lib.optionals jitSupport [ "jit" ];
201 disallowedRequisites = [
202 stdenv'.cc
203 llvmPackages.llvm.out
204 llvmPackages.llvm.lib
205 ] ++ (map lib.getDev (builtins.filter (drv: drv ? "dev") finalAttrs.buildInputs));
206 };
207
208 doc = {
209 disallowedReferences = [
210 "out"
211 "dev"
212 "man"
213 ] ++ lib.optionals jitSupport [ "jit" ];
214 };
215
216 man = {
217 disallowedReferences = [
218 "out"
219 "dev"
220 "doc"
221 ] ++ lib.optionals jitSupport [ "jit" ];
222 };
223 }
224 // lib.optionalAttrs jitSupport {
225 jit = {
226 disallowedReferences = [
227 "dev"
228 "doc"
229 "man"
230 ];
231 disallowedRequisites = [
232 stdenv'.cc
233 llvmPackages.llvm.out
234 ] ++ (map lib.getDev (builtins.filter (drv: drv ? "dev") finalAttrs.buildInputs));
235 };
236 };
237
238 strictDeps = true;
239
240 buildInputs =
241 [
242 zlib
243 readline
244 openssl
245 libxml2
246 libuuid
247 ]
248 ++ lib.optionals icuSupport [ icu ]
249 ++ lib.optionals jitSupport [ llvmPackages.llvm ]
250 ++ lib.optionals lz4Enabled [ lz4 ]
251 ++ lib.optionals zstdEnabled [ zstd ]
252 ++ lib.optionals systemdSupport [ systemdLibs ]
253 ++ lib.optionals gssSupport [ libkrb5 ]
254 ++ lib.optionals pamSupport [ linux-pam ]
255 ++ lib.optionals perlSupport [ perl ]
256 ++ lib.optionals ldapSupport [ openldap ]
257 ++ lib.optionals selinuxSupport [ libselinux ]
258 ++ lib.optionals nlsSupport [ gettext ];
259
260 nativeBuildInputs =
261 [
262 bison
263 docbook-xsl-nons
264 docbook_xml_dtd_45
265 flex
266 libxml2
267 libxslt
268 makeBinaryWrapper
269 perl
270 pkg-config
271 removeReferencesTo
272 ]
273 ++ lib.optionals jitSupport [
274 llvmPackages.llvm.dev
275 nukeReferences
276 ];
277
278 enableParallelBuilding = true;
279
280 separateDebugInfo = true;
281
282 buildFlags = [ "world" ];
283
284 env =
285 {
286 # libpgcommon.a and libpgport.a contain all paths returned by pg_config and are linked
287 # into all binaries. However, almost no binaries actually use those paths. The following
288 # flags will remove unused sections from all shared libraries and binaries - including
289 # those paths. This avoids a lot of circular dependency problems with different outputs,
290 # and allows splitting them cleanly.
291 CFLAGS = "-fdata-sections -ffunction-sections -flto";
292
293 # This flag was introduced upstream in:
294 # https://github.com/postgres/postgres/commit/b6c7cfac88c47a9194d76f3d074129da3c46545a
295 # It causes errors when linking against libpq.a in pkgsStatic:
296 # undefined reference to `pg_encoding_to_char'
297 # Unsetting the flag fixes it. The upstream reasoning to introduce it is about the risk
298 # to have initdb load a libpq.so from a different major version and how to avoid that.
299 # This doesn't apply to us with Nix.
300 NIX_CFLAGS_COMPILE = "-UUSE_PRIVATE_ENCODING_FUNCS";
301 }
302 // lib.optionalAttrs perlSupport { PERL = lib.getExe perl; }
303 // lib.optionalAttrs pythonSupport { PYTHON = lib.getExe python3; }
304 // lib.optionalAttrs tclSupport { TCLSH = "${lib.getBin tcl}/bin/tclsh"; };
305
306 configureFlags =
307 let
308 inherit (lib) withFeature;
309 in
310 [
311 "--with-openssl"
312 "--with-libxml"
313 (withFeature icuSupport "icu")
314 "--sysconfdir=/etc"
315 "--with-system-tzdata=${tzdata}/share/zoneinfo"
316 "--enable-debug"
317 (lib.optionalString systemdSupport "--with-systemd")
318 (if stdenv.hostPlatform.isFreeBSD then "--with-uuid=bsd" else "--with-uuid=e2fs")
319 (withFeature perlSupport "perl")
320 ]
321 ++ lib.optionals lz4Enabled [ "--with-lz4" ]
322 ++ lib.optionals zstdEnabled [ "--with-zstd" ]
323 ++ lib.optionals gssSupport [ "--with-gssapi" ]
324 ++ lib.optionals pythonSupport [ "--with-python" ]
325 ++ lib.optionals jitSupport [ "--with-llvm" ]
326 ++ lib.optionals pamSupport [ "--with-pam" ]
327 # This can be removed once v17 is removed. v18+ ships with it.
328 ++ lib.optionals (stdenv'.hostPlatform.isDarwin && atLeast "16" && olderThan "18") [
329 "LDFLAGS_EX_BE=-Wl,-export_dynamic"
330 ]
331 # some version of this flag is required in all cross configurations
332 # since it cannot be automatically detected
333 ++
334 lib.optionals
335 (
336 (!stdenv'.hostPlatform.isDarwin)
337 && (!(stdenv'.buildPlatform.canExecute stdenv.hostPlatform))
338 && atLeast "16"
339 )
340 [
341 "LDFLAGS_EX_BE=-Wl,--export-dynamic"
342 ]
343 ++ lib.optionals ldapSupport [ "--with-ldap" ]
344 ++ lib.optionals tclSupport [ "--with-tcl" ]
345 ++ lib.optionals selinuxSupport [ "--with-selinux" ]
346 ++ lib.optionals nlsSupport [ "--enable-nls" ]
347 ++ lib.optionals bonjourSupport [ "--with-bonjour" ];
348
349 patches =
350 [
351 (
352 if atLeast "16" then
353 ./patches/relative-to-symlinks-16+.patch
354 else
355 ./patches/relative-to-symlinks.patch
356 )
357 (
358 if atLeast "15" then
359 ./patches/empty-pg-config-view-15+.patch
360 else
361 ./patches/empty-pg-config-view.patch
362 )
363 ./patches/less-is-more.patch
364 ./patches/paths-for-split-outputs.patch
365 ./patches/paths-with-postgresql-suffix.patch
366
367 (replaceVars ./patches/locale-binary-path.patch {
368 locale = "${
369 if stdenv.hostPlatform.isDarwin then
370 darwin.adv_cmds
371 else if stdenv.hostPlatform.isFreeBSD then
372 freebsd.locale
373 else
374 lib.getBin stdenv.cc.libc
375 }/bin/locale";
376 })
377 ]
378 ++ lib.optionals stdenv'.hostPlatform.isMusl (
379 # Using fetchurl instead of fetchpatch on purpose: https://github.com/NixOS/nixpkgs/issues/240141
380 map fetchurl (lib.attrValues muslPatches)
381 )
382 ++ lib.optionals stdenv'.hostPlatform.isLinux [
383 ./patches/socketdir-in-run-13+.patch
384 ]
385 ++ lib.optionals (stdenv'.hostPlatform.isDarwin && olderThan "16") [
386 ./patches/export-dynamic-darwin-15-.patch
387 ];
388
389 installTargets = [ "install-world" ];
390
391 postPatch =
392 ''
393 substituteInPlace "src/Makefile.global.in" --subst-var out
394 substituteInPlace "src/common/config_info.c" --subst-var dev
395 cat ${./pg_config.env.mk} >> src/common/Makefile
396 ''
397 # This check was introduced upstream to prevent calls to "exit" inside libpq.
398 # However, this doesn't work reliably with static linking, see this and following:
399 # https://postgr.es/m/flat/20210703001639.GB2374652%40rfd.leadboat.com#52584ca4bd3cb9dac376f3158c419f97
400 # Thus, disable the check entirely, as it would currently fail with this:
401 # > libpq.so.5.17: U atexit
402 # > libpq.so.5.17: U pthread_exit
403 # > libpq must not be calling any function which invokes exit
404 # Don't mind the fact that this checks libpq.**so** in pkgsStatic - that's correct, since PostgreSQL
405 # still needs a shared library internally.
406 + lib.optionalString (atLeast "15" && stdenv'.hostPlatform.isStatic) ''
407 substituteInPlace src/interfaces/libpq/Makefile \
408 --replace-fail "echo 'libpq must not be calling any function which invokes exit'; exit 1;" "echo;"
409 '';
410
411 postInstall =
412 ''
413 moveToOutput "bin/ecpg" "$dev"
414 moveToOutput "lib/pgxs" "$dev"
415 ''
416 + lib.optionalString (stdenv'.buildPlatform.canExecute stdenv'.hostPlatform) ''
417 mkdir -p "$dev/nix-support"
418 "$out/bin/pg_config" > "$dev/nix-support/pg_config.expected"
419 ''
420 + ''
421 rm "$out/bin/pg_config"
422 make -C src/common pg_config.env
423 install -D src/common/pg_config.env "$dev/nix-support/pg_config.env"
424
425 # postgres exposes external symbols get_pkginclude_path and similar. Those
426 # can't be stripped away by --gc-sections/LTO, because they could theoretically
427 # be used by dynamically loaded modules / extensions. To avoid circular dependencies,
428 # references to -dev, -doc and -man are removed here. References to -lib must be kept,
429 # because there is a realistic use-case for extensions to locate the /lib directory to
430 # load other shared modules.
431 remove-references-to -t "$dev" -t "$doc" -t "$man" "$out/bin/postgres"
432 ''
433 + lib.optionalString (!stdenv'.hostPlatform.isStatic) ''
434 if [ -z "''${dontDisableStatic:-}" ]; then
435 # Remove static libraries in case dynamic are available.
436 for i in $lib/lib/*.a; do
437 name="$(basename "$i")"
438 ext="${stdenv'.hostPlatform.extensions.sharedLibrary}"
439 if [ -e "$lib/lib/''${name%.a}$ext" ] || [ -e "''${i%.a}$ext" ]; then
440 rm "$i"
441 fi
442 done
443 fi
444 ''
445 + ''
446 # The remaining static libraries are libpgcommon.a, libpgport.a and related.
447 # Those are only used when building e.g. extensions, so go to $dev.
448 moveToOutput "lib/*.a" "$dev"
449 ''
450 + lib.optionalString jitSupport ''
451 # In the case of JIT support, prevent useless dependencies on header files
452 find "$out/lib" -iname '*.bc' -type f -exec nuke-refs '{}' +
453
454 # Stop lib depending on the -dev output of llvm
455 remove-references-to -t ${llvmPackages.llvm.dev} "$out/lib/llvmjit${dlSuffix}"
456
457 moveToOutput "lib/bitcode" "$jit"
458 moveToOutput "lib/llvmjit*" "$jit"
459 ''
460 + lib.optionalString stdenv'.hostPlatform.isDarwin ''
461 # The darwin specific Makefile for PGXS contains a reference to the postgres
462 # binary. Some extensions (here: postgis), which are able to set bindir correctly
463 # to their own output for installation, will then fail to find "postgres" during linking.
464 substituteInPlace "$dev/lib/pgxs/src/Makefile.port" \
465 --replace-fail '-bundle_loader $(bindir)/postgres' "-bundle_loader $out/bin/postgres"
466 ''
467 + lib.optionalString perlSupport ''
468 moveToOutput "lib/*plperl*" "$plperl"
469 moveToOutput "share/postgresql/extension/*plperl*" "$plperl"
470 ''
471 + lib.optionalString pythonSupport ''
472 moveToOutput "lib/*plpython3*" "$plpython3"
473 moveToOutput "share/postgresql/extension/*plpython3*" "$plpython3"
474 ''
475 + lib.optionalString tclSupport ''
476 moveToOutput "lib/*pltcl*" "$pltcl"
477 moveToOutput "share/postgresql/extension/*pltcl*" "$pltcl"
478 '';
479
480 postFixup = lib.optionalString stdenv'.hostPlatform.isGnu ''
481 # initdb needs access to "locale" command from glibc.
482 wrapProgram $out/bin/initdb --prefix PATH ":" ${glibc.bin}/bin
483 '';
484
485 # Running tests as "install check" to work around SIP issue on macOS:
486 # https://www.postgresql.org/message-id/flat/4D8E1BC5-BBCF-4B19-8226-359201EA8305%40gmail.com
487 # Also see <nixpkgs>/doc/stdenv/platform-notes.chapter.md
488 doCheck = false;
489 doInstallCheck =
490 !(stdenv'.hostPlatform.isStatic)
491 &&
492 # Tests currently can't be run on darwin, because of a Nix bug:
493 # https://github.com/NixOS/nix/issues/12548
494 # https://git.lix.systems/lix-project/lix/issues/691
495 # The error appears as this in the initdb logs:
496 # FATAL: could not create shared memory segment: Cannot allocate memory
497 # Don't let yourself be fooled when trying to remove this condition: Running
498 # the tests works fine most of the time. But once the tests (or any package using
499 # postgresqlTestHook) fails on the same machine for a few times, enough IPC objects
500 # will be stuck around, and any future builds with the tests enabled *will* fail.
501 !(stdenv'.hostPlatform.isDarwin)
502 &&
503 # Regression tests currently fail in pkgsMusl because of a difference in EXPLAIN output.
504 !(stdenv'.hostPlatform.isMusl);
505 installCheckTarget = "check-world";
506
507 passthru =
508 let
509 this = self.callPackage generic args;
510 in
511 {
512 inherit dlSuffix;
513
514 psqlSchema = lib.versions.major version;
515
516 withJIT = this.withPackages (_: [ this.jit ]);
517 withoutJIT = this;
518
519 pkgs =
520 let
521 scope = {
522 inherit
523 jitSupport
524 pythonSupport
525 perlSupport
526 tclSupport
527 ;
528 inherit (llvmPackages) llvm;
529 postgresql = this;
530 stdenv = stdenv';
531 postgresqlTestExtension = newSuper.callPackage ./postgresqlTestExtension.nix { };
532 postgresqlBuildExtension = newSuper.callPackage ./postgresqlBuildExtension.nix { };
533 };
534 newSelf = self // scope;
535 newSuper = {
536 callPackage = newScope (scope // this.pkgs);
537 };
538 in
539 import ./ext.nix newSelf newSuper;
540
541 withPackages = postgresqlWithPackages {
542 inherit buildEnv lib makeBinaryWrapper;
543 postgresql = this;
544 };
545
546 pg_config = buildPackages.callPackage ./pg_config.nix { inherit (finalAttrs) finalPackage; };
547
548 tests =
549 {
550 postgresql = nixosTests.postgresql.postgresql.passthru.override finalAttrs.finalPackage;
551 postgresql-tls-client-cert = nixosTests.postgresql.postgresql-tls-client-cert.passthru.override finalAttrs.finalPackage;
552 postgresql-wal-receiver = nixosTests.postgresql.postgresql-wal-receiver.passthru.override finalAttrs.finalPackage;
553 pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
554 }
555 // lib.optionalAttrs jitSupport {
556 postgresql-jit = nixosTests.postgresql.postgresql-jit.passthru.override finalAttrs.finalPackage;
557 };
558 };
559
560 meta = with lib; {
561 homepage = "https://www.postgresql.org";
562 description = "Powerful, open source object-relational database system";
563 license = licenses.postgresql;
564 changelog = "https://www.postgresql.org/docs/release/${finalAttrs.version}/";
565 teams = [ teams.postgres ];
566 pkgConfigModules = [
567 "libecpg"
568 "libecpg_compat"
569 "libpgtypes"
570 "libpq"
571 ];
572 platforms = platforms.unix;
573
574 # JIT support doesn't work with cross-compilation. It is attempted to build LLVM-bytecode
575 # (`%.bc` is the corresponding `make(1)`-rule) for each sub-directory in `backend/` for
576 # the JIT apparently, but with a $(CLANG) that can produce binaries for the build, not the
577 # host-platform.
578 #
579 # I managed to get a cross-build with JIT support working with
580 # `depsBuildBuild = [ llvmPackages.clang ] ++ buildInputs`, but considering that the
581 # resulting LLVM IR isn't platform-independent this doesn't give you much.
582 # In fact, I tried to test the result in a VM-test, but as soon as JIT was used to optimize
583 # a query, postgres would coredump with `Illegal instruction`.
584 #
585 # Note: This is "host canExecute build" on purpose, since this is about the LLVM that is called
586 # to do JIT at **runtime**.
587 broken = jitSupport && !stdenv.hostPlatform.canExecute stdenv.buildPlatform;
588 };
589 });
590
591 postgresqlWithPackages =
592 {
593 postgresql,
594 buildEnv,
595 lib,
596 makeBinaryWrapper,
597 }:
598 f:
599 let
600 installedExtensions = f postgresql.pkgs;
601 in
602 buildEnv {
603 name = "${postgresql.pname}-and-plugins-${postgresql.version}";
604 paths = installedExtensions ++ [
605 postgresql
606 postgresql.man # in case user installs this into environment
607 ];
608
609 pathsToLink = [
610 "/"
611 "/bin"
612 ];
613
614 nativeBuildInputs = [ makeBinaryWrapper ];
615 postBuild =
616 let
617 args = lib.concatMap (ext: ext.wrapperArgs or [ ]) installedExtensions;
618 in
619 ''
620 wrapProgram "$out/bin/postgres" ${lib.concatStringsSep " " args}
621 '';
622
623 passthru = {
624 inherit installedExtensions;
625 inherit (postgresql)
626 pg_config
627 pkgs
628 psqlSchema
629 version
630 ;
631
632 withJIT = postgresqlWithPackages {
633 inherit
634 buildEnv
635 lib
636 makeBinaryWrapper
637 postgresql
638 ;
639 } (_: installedExtensions ++ [ postgresql.jit ]);
640 withoutJIT = postgresqlWithPackages {
641 inherit
642 buildEnv
643 lib
644 makeBinaryWrapper
645 postgresql
646 ;
647 } (_: lib.remove postgresql.jit installedExtensions);
648
649 withPackages =
650 f':
651 postgresqlWithPackages {
652 inherit
653 buildEnv
654 lib
655 makeBinaryWrapper
656 postgresql
657 ;
658 } (ps: installedExtensions ++ f' ps);
659 };
660 };
661
662in
663# passed by <major>.nix
664versionArgs:
665# passed by default.nix
666{ self, ... }@defaultArgs:
667self.callPackage generic (defaultArgs // versionArgs)