pgweb/pkg/client/dump.go

94 lines
2.1 KiB
Go
Raw Normal View History

2017-09-17 04:32:41 +03:00
package client
import (
"bytes"
"context"
2017-09-17 04:32:41 +03:00
"fmt"
"io"
"net/url"
2017-09-17 04:32:41 +03:00
"os/exec"
"strings"
2017-09-17 04:32:41 +03:00
)
var (
unsupportedDumpOptions = []string{
"search_path",
}
)
// Dump represents a database dump
2017-09-17 04:32:41 +03:00
type Dump struct {
Table string
}
// Validate checks availability and version of pg_dump CLI
func (d *Dump) Validate(serverVersion string) error {
out := bytes.NewBuffer(nil)
cmd := exec.Command("pg_dump", "--version")
cmd.Stdout = out
cmd.Stderr = out
if err := cmd.Run(); err != nil {
return fmt.Errorf("pg_dump command failed: %s", out.Bytes())
}
detected, dumpVersion := detectDumpVersion(out.String())
if detected && serverVersion != "" {
satisfied := checkVersionRequirement(dumpVersion, serverVersion)
if !satisfied {
return fmt.Errorf("pg_dump version %v not compatible with server version %v", dumpVersion, serverVersion)
}
}
return nil
}
// Export streams the database dump to the specified writer
func (d *Dump) Export(ctx context.Context, connstr string, writer io.Writer) error {
if str, err := removeUnsupportedOptions(connstr); err != nil {
return err
} else {
connstr = str
}
2017-09-17 04:32:41 +03:00
opts := []string{
"--no-owner", // skip restoration of object ownership in plain-text format
"--clean", // clean (drop) database objects before recreating
"--compress", "6", // compression level for compressed formats
}
if d.Table != "" {
opts = append(opts, []string{"--table", d.Table}...)
}
opts = append(opts, connstr)
errOutput := bytes.NewBuffer(nil)
2017-09-17 04:32:41 +03:00
cmd := exec.CommandContext(ctx, "pg_dump", opts...)
2017-09-17 04:32:41 +03:00
cmd.Stdout = writer
cmd.Stderr = errOutput
if err := cmd.Run(); err != nil {
return fmt.Errorf("error: %s. output: %s", err.Error(), errOutput.Bytes())
}
return nil
}
// removeUnsupportedOptions removes any options unsupported for making a db dump
func removeUnsupportedOptions(input string) (string, error) {
uri, err := url.Parse(input)
if err != nil {
return "", err
}
q := uri.Query()
for _, opt := range unsupportedDumpOptions {
q.Del(opt)
q.Del(strings.ToUpper(opt))
}
uri.RawQuery = q.Encode()
return uri.String(), nil
}