Skip to content

Commit dcd9455

Browse files
thoughtpolicebasvandijk
authored andcommitted
postgresql11: preliminary JIT support
This adds preliminary support for JIT'ing SQL queries via LLVM to PostgreSQL, enabled on versions 11+, on Linux. The default stdenv of PostgreSQL itself and all exposed sub-packages are overridden to use a particular major version of llvmPackages.stdenv so that 'clang' is chosen as the default compiler. Currently, we use llvmPackages_6. To do this, we expose another component from postgresqlXXPackages -- a new .stdenv attribute, which exposes which stdenv PostgreSQL was built with. PostgreSQL JIT support works by compiling all of the source code for the system into LLVM bitcode, and shipping this as part of the binary distribution (under the .lib output, in our case, $lib/lib/bitcode/). At runtime, Postgres uses the LLVM API in order to load this bitcode and JIT queries directly against the source code. This allows inlining database code into queries, in particular inlining operator and expression definitions for custom types, in any extensions. This feature is enabled via the '--with-llvm' flag during configurePhase. This feature is integrated with 'PGXS', the Makefile infrastructure for writing and distributing Postgres extensions. If an extension is being compiled against a version of PostgreSQL that has LLVM JIT support, then the extension code is *also* compiled to bitcode and distributed transparently in the binary output, alongside the extension shared object. In order to ensure consistent bitcode is emitted, when Postgres is compiled with LLVM support, it *must* be compiled with Clang (hence the stdenv choice), and it also *must* embed the copy/path of Clang into the resulting binaries, so PGXS can use the exact same compiler later on during 3rd party extension builds. This design decision is likely because on systems such as Debian, multiple versions of Clang can exist, so any compiled Postgres code/extensions must use a consistent compiler for all future builds. However, this decision has extremely negative consequences for Nix-based packages, because it inserts LLVM and Clang into the closure of the PostgreSQL derivations as a hard runtime-dependency. This bloats the closure size by over a gigabyte (~140MB -> 1.4GB), which is fairly unwieldly and unlikely to be permissible by default. Currently this bloat only applies to the .out (binary) outputs. But this is made worse by the fact that the .lib output is also bloated by having a hard runtime dependency on llvmPackages.llvm.lib. This is because postgresql.lib now ships libllvmjit.so which talks to libLLVM.so, but postgresql.lib also contains client libraries like libpq.so. This effectively bloats every libpq client expression as well by about 200MB. Finally, because of the way PGXS's default installation logic works, it wants to install binary artifacts into the postgresql lib/ directory, which obviously isn't possible in the Nix store as it's read-only. Hence, we create environments composed of all extensions outputs and patch postgresql to load that. But this means the installPhase for every extension is currently a custom hand-written script, and *that* means every extension must now contain logic to install LLVM .bc files on top of .sql and .so files. Instead, we should probably patch PGXS to install to a proper external directory so its install logic can take over and we can remove custom installPhase scripts for most extensions. Postgres' LLVM support logic in its configure script and PGXS code will need to be patched to remove hard-coded references to clang-wrapper, since we always control the exact version of clang used and can remove it as a run-time dependency. Finally, bitcode should probably be moved to separate .bitcode derivations for all server versions and extension outputs, so that libpq clients aren't bloated by indirect dependencies on libLLVM.so (by way of libllvmjit.so). Oh, and PostGIS fails to build with JIT support/clang as the compiler, for some reason. As a result of these significant complications, this support is disabled by default, and should only be considered supported for vanilla PostgreSQL with no third-party extensions. Darwin may also be supported in the future; it may even build, but can't be tested. Signed-off-by: Austin Seipp <aseipp@pobox.com>
1 parent f30de35 commit dcd9455

3 files changed

Lines changed: 34 additions & 7 deletions

File tree

pkgs/servers/sql/postgresql/default.nix

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
{ stdenv, lib, fetchurl, makeWrapper
22
, glibc, zlib, readline, libossp_uuid, openssl, libxml2, tzdata, systemd
3-
}:
3+
4+
# Gate JIT support right now behind a flag; it increases closure size
5+
# dramatically due to the PostgreSQL build system requiring a hard dependency
6+
# on clang-wrapper (~140MB -> 1.4GB). This must be worked around before it can
7+
# be enabled by default by making clang-wrapper a build-time only dependency.
8+
, llvmPackages, enableJitSupport ? false
9+
}@deps:
410

511
let
612

713
common = { version, sha256, psqlSchema }:
8-
let atLeast = lib.versionAtLeast version; in stdenv.mkDerivation (rec {
14+
let
15+
atLeast = lib.versionAtLeast version;
16+
17+
# JIT is only supported on Linux, for now. (Darwin may build, but must be
18+
# tested).
19+
jitEnabled = atLeast "11" && enableJitSupport && deps.stdenv.isLinux;
20+
21+
# Note: use deps.stdenv, not just 'stdenv', otherwise infinite recursion
22+
# will occur due to lexical scoping rules.
23+
stdenv = if jitEnabled then llvmPackages.stdenv else deps.stdenv;
24+
in stdenv.mkDerivation (rec {
925
name = "postgresql-${version}";
1026
inherit version;
1127

@@ -20,7 +36,8 @@ let
2036
buildInputs =
2137
[ zlib readline openssl libxml2 makeWrapper ]
2238
++ lib.optionals (!stdenv.isDarwin) [ libossp_uuid ]
23-
++ lib.optionals (atLeast "9.6" && !stdenv.isDarwin) [ systemd ];
39+
++ lib.optionals (atLeast "9.6" && !stdenv.isDarwin) [ systemd ]
40+
++ lib.optionals jitEnabled (with llvmPackages; [ clang llvm ]);
2441

2542
enableParallelBuilding = true;
2643

@@ -39,6 +56,7 @@ let
3956
"--with-system-tzdata=${tzdata}/share/zoneinfo"
4057
(lib.optionalString (atLeast "9.6" && !stdenv.isDarwin) "--with-systemd")
4158
(if stdenv.isDarwin then "--with-uuid=e2fs" else "--with-ossp-uuid")
59+
(lib.optionalString jitEnabled "--with-llvm")
4260
];
4361

4462
patches =
@@ -87,11 +105,14 @@ let
87105

88106
doInstallCheck = false; # needs a running daemon?
89107

90-
disallowedReferences = [ stdenv.cc ];
108+
disallowedReferences = lib.optionals (!jitEnabled) [ stdenv.cc ];
91109

92110
passthru = {
93-
inherit readline psqlSchema;
111+
# Note: we export 'stdenv', because the chosen stdenv *might* be a llvmPackages-based
112+
# one, and we want to propagate that to all extensions.
113+
inherit readline psqlSchema stdenv;
94114
compareVersion = builtins.compareVersions version;
115+
hasJitSupport = jitEnabled;
95116
};
96117

97118
meta = with lib; {

pkgs/servers/sql/postgresql/ext/postgis.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ in stdenv.mkDerivation rec {
9595
"raster/scripts/python/Makefile";
9696
'';
9797

98+
passthru = {
99+
versionCheck = postgresql.hasJitSupport == false;
100+
};
101+
98102
meta = with stdenv.lib; {
99103
description = "Geographic Objects for PostgreSQL";
100104
homepage = http://postgis.refractions.net;

pkgs/servers/sql/postgresql/packages.nix

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
{ pkgs, lib }:
22

33
let
4-
postgresqlPackages = pkgs.callPackages ./default.nix {};
4+
llvmPackages = pkgs.llvmPackages_6;
5+
postgresqlPackages = pkgs.callPackages ./default.nix { inherit llvmPackages; };
56

67
# Filter out any versions which fail a version check.
78
filterPackages = lib.filterAttrs (_: drv: drv.versionCheck or true);
89

910
makePackageSet = postgresql:
1011
let
11-
callPackage = p: args: pkgs.callPackage p (args // { inherit postgresql; });
12+
stdenv = postgresql.stdenv; # Use the stdenv for the particular version of Postgres
13+
callPackage = p: args: pkgs.callPackage p (args // { inherit postgresql stdenv; });
1214
in
1315
filterPackages {
1416
# Convenience function for end-users to easily build packages against a specific

0 commit comments

Comments
 (0)