summaryrefslogtreecommitdiffstats
path: root/pg_wrapper
diff options
context:
space:
mode:
Diffstat (limited to 'pg_wrapper')
-rwxr-xr-xpg_wrapper329
1 files changed, 329 insertions, 0 deletions
diff --git a/pg_wrapper b/pg_wrapper
new file mode 100755
index 0000000..5b741c5
--- /dev/null
+++ b/pg_wrapper
@@ -0,0 +1,329 @@
+#!/usr/bin/perl
+# Call a PostgreSQL client program with the version, cluster and default
+# database specified in ~/.postgresqlrc or
+# /etc/postgresql-common/user_clusters.
+#
+# (C) 2005-2009 Martin Pitt <mpitt@debian.org>
+# (C) 2013-2022 Christoph Berg <myon@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+use warnings;
+use strict;
+use POSIX;
+use PgCommon;
+
+my ($version, $cluster);
+my $explicit_host = exists $ENV{PGHOST};
+my $explicit_port = $ENV{PGPORT};
+my $explicit_service = exists $ENV{PGSERVICE};
+
+# Evaluate PGCLUSTER (unless PGHOST is set as well)
+if (exists $ENV{'PGCLUSTER'} and not $explicit_host) {
+ ($version, $cluster) = split ('/', $ENV{'PGCLUSTER'}, 2);
+ error "Invalid version $version specified in PGCLUSTER" unless version_exists $version;
+ error 'No cluster specified with $PGCLUSTER' unless $cluster;
+}
+
+# Check for --cluster argument and filter it out
+for (my $i = 0; $i <= $#ARGV; ++$i) {
+ last if $ARGV[$i] eq '--';
+
+ if ($ARGV[$i] eq '--cluster') {
+ error '--cluster option needs an argument (<version>/<cluster>)' if ($i >= $#ARGV);
+
+ ($version, $cluster) = split ('/', $ARGV[$i+1], 2);
+ error "Invalid version $version specified with --cluster" unless version_exists $version;
+ error 'No cluster specified with --cluster' unless $cluster;
+
+ splice @ARGV, $i, 2;
+ last;
+ } elsif ($ARGV[$i] =~ /^--cluster=(\d+\.?\d)\/(.+)/) {
+ ($version, $cluster) = ($1, $2);
+ error "Invalid version $version specified with --cluster" unless version_exists $version;
+ error 'No cluster specified with --cluster' unless $cluster;
+
+ splice @ARGV, $i, 1;
+ last;
+ }
+ # --host or -h on command line, drop info from PGCLUSTER
+ if ($ARGV[$i] =~ /^--host\b/ || $ARGV[$i] =~ /^-\w*h\w*$/) {
+ ($version, $cluster) = (undef, undef);
+ $explicit_host = 1;
+ delete $ENV{PGCLUSTER};
+ }
+ # --port or -p on command line
+ if ($ARGV[$i] =~ /^--port\b(?:=(\d+))?/ || $ARGV[$i] =~ /^-\w*p(\d+)?$/) {
+ if (defined $1) {
+ $explicit_port = $1;
+ } elsif ($i < $#ARGV) {
+ $explicit_port = $ARGV[$i+1];
+ }
+ }
+ # "service=" in connection string
+ if ($ARGV[$i] =~ /\bservice=/) {
+ $explicit_service = 1;
+ }
+}
+
+# if only a port is specified, look for local cluster on specified port
+if ($explicit_port and not $version and not $cluster and not $explicit_host and not $explicit_service) {
+ LOOP: foreach my $v (reverse get_versions()) {
+ foreach my $c (get_version_clusters $v) {
+ my $p = get_cluster_port $v, $c;
+ if ($p eq $explicit_port) {
+ $version = $v;
+ # set PGCLUSTER variable for information
+ $ENV{PGCLUSTER} = "$version/$c";
+ last LOOP;
+ }
+ }
+ }
+}
+
+# if we don't have a cluster, and no specific host or port was given, consult postgresqlrc
+# or fall back to default port cluster (on 5432), or undef otherwise
+my ($db);
+($version, $cluster, $db) = user_cluster_map() unless ($cluster or $explicit_host or $explicit_port);
+
+my ($host, $port);
+
+if ($cluster) {
+ # check if we have a network cluster (N.N/the.host.name:port)
+ if ($cluster =~ /^(\S+):(\d*)$/) {
+ $host = $1;
+ $port = $2 || $PgCommon::defaultport;
+ } elsif (not cluster_exists($version, $cluster)) {
+ # a specific cluster was requested, error out because it doesn't exist
+ error "Cluster $version $cluster does not exist";
+ } else {
+ $host = get_cluster_socketdir ($version, $cluster);
+ $port = get_cluster_port($version, $cluster);
+ }
+ # set PGCLUSTER variable for information
+ $ENV{PGCLUSTER} = "$version/$cluster";
+}
+
+# setup environment
+$ENV{'PGSYSCONFDIR'} //= '/etc/postgresql-common';
+$ENV{'PGHOST'} = $host if ($host);
+$ENV{'PGPORT'} = $port if $port && !$ENV{'PGPORT'};
+$ENV{'PGDATABASE'} = $db if $db && !$ENV{'PGDATABASE'};
+
+# check under which name we were called
+my $cmdname = (split '/', $0)[-1];
+
+unless ($version or $explicit_host or $explicit_port or $explicit_service) {
+ print STDERR "Warning: No existing cluster is suitable as a default target. Please see man pg_wrapper(1) how to specify one.\n";
+}
+
+# if we have no version yet, use the latest version. If we were called as psql,
+# pg_archivecleanup, or pg_isready, always use latest version
+if (not $version or $cmdname =~ /^(psql|pg_archivecleanup|pg_isready)$/) {
+ my $max_version;
+ if ($version and $version < 9.2) { # psql 15 only supports PG 9.2+
+ $max_version = 14;
+ }
+ $version = get_newest_version($cmdname, $max_version);
+}
+unless ($version) {
+ error 'You must install at least one postgresql-client-<version> package';
+}
+error "PostgreSQL version $version is not installed" unless -d "$PgCommon::binroot$version";
+
+my $cmd;
+if ($cmdname eq 'pg_wrapper') {
+ error "pg_wrapper called directly but no program given as argument"
+ if (@ARGV == 0);
+ $cmd = shift; # will be unshifted back below
+} else {
+ $cmd = get_program_path ($cmdname, $version);
+}
+
+# libreadline is a lot better than libedit, so prefer that on versions that still use it
+if ($cmdname eq 'psql' and $version < 13 and not $PgCommon::rpm) {
+ my @readlines;
+ # non-multiarch path
+ @readlines = sort(</lib/libreadline.so.?>);
+
+ unless (@readlines) {
+ # get multiarch dir for our architecture
+ if (open PS, '-|', '/usr/bin/ldd', $cmd) {
+ my $out;
+ read PS, $out, 10000;
+ close PS;
+ if ($out =~ m!/libreadline.so!) {
+ # already linked against libreadline
+ @readlines = ();
+ }
+ else
+ {
+ my ($lib_path) = $out =~ m!(/lib/.*)/libedit.so!;
+
+ @readlines = sort(<$lib_path/libreadline.so.?>);
+ }
+ }
+ }
+
+ if (@readlines) {
+ $ENV{'LD_PRELOAD'} = ($ENV{'LD_PRELOAD'} or '') . ':' . $readlines[-1];
+ }
+}
+
+error "pg_wrapper: $cmdname was not found in $PgCommon::binroot$version/bin" unless $cmd;
+unshift @ARGV, $cmd;
+exec @ARGV;
+
+__END__
+
+=head1 NAME
+
+pg_wrapper - wrapper for PostgreSQL client commands
+
+=head1 SYNOPSIS
+
+I<client-program> [B<--cluster> I<version>/I<cluster>] [...]
+
+(I<client-program>: B<psql>, B<createdb>, B<dropuser>, and all other client
+programs installed in C</usr/lib/postgresql/>I<version>C</bin>).
+
+=head1 DESCRIPTION
+
+This program is run only as a link to names which correspond to PostgreSQL
+programs in C</usr/lib/postgresql/>I<version>C</bin>. It determines the
+configured cluster and database for the user and calls the appropriate version
+of the desired program to connect to that cluster and database, supplying any
+specified options to that command.
+
+The target cluster is selected by the following means, in descending order of
+precedence:
+
+=over
+
+=item
+
+explicit specification with the B<--host> option
+
+=item
+
+explicit specification with the B<--cluster> option
+
+=item
+
+if the B<PGHOST> environment variable is set, no further cluster selection is
+performed. The default PostgreSQL version and port number (from the command
+line, the environment variable B<PGPORT>, or default 5432) will be used.
+
+=item
+
+explicit specification with the B<PGCLUSTER> environment variable
+
+=item
+
+if a port is given (either via B<-p>, B<--port>, or B<PGPORT>), and no host is
+given, the local cluster matching that port number is used
+
+=item
+
+matching entry in C<~/.postgresqlrc> (see L<postgresqlrc(5)>), if that
+file exists
+
+=item
+
+matching entry in C</etc/postgresql-common/user_clusters> (see
+L<user_clusters(5)>), if that file exists
+
+=item
+
+If only one cluster exists on the local system, that one will be selected.
+
+=item
+
+If several clusters exist on the local system, the one listening on the default port 5432
+will be selected.
+
+=back
+
+If none of these rules match, B<pg_wrapper> does not set any environment
+variables and the program called will likely error out with a message like
+"could not connect to server: Connection refused".
+
+For B<psql>, B<pg_archivecleanup>, and B<pg_isready>, B<pg_wrapper> will always use the binary from
+the newest PostgreSQL version installed, as these are downwards compatible.
+If the cluster version is older than 9.2, the newest considered binary version is 14.
+
+Note that B<pg_wrapper> needs to be able to read the server config to get the
+port number to connect to. If a non-standard port is configured in a place that
+pg_wrapper cannot read, connecting will fail. This particularly holds if the
+port was configured via B<ALTER SYSTEM> in C<postgresql.auto.conf> and
+pg_wrapper is invoked as any user other than B<postgres> and B<root>.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--cluster> I<version>B</>I<cluster>
+
+=item B<--cluster> I<version>B</>I<host>B<:>[I<port>]
+
+I<cluster> is either the name of a cluster on the local system, or takes the form
+I<host>:I<port> for a remote cluster. If I<port> is left empty (i. e. you just
+specify I<host:>), it defaults to 5432.
+
+=back
+
+=head1 ENVIRONMENT
+
+=over
+
+=item B<PGCLUSTER>
+
+If C<$PGCLUSTER> is set, its value (of the form I<version>/I<cluster>)
+specifies the desired cluster, similar to the B<--cluster> option. However, if
+B<--cluster> is specified, it overrides the value of C<$PGCLUSTER>.
+
+=item B<PG_CLUSTER_CONF_ROOT>
+
+This specifies an alternative base directory for cluster configurations. This
+is usually C</etc/postgresql/>, but for testing/development purposes you can
+change this to point to e. g. your home directory, so that you can use the
+postgresql-common tools without root privileges.
+
+=item B<PGSYSCONFDIR>
+
+This is the location of PostgreSQL's and postgresql-common's global
+configuration (e. g. C<pg_service.conf>, L<user_clusters(5)>). The default is
+C</etc/postgresql-common/>.
+
+=back
+
+=head1 FILES
+
+=over
+
+=item C</etc/postgresql-common/user_clusters>
+
+stores the default cluster and database for users and groups as set by
+the administrators.
+
+=item C<$HOME/.postgresqlrc>
+
+stores defaults set by the user himself.
+
+=back
+
+=head1 SEE ALSO
+
+L<user_clusters(5)>, L<postgresqlrc(5)>
+
+=head1 AUTHOR
+
+Martin Pitt L<E<lt>mpitt@debian.orgE<gt>>