Skip to content

Commit abc1005

Browse files
PostgreSQL plugin: enumerate via pg_database query instead of parsing psql -l (#341)
Parsing 'psql -l -t -A' breaks for databases with GRANTed privileges: the multi-line Access privileges column emits continuation lines (no '|') that were mis-parsed as database names, making '*' backups fail. Query pg_database instead. Fixes execute_plugin_pg_dump and test_plugin_pg_dump.
1 parent edd6cb4 commit abc1005

1 file changed

Lines changed: 13 additions & 10 deletions

File tree

agent/bbs-agent.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,19 +1699,21 @@ def execute_plugin_pg_dump(config):
16991699
pg_env["PGPASSWORD"] = password
17001700

17011701
if isinstance(databases, str) and databases.strip() == "*":
1702-
# List all databases via psql
1703-
list_cmd = ["psql", "-h", host, "-p", port, "-U", user, "-l", "-t", "-A"]
1702+
# List databases via a catalog query. Parsing `psql -l` is unsafe: DBs with
1703+
# GRANTed privileges have a multi-line "Access privileges" column whose
1704+
# continuation lines (e.g. "<grantee>=<privs>/<grantor>") get mis-parsed as names.
1705+
list_cmd = ["psql", "-h", host, "-p", port, "-U", user, "-tAc",
1706+
"SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate"]
17041707
result = subprocess.run(list_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=pg_env)
17051708
if result.returncode != 0:
17061709
raise Exception("Failed to list databases: {}".format(result.stderr.decode('utf-8', errors='replace').strip()))
17071710
if isinstance(exclude, str):
17081711
exclude = [x.strip() for x in exclude.split(",")]
1709-
# psql -l -t -A outputs: dbname|owner|encoding|collate|ctype|access
17101712
databases = []
17111713
for line in result.stdout.decode("utf-8", errors="replace").strip().split("\n"):
1712-
parts = line.split("|")
1713-
if parts and parts[0].strip() and parts[0].strip() not in exclude:
1714-
databases.append(parts[0].strip())
1714+
name = line.strip()
1715+
if name and name not in exclude:
1716+
databases.append(name)
17151717
elif isinstance(databases, str):
17161718
databases = [d.strip() for d in databases.split(",") if d.strip()]
17171719

@@ -1798,13 +1800,14 @@ def test_plugin_pg_dump(config):
17981800
raise Exception("Connection failed: {}".format(result.stderr.decode('utf-8', errors='replace').strip()))
17991801

18001802
# List databases
1801-
cmd2 = ["psql", "-h", host, "-p", port, "-U", user, "-l", "-t", "-A"]
1803+
cmd2 = ["psql", "-h", host, "-p", port, "-U", user, "-tAc",
1804+
"SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate"]
18021805
result2 = subprocess.run(cmd2, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=pg_env, timeout=15)
18031806
dbs = []
18041807
for line in result2.stdout.decode("utf-8", errors="replace").strip().split("\n"):
1805-
parts = line.split("|")
1806-
if parts and parts[0].strip():
1807-
dbs.append(parts[0].strip())
1808+
name = line.strip()
1809+
if name:
1810+
dbs.append(name)
18081811
return "Connection successful. Found {} database(s): {}".format(len(dbs), ', '.join(dbs[:10]))
18091812

18101813

0 commit comments

Comments
 (0)