Skip to content

Commit

Permalink
check and adapt to the version of pg_dump and pg_dumpall
Browse files Browse the repository at this point in the history
When pg_dump/all is older than 9.3 pass the connection string as
environment variables instead of using -d which does not exist. Also,
fail when the user asks for the directory format and pg_dump does not
know it (<9.1). For other unknown parameters (-j and -B) ignore them
with a warning if pg_dump is too old.
  • Loading branch information
orgrim committed Nov 2, 2021
1 parent fa67fe9 commit 2d082e9
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 12 deletions.
70 changes: 70 additions & 0 deletions connstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,76 @@ func (c *ConnInfo) Del(keyword string) *ConnInfo {
return newC
}

// MakeEnv return the conninfo as a list of "key=value" environment variables
// that the libpq understands, as stated in the documentation of PostgreSQL 14
func (c *ConnInfo) MakeEnv() []string {
env := make([]string, 0, len(c.Infos))
for k, v := range c.Infos {
switch k {
case "host":
env = append(env, "PGHOST="+v)
case "hostaddr":
env = append(env, "PGHOSTADDR="+v)
case "port":
env = append(env, "PGPORT="+v)
case "dbname":
env = append(env, "PGDATABASE="+v)
case "user":
env = append(env, "PGUSER="+v)
case "password":
env = append(env, "PGPASSWORD="+v)
case "passfile":
env = append(env, "PGPASSFILE="+v)
case "service":
env = append(env, "PGSERVICE="+v)
case "options":
env = append(env, "PGOPTIONS="+v)
case "application_name":
env = append(env, "PGAPPNAME="+v)
case "sslmode":
env = append(env, "PGSSLMODE="+v)
case "requiressl":
env = append(env, "PGREQUIRESSL="+v)
case "sslcert":
env = append(env, "PGSSLCERT="+v)
case "sslkey":
env = append(env, "PGSSLKEY="+v)
case "sslrootcert":
env = append(env, "PGSSLROOTCERT="+v)
case "sslcrl":
env = append(env, "PGSSLCRL="+v)
case "krbsrvname":
env = append(env, "PGKRBSRVNAME="+v)
case "gsslib":
env = append(env, "PGGSSLIB="+v)
case "connect_timeout":
env = append(env, "PGCONNECT_TIMEOUT="+v)
case "channel_binding":
env = append(env, "PGCHANNELBINDING="+v)
case "sslcompression":
env = append(env, "PGSSLCOMPRESSION="+v)
case "sslcrldir":
env = append(env, "PGSSLCRLDIR="+v)
case "sslsni":
env = append(env, "PGSSLSNI="+v)
case "requirepeer":
env = append(env, "PGREQUIREPEER="+v)
case "ssl_min_protocol_version":
env = append(env, "PGSSLMINPROTOCOLVERSION="+v)
case "ssl_max_protocol_version":
env = append(env, "PGSSLMAXPROTOCOLVERSION="+v)
case "gssencmode":
env = append(env, "PGGSSENCMODE="+v)
case "client_encoding":
env = append(env, "PGCLIENTENCODING="+v)
case "target_session_attrs":
env = append(env, "PGTARGETSESSIONATTRS="+v)
}
}

return env
}

func parseUrlConnInfo(connstring string) (map[string]string, error) {
u, err := url.Parse(connstring)
if err != nil {
Expand Down
64 changes: 52 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ type dump struct {
// Result
When time.Time
ExitCode int

// Version of pg_dump
PgDumpVersion int
}

type dbOpts struct {
Expand Down Expand Up @@ -150,6 +153,8 @@ func main() {
binDir = opts.BinDirectory
}

pgDumpVersion := pgToolVersion("pg_dump")

// Parse the connection information
l.Verboseln("processing input connection parameters")
conninfo, err := prepareConnInfo(opts.Host, opts.Port, opts.Username, opts.ConnDb)
Expand Down Expand Up @@ -268,12 +273,13 @@ func main() {
}

d := &dump{
Database: dbname,
Options: o,
Directory: opts.Directory,
TimeFormat: opts.TimeFormat,
ConnString: conninfo,
ExitCode: -1,
Database: dbname,
Options: o,
Directory: opts.Directory,
TimeFormat: opts.TimeFormat,
ConnString: conninfo,
ExitCode: -1,
PgDumpVersion: pgDumpVersion,
}

l.Verbosef("sending dump job for database %s to worker pool", dbname)
Expand Down Expand Up @@ -434,6 +440,10 @@ func (d *dump) dump() error {
case 't':
fileEnd = "tar"
case 'd':
if d.PgDumpVersion < 90100 {
return fmt.Errorf("provided pg_dump version does not support directory format")
}

fileEnd = "d"
}

Expand All @@ -444,13 +454,17 @@ func (d *dump) dump() error {
args := []string{formatOpt, "-f", file, "-w"}

if fileEnd == "d" && d.Options.Jobs > 1 {
args = append(args, "-j", fmt.Sprintf("%d", d.Options.Jobs))
if d.PgDumpVersion < 90300 {
l.Warnln("provided pg_dump version does not support parallel jobs, ignoring option")
} else {
args = append(args, "-j", fmt.Sprintf("%d", d.Options.Jobs))
}
}

// It is recommended to use --create with the plain format
// from PostgreSQL 11 to get the ACL and configuration of the
// database
if pgToolVersion("pg_dump") >= 110000 && fileEnd == "sql" {
if d.PgDumpVersion >= 110000 && fileEnd == "sql" {
args = append(args, "--create")
}

Expand All @@ -472,7 +486,11 @@ func (d *dump) dump() error {
case 1: // with blobs
args = append(args, "-b")
case 2: // without blobs
args = append(args, "-B")
if d.PgDumpVersion < 100000 {
l.Warnln("provided pg_dump version does not support excluding blobs, ignoring option")
} else {
args = append(args, "-B")
}
}

// Add compression level option only if not dumping in the plain format
Expand All @@ -489,11 +507,22 @@ func (d *dump) dump() error {
}

// Connection option are passed as a connstring even if we add options
// on the command line
// on the command line. For older version, it is passed using the
// environment
conninfo := d.ConnString.Set("dbname", dbname)
args = append(args, "-d", conninfo.String())

var env []string

if d.PgDumpVersion < 90300 {
args = append(args, dbname)
env = os.Environ()
env = append(env, d.ConnString.MakeEnv()...)
} else {
args = append(args, "-d", conninfo.String())
}

pgDumpCmd := exec.Command(command, args...)
pgDumpCmd.Env = env
l.Verboseln("running:", pgDumpCmd)
stdoutStderr, err := pgDumpCmd.CombinedOutput()
if err != nil {
Expand Down Expand Up @@ -615,7 +644,17 @@ func dumpGlobals(dir string, timeFormat string, conninfo *ConnInfo, fc chan<- st
args = append(args, "-l", dbname)
}

args = append(args, "-d", conninfo.String())
// With older version of PostgreSQL not supporting connection strings
// on their -d option, use the environment to pass the connection
// information
var env []string

if pgToolVersion("pg_dumpall") < 90300 {
env = os.Environ()
env = append(env, conninfo.MakeEnv()...)
} else {
args = append(args, "-d", conninfo.String())
}

file := formatDumpPath(dir, timeFormat, "sql", "pg_globals", time.Now())
args = append(args, "-f", file)
Expand All @@ -625,6 +664,7 @@ func dumpGlobals(dir string, timeFormat string, conninfo *ConnInfo, fc chan<- st
}

pgDumpallCmd := exec.Command(command, args...)
pgDumpallCmd.Env = env
l.Verboseln("running:", pgDumpallCmd)
stdoutStderr, err := pgDumpallCmd.CombinedOutput()
if err != nil {
Expand Down

0 comments on commit 2d082e9

Please sign in to comment.