From 98f483ae118bbd6031f44090d69d03616d6e9e8b Mon Sep 17 00:00:00 2001 From: Renz Date: Wed, 12 Nov 2025 08:43:16 +0000 Subject: [PATCH] Fix: Database listing now works with peer authentication Issue: Interactive cluster restore preview showed 'Cannot list databases: exit status 2' when trying to detect existing databases. This happened because the safety check functions always used '-h hostname' flag with psql, which breaks peer authentication. Root cause: - listPostgresUserDatabases() and checkPostgresDatabaseExists() always included -h flag - For localhost peer auth, psql should connect via Unix socket (no -h flag) - Adding -h localhost forces TCP connection which fails with peer authentication Solution: Match the pattern used throughout the codebase: - Only add -h flag when host is NOT localhost/127.0.0.1/empty - For localhost, skip -h flag to use Unix socket - Set PGPASSWORD only if password is provided Fixed functions in internal/restore/safety.go: - listPostgresUserDatabases() - checkPostgresDatabaseExists() Now interactive mode correctly shows existing databases count and list when running as postgres user with peer authentication. --- internal/restore/safety.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/internal/restore/safety.go b/internal/restore/safety.go index d478aa5..6cc3a8b 100644 --- a/internal/restore/safety.go +++ b/internal/restore/safety.go @@ -297,16 +297,24 @@ func (s *Safety) CheckDatabaseExists(ctx context.Context, dbName string) (bool, // checkPostgresDatabaseExists checks if PostgreSQL database exists func (s *Safety) checkPostgresDatabaseExists(ctx context.Context, dbName string) (bool, error) { - cmd := exec.CommandContext(ctx, - "psql", - "-h", s.cfg.Host, + args := []string{ "-p", fmt.Sprintf("%d", s.cfg.Port), "-U", s.cfg.User, "-d", "postgres", "-tAc", fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname='%s'", dbName), - ) + } + + // Only add -h flag if host is not localhost (to use Unix socket for peer auth) + if s.cfg.Host != "localhost" && s.cfg.Host != "127.0.0.1" && s.cfg.Host != "" { + args = append([]string{"-h", s.cfg.Host}, args...) + } + + cmd := exec.CommandContext(ctx, "psql", args...) - cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", s.cfg.Password)) + // Set password if provided + if s.cfg.Password != "" { + cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", s.cfg.Password)) + } output, err := cmd.Output() if err != nil { @@ -354,17 +362,25 @@ func (s *Safety) listPostgresUserDatabases(ctx context.Context) ([]string, error // Query to get non-template databases excluding 'postgres' system DB query := "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres' ORDER BY datname" - cmd := exec.CommandContext(ctx, - "psql", - "-h", s.cfg.Host, + args := []string{ "-p", fmt.Sprintf("%d", s.cfg.Port), "-U", s.cfg.User, "-d", "postgres", "-tA", // Tuples only, unaligned "-c", query, - ) + } + + // Only add -h flag if host is not localhost (to use Unix socket for peer auth) + if s.cfg.Host != "localhost" && s.cfg.Host != "127.0.0.1" && s.cfg.Host != "" { + args = append([]string{"-h", s.cfg.Host}, args...) + } + + cmd := exec.CommandContext(ctx, "psql", args...) - cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", s.cfg.Password)) + // Set password if provided + if s.cfg.Password != "" { + cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", s.cfg.Password)) + } output, err := cmd.Output() if err != nil {