1let
2
3 generic =
4 # dependencies
5 {
6 stdenv,
7 lib,
8 fetchurl,
9 fetchpatch,
10 makeWrapper,
11 glibc,
12 zlib,
13 readline,
14 openssl,
15 icu,
16 lz4,
17 zstd,
18 systemdLibs,
19 libuuid,
20 pkg-config,
21 libxml2,
22 tzdata,
23 libkrb5,
24 substituteAll,
25 darwin,
26 linux-pam,
27 bison,
28 flex,
29 perl,
30 docbook_xml_dtd_45,
31 docbook-xsl-nons,
32 libxslt,
33
34 removeReferencesTo,
35 writeShellScriptBin,
36
37 systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemdLibs,
38 gssSupport ? with stdenv.hostPlatform; !isWindows && !isStatic,
39
40 # for postgresql.pkgs
41 self,
42 newScope,
43 buildEnv,
44 stdenvNoCC,
45 postgresqlTestHook,
46
47 # source specification
48 version,
49 hash,
50 muslPatches ? { },
51
52 # for tests
53 testers,
54 nixosTests,
55
56 # JIT
57 jitSupport,
58 nukeReferences,
59 llvmPackages,
60 overrideCC,
61
62 # PL/Python
63 pythonSupport ? false,
64 python3,
65 }@args:
66 let
67 atLeast = lib.versionAtLeast version;
68 olderThan = lib.versionOlder version;
69 lz4Enabled = atLeast "14";
70 zstdEnabled = atLeast "15";
71
72 dlSuffix = if olderThan "16" then ".so" else stdenv.hostPlatform.extensions.sharedLibrary;
73
74 pname = "postgresql";
75
76 stdenv' =
77 if jitSupport && !stdenv.cc.isClang then
78 overrideCC llvmPackages.stdenv (
79 llvmPackages.stdenv.cc.override {
80 # LLVM bintools are not used by default, but are needed to make -flto work below.
81 bintools = llvmPackages.bintools;
82 }
83 )
84 else
85 stdenv;
86
87 pg_config = writeShellScriptBin "pg_config" (builtins.readFile ./pg_config.sh);
88 in
89 stdenv'.mkDerivation (finalAttrs: {
90 inherit version;
91 pname = pname + lib.optionalString jitSupport "-jit";
92
93 src = fetchurl {
94 url = "mirror://postgresql/source/v${version}/${pname}-${version}.tar.bz2";
95 inherit hash;
96 };
97
98 __structuredAttrs = true;
99
100 hardeningEnable = lib.optionals (!stdenv'.cc.isClang) [ "pie" ];
101
102 outputs = [
103 "out"
104 "dev"
105 "doc"
106 "lib"
107 "man"
108 ];
109 outputChecks.out = {
110 disallowedReferences = [
111 "dev"
112 "doc"
113 "man"
114 ];
115 disallowedRequisites =
116 [
117 stdenv'.cc
118 ]
119 ++ (map lib.getDev (builtins.filter (drv: drv ? "dev") finalAttrs.buildInputs))
120 ++ lib.optionals jitSupport [
121 llvmPackages.llvm.out
122 ];
123 };
124 outputChecks.lib = {
125 disallowedReferences = [
126 "out"
127 "dev"
128 "doc"
129 "man"
130 ];
131 disallowedRequisites =
132 [
133 stdenv'.cc
134 ]
135 ++ (map lib.getDev (builtins.filter (drv: drv ? "dev") finalAttrs.buildInputs))
136 ++ lib.optionals jitSupport [
137 llvmPackages.llvm.out
138 ];
139 };
140
141 buildInputs =
142 [
143 zlib
144 readline
145 openssl
146 (libxml2.override { enableHttp = true; })
147 icu
148 libuuid
149 ]
150 ++ lib.optionals jitSupport [ llvmPackages.llvm ]
151 ++ lib.optionals lz4Enabled [ lz4 ]
152 ++ lib.optionals zstdEnabled [ zstd ]
153 ++ lib.optionals systemdSupport [ systemdLibs ]
154 ++ lib.optionals pythonSupport [ python3 ]
155 ++ lib.optionals gssSupport [ libkrb5 ]
156 ++ lib.optionals stdenv'.hostPlatform.isLinux [ linux-pam ];
157
158 nativeBuildInputs =
159 [
160 makeWrapper
161 pkg-config
162 removeReferencesTo
163 ]
164 ++ lib.optionals jitSupport [
165 llvmPackages.llvm.dev
166 nukeReferences
167 ]
168 ++ lib.optionals (atLeast "17") [
169 bison
170 flex
171 perl
172 docbook_xml_dtd_45
173 docbook-xsl-nons
174 libxslt
175 ];
176
177 enableParallelBuilding = true;
178
179 separateDebugInfo = true;
180
181 buildFlags = [ "world" ];
182
183 # libpgcommon.a and libpgport.a contain all paths returned by pg_config and are linked
184 # into all binaries. However, almost no binaries actually use those paths. The following
185 # flags will remove unused sections from all shared libraries and binaries - including
186 # those paths. This avoids a lot of circular dependency problems with different outputs,
187 # and allows splitting them cleanly.
188 env.CFLAGS =
189 "-fdata-sections -ffunction-sections"
190 + (if stdenv'.cc.isClang then " -flto" else " -fmerge-constants -Wl,--gc-sections");
191
192 configureFlags =
193 [
194 "--with-openssl"
195 "--with-libxml"
196 "--with-icu"
197 "--sysconfdir=/etc"
198 "--with-system-tzdata=${tzdata}/share/zoneinfo"
199 "--enable-debug"
200 (lib.optionalString systemdSupport "--with-systemd")
201 "--with-uuid=e2fs"
202 ]
203 ++ lib.optionals lz4Enabled [ "--with-lz4" ]
204 ++ lib.optionals zstdEnabled [ "--with-zstd" ]
205 ++ lib.optionals gssSupport [ "--with-gssapi" ]
206 ++ lib.optionals pythonSupport [ "--with-python" ]
207 ++ lib.optionals jitSupport [ "--with-llvm" ]
208 ++ lib.optionals stdenv'.hostPlatform.isLinux [ "--with-pam" ]
209 # This could be removed once the upstream issue is resolved:
210 # https://postgr.es/m/flat/427c7c25-e8e1-4fc5-a1fb-01ceff185e5b%40technowledgy.de
211 ++ lib.optionals (stdenv'.hostPlatform.isDarwin && atLeast "16") [
212 "LDFLAGS_EX_BE=-Wl,-export_dynamic"
213 ]
214 ++ lib.optionals (atLeast "17") [ "--without-perl" ];
215
216 patches =
217 [
218 (
219 if atLeast "16" then
220 ./patches/relative-to-symlinks-16+.patch
221 else
222 ./patches/relative-to-symlinks.patch
223 )
224 (
225 if atLeast "15" then
226 ./patches/empty-pg-config-view-15+.patch
227 else
228 ./patches/empty-pg-config-view.patch
229 )
230 ./patches/less-is-more.patch
231 ./patches/paths-for-split-outputs.patch
232 ./patches/paths-with-postgresql-suffix.patch
233
234 (substituteAll {
235 src = ./patches/locale-binary-path.patch;
236 locale = "${
237 if stdenv.hostPlatform.isDarwin then darwin.adv_cmds else lib.getBin stdenv.cc.libc
238 }/bin/locale";
239 })
240 ]
241 ++ lib.optionals stdenv'.hostPlatform.isMusl (
242 # Using fetchurl instead of fetchpatch on purpose: https://github.com/NixOS/nixpkgs/issues/240141
243 map fetchurl (lib.attrValues muslPatches)
244 )
245 ++ lib.optionals stdenv'.hostPlatform.isLinux [
246 ./patches/socketdir-in-run-13+.patch
247 ]
248 ++ lib.optionals (stdenv'.hostPlatform.isDarwin && olderThan "16") [
249 ./patches/export-dynamic-darwin-15-.patch
250 ];
251
252 installTargets = [ "install-world" ];
253
254 postPatch = ''
255 substituteInPlace "src/Makefile.global.in" --subst-var out
256 # Hardcode the path to pgxs so pg_config returns the path in $dev
257 substituteInPlace "src/common/config_info.c" --subst-var dev
258 '';
259
260 postInstall =
261 ''
262 moveToOutput "bin/ecpg" "$dev"
263 moveToOutput "lib/pgxs" "$dev"
264
265 # Pretend pg_config is located in $out/bin to return correct paths, but
266 # actually have it in -dev to avoid pulling in all other outputs. See the
267 # pg_config.sh script's comments for details.
268 moveToOutput "bin/pg_config" "$dev"
269 install -c -m 755 "${pg_config}"/bin/pg_config "$out/bin/pg_config"
270 wrapProgram "$dev/bin/pg_config" --argv0 "$out/bin/pg_config"
271
272 # postgres exposes external symbols get_pkginclude_path and similar. Those
273 # can't be stripped away by --gc-sections/LTO, because they could theoretically
274 # be used by dynamically loaded modules / extensions. To avoid circular dependencies,
275 # references to -dev, -doc and -man are removed here. References to -lib must be kept,
276 # because there is a realistic use-case for extensions to locate the /lib directory to
277 # load other shared modules.
278 remove-references-to -t "$dev" -t "$doc" -t "$man" "$out/bin/postgres"
279
280 if [ -z "''${dontDisableStatic:-}" ]; then
281 # Remove static libraries in case dynamic are available.
282 for i in $lib/lib/*.a; do
283 name="$(basename "$i")"
284 ext="${stdenv'.hostPlatform.extensions.sharedLibrary}"
285 if [ -e "$lib/lib/''${name%.a}$ext" ] || [ -e "''${i%.a}$ext" ]; then
286 rm "$i"
287 fi
288 done
289 fi
290 # The remaining static libraries are libpgcommon.a, libpgport.a and related.
291 # Those are only used when building e.g. extensions, so go to $dev.
292 moveToOutput "lib/*.a" "$dev"
293 ''
294 + lib.optionalString jitSupport ''
295 # In the case of JIT support, prevent useless dependencies on header files
296 find "$out/lib" -iname '*.bc' -type f -exec nuke-refs '{}' +
297
298 # Stop lib depending on the -dev output of llvm
299 remove-references-to -t ${llvmPackages.llvm.dev} "$out/lib/llvmjit${dlSuffix}"
300 '';
301
302 postFixup = lib.optionalString stdenv'.hostPlatform.isGnu ''
303 # initdb needs access to "locale" command from glibc.
304 wrapProgram $out/bin/initdb --prefix PATH ":" ${glibc.bin}/bin
305 '';
306
307 doCheck = !stdenv'.hostPlatform.isDarwin;
308 # autodetection doesn't seem to able to find this, but it's there.
309 checkTarget = "check-world";
310
311 passthru =
312 let
313 this = self.callPackage generic args;
314 jitToggle = this.override {
315 jitSupport = !jitSupport;
316 };
317 in
318 {
319 inherit dlSuffix;
320
321 psqlSchema = lib.versions.major version;
322
323 withJIT = if jitSupport then this else jitToggle;
324 withoutJIT = if jitSupport then jitToggle else this;
325
326 pkgs =
327 let
328 scope = {
329 inherit jitSupport;
330 inherit (llvmPackages) llvm;
331 postgresql = this;
332 stdenv = stdenv';
333 postgresqlTestExtension =
334 {
335 finalPackage,
336 withPackages ? [ ],
337 ...
338 }@extraArgs:
339 stdenvNoCC.mkDerivation (
340 {
341 name = "${finalPackage.name}-test-extension";
342 dontUnpack = true;
343 doCheck = true;
344 nativeCheckInputs = [
345 postgresqlTestHook
346 (this.withPackages (ps: [ finalPackage ] ++ (map (p: ps."${p}") withPackages)))
347 ];
348 failureHook = "postgresqlStop";
349 postgresqlTestUserOptions = "LOGIN SUPERUSER";
350 passAsFile = [ "sql" ];
351 checkPhase = ''
352 runHook preCheck
353 psql -a -v ON_ERROR_STOP=1 -f "$sqlPath"
354 runHook postCheck
355 '';
356 installPhase = "touch $out";
357 }
358 // extraArgs
359 );
360 buildPostgresqlExtension = newSuper.callPackage ./buildPostgresqlExtension.nix { };
361 };
362 newSelf = self // scope;
363 newSuper = {
364 callPackage = newScope (scope // this.pkgs);
365 };
366 in
367 import ./ext newSelf newSuper;
368
369 withPackages = postgresqlWithPackages {
370 inherit buildEnv;
371 postgresql = this;
372 };
373
374 tests =
375 {
376 postgresql = nixosTests.postgresql.postgresql.passthru.override finalAttrs.finalPackage;
377 postgresql-tls-client-cert = nixosTests.postgresql.postgresql-tls-client-cert.passthru.override finalAttrs.finalPackage;
378 postgresql-wal-receiver = nixosTests.postgresql.postgresql-wal-receiver.passthru.override finalAttrs.finalPackage;
379 pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
380 }
381 // lib.optionalAttrs jitSupport {
382 postgresql-jit = nixosTests.postgresql.postgresql-jit.passthru.override finalAttrs.finalPackage;
383 };
384 };
385
386 meta = with lib; {
387 homepage = "https://www.postgresql.org";
388 description = "Powerful, open source object-relational database system";
389 license = licenses.postgresql;
390 changelog = "https://www.postgresql.org/docs/release/${finalAttrs.version}/";
391 maintainers = with maintainers; teams.postgres.members;
392 pkgConfigModules = [
393 "libecpg"
394 "libecpg_compat"
395 "libpgtypes"
396 "libpq"
397 ];
398 platforms = platforms.unix;
399
400 # JIT support doesn't work with cross-compilation. It is attempted to build LLVM-bytecode
401 # (`%.bc` is the corresponding `make(1)`-rule) for each sub-directory in `backend/` for
402 # the JIT apparently, but with a $(CLANG) that can produce binaries for the build, not the
403 # host-platform.
404 #
405 # I managed to get a cross-build with JIT support working with
406 # `depsBuildBuild = [ llvmPackages.clang ] ++ buildInputs`, but considering that the
407 # resulting LLVM IR isn't platform-independent this doesn't give you much.
408 # In fact, I tried to test the result in a VM-test, but as soon as JIT was used to optimize
409 # a query, postgres would coredump with `Illegal instruction`.
410 broken = jitSupport && !stdenv.hostPlatform.canExecute stdenv.buildPlatform;
411 };
412 });
413
414 postgresqlWithPackages =
415 { postgresql, buildEnv }:
416 f:
417 let
418 installedExtensions = f postgresql.pkgs;
419 in
420 buildEnv {
421 name = "${postgresql.pname}-and-plugins-${postgresql.version}";
422 paths = installedExtensions ++ [
423 postgresql
424 postgresql.man # in case user installs this into environment
425 ];
426
427 pathsToLink = [ "/" ];
428
429 passthru = {
430 inherit installedExtensions;
431 inherit (postgresql)
432 psqlSchema
433 version
434 ;
435
436 withJIT = postgresqlWithPackages {
437 inherit buildEnv;
438 postgresql = postgresql.withJIT;
439 } f;
440 withoutJIT = postgresqlWithPackages {
441 inherit buildEnv;
442 postgresql = postgresql.withoutJIT;
443 } f;
444 };
445 };
446
447in
448# passed by <major>.nix
449versionArgs:
450# passed by default.nix
451{ self, ... }@defaultArgs:
452self.callPackage generic (defaultArgs // versionArgs)