package pgdump import ( "context" "fmt" "io" "os" "os/exec" "strings" "git.tswf.io/infra/go-synapse-backupper/pkg/domain/pgdump" ) // adapter provides pg_dump functionality using the system's pg_dump binary. type adapter struct { commandContext func(ctx context.Context, name string, arg ...string) *exec.Cmd } // New creates a new pg_dump adapter. func New() pgdump.Dumper { return &adapter{ commandContext: exec.CommandContext, } } // Dump executes pg_dump and writes the output to sink. func (a *adapter) Dump( ctx context.Context, opts pgdump.Options, sink io.Writer, ) (retErr error) { defer func() { if pipeWriter, ok := sink.(*io.PipeWriter); ok { if retErr != nil { _ = pipeWriter.CloseWithError(retErr) } else { _ = pipeWriter.Close() } } }() args := buildArgs(opts) cmd := a.commandContext(ctx, "pg_dump", args...) env := os.Environ() if opts.Password != "" { env = append(env, fmt.Sprintf("PGPASSWORD=%s", opts.Password)) } if opts.Host != "" { env = append(env, fmt.Sprintf("PGHOST=%s", opts.Host)) } if opts.Port != 0 { env = append(env, fmt.Sprintf("PGPORT=%d", opts.Port)) } if opts.User != "" { env = append(env, fmt.Sprintf("PGUSER=%s", opts.User)) } if opts.Database != "" { env = append(env, fmt.Sprintf("PGDATABASE=%s", opts.Database)) } cmd.Env = env var stderrBuilder strings.Builder cmd.Stderr = &stderrBuilder cmd.Stdout = sink runErr := cmd.Run() stderr := strings.TrimSpace(stderrBuilder.String()) if runErr != nil { if exitErr, ok := runErr.(*exec.ExitError); ok { return fmt.Errorf("%w (exit %d): %s", pgdump.ErrPgDumpFailed(exitErr.ExitCode()), exitErr.ExitCode(), stderr) } return runErr } if cmd.ProcessState.ExitCode() != 0 { return fmt.Errorf("%w (exit %d): %s", pgdump.ErrPgDumpFailed(cmd.ProcessState.ExitCode()), cmd.ProcessState.ExitCode(), stderr) } return nil } func buildArgs(opts pgdump.Options) []string { excludeTables := opts.ExcludeTables if len(excludeTables) == 0 { excludeTables = make([]string, 1) excludeTables[0] = "e2e_one_time_keys_json" } args := make([]string, 0, len(excludeTables)+1) args = append(args, "--format=custom") for _, table := range excludeTables { args = append(args, "--exclude-table="+table) } return args }