@@ -23,23 +23,105 @@ import (
2323
2424// RestoreProcess represents a pg_restore process
2525type RestoreProcess struct {
26- cmd * exec.Cmd
27- stdin io.WriteCloser
26+ cmd * exec.Cmd
27+ stdin io.WriteCloser
28+ stdout io.ReadCloser
29+ stderr io.ReadCloser
2830}
2931
3032// Start starts the pg_restore process
3133func (p * RestoreProcess ) Start () error {
32- p .stdin , _ = p .cmd .StdinPipe ()
34+ var err error
35+
36+ // Set up pipes for stdin, stdout, and stderr
37+ p .stdin , err = p .cmd .StdinPipe ()
38+ if err != nil {
39+ return fmt .Errorf ("failed to create stdin pipe: %w" , err )
40+ }
41+
42+ p .stdout , err = p .cmd .StdoutPipe ()
43+ if err != nil {
44+ return fmt .Errorf ("failed to create stdout pipe: %w" , err )
45+ }
46+
47+ p .stderr , err = p .cmd .StderrPipe ()
48+ if err != nil {
49+ return fmt .Errorf ("failed to create stderr pipe: %w" , err )
50+ }
51+
3352 if err := p .cmd .Start (); err != nil {
34- return err
53+ return fmt . Errorf ( "failed to start pg_restore process: %w" , err )
3554 }
55+
3656 return nil
3757}
3858
3959// Wait waits for the pg_restore process to complete
4060func (p * RestoreProcess ) Wait () error {
61+ logger := log .Logger .With ().Str ("caller" , "restore_process_wait" ).Logger ()
62+
63+ // Close stdin to signal we're done sending data
4164 p .stdin .Close ()
42- return p .cmd .Wait ()
65+
66+ // Read stdout and stderr concurrently
67+ stdoutChan := make (chan string , 1 )
68+ stderrChan := make (chan string , 1 )
69+
70+ go func () {
71+ if p .stdout != nil {
72+ if output , err := io .ReadAll (p .stdout ); err == nil {
73+ stdoutChan <- string (output )
74+ } else {
75+ stdoutChan <- ""
76+ }
77+ p .stdout .Close ()
78+ } else {
79+ stdoutChan <- ""
80+ }
81+ }()
82+
83+ go func () {
84+ if p .stderr != nil {
85+ if output , err := io .ReadAll (p .stderr ); err == nil {
86+ stderrChan <- string (output )
87+ } else {
88+ stderrChan <- ""
89+ }
90+ p .stderr .Close ()
91+ } else {
92+ stderrChan <- ""
93+ }
94+ }()
95+
96+ // Wait for the process to complete
97+ err := p .cmd .Wait ()
98+
99+ // Collect output
100+ stdoutOutput := <- stdoutChan
101+ stderrOutput := <- stderrChan
102+
103+ // Log outputs for debugging
104+ if stdoutOutput != "" {
105+ logger .Debug ().Str ("pg_restore_stdout" , stdoutOutput ).Msg ("pg_restore stdout output" )
106+ }
107+
108+ if stderrOutput != "" {
109+ if err != nil {
110+ logger .Error ().Str ("pg_restore_stderr" , stderrOutput ).Msg ("pg_restore error output" )
111+ } else {
112+ logger .Debug ().Str ("pg_restore_stderr" , stderrOutput ).Msg ("pg_restore stderr output" )
113+ }
114+ }
115+
116+ // Return enhanced error with pg_restore output
117+ if err != nil {
118+ if stderrOutput != "" {
119+ return fmt .Errorf ("pg_restore failed: %w\n pg_restore stderr: %s" , err , stderrOutput )
120+ }
121+ return fmt .Errorf ("pg_restore failed: %w" , err )
122+ }
123+
124+ return nil
43125}
44126
45127// Write writes data to the pg_restore process stdin
@@ -73,8 +155,13 @@ func NewRestore(ctx context.Context, targetDatabase string) (*RestoreProcess, er
73155 argument = append (argument , "--username" , * config .Loaded .Postgres .User )
74156 }
75157
76- // Use the target database name
77- argument = append (argument , "--dbname" , targetDatabase )
158+ // When using --create, connect to a maintenance database (postgres) instead of the target database
159+ // This avoids connection issues when the target database doesn't exist yet
160+ maintenanceDB := "postgres"
161+ if config .Loaded .Postgres .Database != nil && * config .Loaded .Postgres .Database != targetDatabase {
162+ maintenanceDB = * config .Loaded .Postgres .Database
163+ }
164+ argument = append (argument , "--dbname" , maintenanceDB )
78165
79166 process .cmd = exec .CommandContext (ctx , "pg_restore" , argument ... )
80167 if config .Loaded .Postgres .Password != nil {
@@ -87,6 +174,13 @@ func NewRestore(ctx context.Context, targetDatabase string) (*RestoreProcess, er
87174// Restore performs a complete restore operation from a backup reader to the target database
88175func Restore (backupReader io.Reader , targetDatabase , backupFilename string ) error {
89176 ctx := context .Background ()
177+ logger := log .Logger .With ().
178+ Str ("caller" , "restore" ).
179+ Str ("target_database" , targetDatabase ).
180+ Str ("backup_filename" , backupFilename ).
181+ Logger ()
182+
183+ logger .Debug ().Msg ("starting restore operation" )
90184
91185 // Apply decompression if needed
92186 decompressedReader , err := Decompress (backupReader , backupFilename )
@@ -105,20 +199,37 @@ func Restore(backupReader io.Reader, targetDatabase, backupFilename string) erro
105199 return fmt .Errorf ("failed to start restore process: %w" , err )
106200 }
107201
108- // Stream backup data to pg_restore
109- _ , err = io .Copy (restoreProcess , decompressedReader )
202+ logger .Debug ().Msg ("pg_restore process started, beginning data stream" )
203+
204+ // Stream backup data to pg_restore with better error handling
205+ bytesStreamed , err := io .Copy (restoreProcess , decompressedReader )
110206 if err != nil {
207+ logger .Error ().
208+ Err (err ).
209+ Int64 ("bytes_streamed" , bytesStreamed ).
210+ Msg ("failed to stream backup data to pg_restore" )
211+
212+ // Try to get pg_restore error output for better diagnostics
111213 if waitErr := restoreProcess .Wait (); waitErr != nil {
112- log .Error ().Err (waitErr ).Msg ("failed to wait for restore process cleanup" )
214+ logger .Error ().Err (waitErr ).Msg ("pg_restore process terminated with error during cleanup" )
215+ return fmt .Errorf ("failed to stream backup data to restore process: %w (pg_restore error: %v)" , err , waitErr )
216+ } else {
217+ logger .Debug ().Msg ("pg_restore process terminated cleanly after streaming error" )
113218 }
114219 return fmt .Errorf ("failed to stream backup data to restore process: %w" , err )
115220 }
116221
222+ logger .Debug ().
223+ Int64 ("bytes_streamed" , bytesStreamed ).
224+ Msg ("backup data streaming completed, waiting for pg_restore to finish" )
225+
117226 // Wait for restore to complete
118227 if err := restoreProcess .Wait (); err != nil {
228+ logger .Error ().Err (err ).Msg ("pg_restore process failed during completion" )
119229 return fmt .Errorf ("pg_restore process failed: %w" , err )
120230 }
121231
232+ logger .Debug ().Msg ("restore operation completed successfully" )
122233 return nil
123234}
124235
0 commit comments