2023-12-24 22:05:03 +03:00
|
|
|
{ config, pkgs, lib }:
|
|
|
|
let
|
2024-02-21 21:06:56 +03:00
|
|
|
psqlUserArg = lib.optionalString (config.superuser != null) "-U ${config.superuser}";
|
2024-01-13 20:18:46 +03:00
|
|
|
setupInitialSchema = dbName: schema: ''
|
|
|
|
echo "Applying database schema on ${dbName}"
|
|
|
|
if [ -f "${schema}" ]
|
|
|
|
then
|
|
|
|
echo "Running file ${schema}"
|
2024-02-21 21:06:56 +03:00
|
|
|
awk 'NF' "${schema}" | psql ${psqlUserArg} -d ${dbName}
|
2024-01-13 20:18:46 +03:00
|
|
|
elif [ -d "${schema}" ]
|
|
|
|
then
|
|
|
|
# Read sql files in version order. Apply one file
|
|
|
|
# at a time to handle files where the last statement
|
|
|
|
# doesn't end in a ;.
|
|
|
|
find "${schema}"/*.sql | while read -r f ; do
|
|
|
|
echo "Applying sql file: $f"
|
2024-02-21 21:06:56 +03:00
|
|
|
awk 'NF' "$f" | psql ${psqlUserArg} -d ${dbName}
|
2024-01-13 20:18:46 +03:00
|
|
|
done
|
|
|
|
else
|
|
|
|
echo "ERROR: Could not determine how to apply schema with ${schema}"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
'';
|
2023-12-24 22:05:03 +03:00
|
|
|
setupInitialDatabases =
|
|
|
|
if config.initialDatabases != [ ] then
|
|
|
|
(lib.concatMapStrings
|
|
|
|
(database: ''
|
|
|
|
echo "Checking presence of database: ${database.name}"
|
|
|
|
# Create initial databases
|
|
|
|
dbAlreadyExists=$(
|
|
|
|
echo "SELECT 1 as exists FROM pg_database WHERE datname = '${database.name}';" | \
|
2024-02-21 21:06:56 +03:00
|
|
|
psql ${psqlUserArg} -d postgres | \
|
2023-12-24 22:05:03 +03:00
|
|
|
grep -c 'exists = "1"' || true
|
|
|
|
)
|
|
|
|
echo "$dbAlreadyExists"
|
|
|
|
if [ 1 -ne "$dbAlreadyExists" ]; then
|
|
|
|
echo "Creating database: ${database.name}"
|
2024-02-21 21:06:56 +03:00
|
|
|
echo 'create database "${database.name}";' | psql ${psqlUserArg} -d postgres
|
2024-01-18 20:40:16 +03:00
|
|
|
${lib.optionalString (database.schemas != null)
|
|
|
|
(lib.concatMapStrings (schema: setupInitialSchema (database.name) schema) database.schemas)}
|
2023-12-24 22:05:03 +03:00
|
|
|
fi
|
|
|
|
'')
|
|
|
|
config.initialDatabases)
|
|
|
|
else
|
|
|
|
lib.optionalString config.createDatabase ''
|
2024-02-21 21:06:56 +03:00
|
|
|
echo "CREATE DATABASE ''${USER:-$(id -nu)};" | psql ${psqlUserArg} -d postgres '';
|
2023-12-26 12:45:32 +03:00
|
|
|
|
|
|
|
|
2023-12-24 22:05:03 +03:00
|
|
|
runInitialScript =
|
|
|
|
let
|
|
|
|
scriptCmd = sqlScript: ''
|
2024-02-21 21:06:56 +03:00
|
|
|
echo "${sqlScript}" | psql ${psqlUserArg} -d postgres
|
2023-12-24 22:05:03 +03:00
|
|
|
'';
|
|
|
|
in
|
|
|
|
{
|
|
|
|
before = with config.initialScript;
|
|
|
|
lib.optionalString (before != null) (scriptCmd before);
|
|
|
|
after = with config.initialScript;
|
|
|
|
lib.optionalString (after != null) (scriptCmd after);
|
|
|
|
};
|
|
|
|
toStr = value:
|
|
|
|
if true == value then
|
|
|
|
"yes"
|
|
|
|
else if false == value then
|
|
|
|
"no"
|
|
|
|
else if lib.isString value then
|
|
|
|
"'${lib.replaceStrings [ "'" ] [ "''" ] value}'"
|
|
|
|
else
|
|
|
|
toString value;
|
|
|
|
configFile = pkgs.writeText "postgresql.conf" (lib.concatStringsSep "\n"
|
|
|
|
(lib.mapAttrsToList (n: v: "${n} = ${toStr v}") (config.defaultSettings // config.settings)));
|
|
|
|
|
|
|
|
initdbArgs =
|
|
|
|
config.initdbArgs
|
|
|
|
++ (lib.optionals (config.superuser != null) [ "-U" config.superuser ])
|
|
|
|
++ [ "-D" config.dataDir ];
|
|
|
|
in
|
|
|
|
(pkgs.writeShellApplication {
|
|
|
|
name = "setup-postgres";
|
|
|
|
runtimeInputs = with pkgs; [ config.package coreutils gnugrep gawk ];
|
|
|
|
text = ''
|
|
|
|
set -euo pipefail
|
|
|
|
# Setup postgres ENVs
|
|
|
|
export PGDATA="${config.dataDir}"
|
|
|
|
export PGPORT="${toString config.port}"
|
|
|
|
POSTGRES_RUN_INITIAL_SCRIPT="false"
|
|
|
|
|
|
|
|
|
|
|
|
if [[ ! -d "$PGDATA" ]]; then
|
|
|
|
initdb ${lib.concatStringsSep " " initdbArgs}
|
|
|
|
POSTGRES_RUN_INITIAL_SCRIPT="true"
|
|
|
|
echo
|
|
|
|
echo "PostgreSQL initdb process complete."
|
|
|
|
echo
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Setup config
|
2024-01-31 16:17:52 +03:00
|
|
|
echo "Setting up postgresql.conf"
|
2023-12-24 22:05:03 +03:00
|
|
|
cp ${configFile} "$PGDATA/postgresql.conf"
|
2024-03-26 16:14:11 +03:00
|
|
|
# Create socketDir if it doesn't exist and it is not empty
|
|
|
|
${lib.optionalString (config.socketDir != "") ''
|
|
|
|
if [ ! -d "${config.socketDir}" ]; then
|
|
|
|
echo "Creating socket directory"
|
|
|
|
mkdir -p "${config.socketDir}"
|
|
|
|
fi
|
|
|
|
''}
|
2023-12-24 22:05:03 +03:00
|
|
|
|
|
|
|
if [[ "$POSTGRES_RUN_INITIAL_SCRIPT" = "true" ]]; then
|
|
|
|
echo
|
|
|
|
echo "PostgreSQL is setting up the initial database."
|
|
|
|
echo
|
2024-03-26 16:14:11 +03:00
|
|
|
${ if config.socketDir != "" then ''
|
|
|
|
PGHOST=$(mktemp -d "$(readlink -f "${config.socketDir}")/pg-init-XXXXXX")
|
|
|
|
'' else ''
|
|
|
|
PGHOST=$(mktemp -d /tmp/pg-init-XXXXXX)
|
|
|
|
''
|
|
|
|
}
|
2023-12-24 22:05:03 +03:00
|
|
|
export PGHOST
|
|
|
|
|
|
|
|
function remove_tmp_pg_init_sock_dir() {
|
|
|
|
if [[ -d "$1" ]]; then
|
|
|
|
rm -rf "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
trap 'remove_tmp_pg_init_sock_dir "$PGHOST"' EXIT
|
|
|
|
|
|
|
|
pg_ctl -D "$PGDATA" -w start -o "-c unix_socket_directories=$PGHOST -c listen_addresses= -p ${toString config.port}"
|
|
|
|
${runInitialScript.before}
|
|
|
|
${setupInitialDatabases}
|
|
|
|
${runInitialScript.after}
|
|
|
|
pg_ctl -D "$PGDATA" -m fast -w stop
|
|
|
|
remove_tmp_pg_init_sock_dir "$PGHOST"
|
|
|
|
else
|
|
|
|
echo
|
|
|
|
echo "PostgreSQL database directory appears to contain a database; Skipping initialization"
|
|
|
|
echo
|
|
|
|
fi
|
|
|
|
unset POSTGRES_RUN_INITIAL_SCRIPT
|
|
|
|
'';
|
|
|
|
})
|