Skip to content

Commit 7a66377

Browse files
afdyAdam Fordpre-commit-ci[bot]
authored
feat: allow passing of postgres options (#556)
--------- Co-authored-by: Adam Ford <adam.ford@neosnetworks.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent dea56a8 commit 7a66377

File tree

5 files changed

+88
-10
lines changed

5 files changed

+88
-10
lines changed

README.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ different server. You may also specify an explicit local file to backup from.
102102
-p PASSPHRASE, --passphrase=PASSPHRASE
103103
Passphrase for decrypt file
104104
-z, --uncompress Uncompress gzip data before restoring
105-
105+
-n SCHEMA, --schema SCHEMA
106+
Specify schema(s) to restore. Can be used multiple times.
107+
-r, --no-drop Don't clean (drop) the database. This only works with mongodb and postgresql.
108+
--pg-options PG_OPTIONS
109+
Additional pg_restore options, e.g. '--if-exists --no-owner'. Use quotes.
106110

107111
mediabackup
108112
-----------

dbbackup/db/postgresql.py

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class PgDumpBinaryConnector(PgDumpConnector):
113113
single_transaction = True
114114
drop = True
115115
if_exists = False
116+
pg_options = None
116117

117118
def _create_dump(self):
118119
cmd = f"{self.dump_cmd} "
@@ -129,22 +130,64 @@ def _create_dump(self):
129130
stdout, _ = self.run_command(cmd, env=self.dump_env)
130131
return stdout
131132

132-
def _restore_dump(self, dump):
133+
def _restore_dump(self, dump: str):
134+
"""
135+
Restore a PostgreSQL dump using subprocess with argument list.
136+
137+
Assumes that restore_prefix, restore_cmd, pg_options, and restore_suffix
138+
are either None, strings (single args), or lists of strings.
139+
140+
Builds the command as a list.
141+
"""
142+
133143
dbname = create_postgres_uri(self)
134-
cmd = f"{self.restore_cmd} {dbname}"
144+
cmd = []
145+
146+
# Flatten optional values
147+
if self.restore_prefix:
148+
cmd.extend(
149+
self.restore_prefix
150+
if isinstance(self.restore_prefix, list)
151+
else [self.restore_prefix]
152+
)
153+
154+
if self.restore_cmd:
155+
cmd.extend(
156+
self.restore_cmd
157+
if isinstance(self.restore_cmd, list)
158+
else [self.restore_cmd]
159+
)
160+
161+
if self.pg_options:
162+
cmd.extend(
163+
self.pg_options
164+
if isinstance(self.pg_options, list)
165+
else [self.pg_options]
166+
)
167+
168+
cmd.extend([dbname])
135169

136170
if self.single_transaction:
137-
cmd += " --single-transaction"
171+
cmd.extend(["--single-transaction"])
138172

139173
if self.drop:
140-
cmd += " --clean"
174+
cmd.extend(["--clean"])
141175

142176
if self.schemas:
143-
cmd += " -n " + " -n ".join(self.schemas)
177+
for schema in self.schemas:
178+
cmd.extend(["-n", schema])
144179

145180
if self.if_exists:
146-
cmd += " --if-exists"
181+
cmd.extend(["--if-exists"])
147182

148-
cmd = f"{self.restore_prefix} {cmd} {self.restore_suffix}"
149-
stdout, stderr = self.run_command(cmd, stdin=dump, env=self.restore_env)
150-
return stdout, stderr
183+
if self.restore_suffix:
184+
cmd.extend(
185+
self.restore_suffix
186+
if isinstance(self.restore_suffix, list)
187+
else [self.restore_suffix]
188+
)
189+
190+
cmd_str = " ".join(cmd)
191+
stdout, _ = self.run_command(cmd_str, stdin=dump, env=self.dump_env)
192+
193+
return stdout

dbbackup/management/commands/dbrestore.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Command(BaseDbBackupCommand):
1616
help = "Restore a database backup from storage, encrypted and/or compressed."
1717
content_type = "db"
1818
no_drop = False
19+
pg_options = ""
1920

2021
option_list = BaseDbBackupCommand.option_list + (
2122
make_option("-d", "--database", help="Database to restore"),
@@ -60,6 +61,12 @@ class Command(BaseDbBackupCommand):
6061
default=False,
6162
help="Don't clean (drop) the database. This only works with mongodb and postgresql.",
6263
),
64+
make_option(
65+
"--pg-options",
66+
dest="pg_options",
67+
default="",
68+
help="Additional pg_restore options, e.g. '--if-exists --no-owner'. Use quotes.",
69+
),
6370
)
6471

6572
def handle(self, *args, **options):
@@ -83,6 +90,7 @@ def handle(self, *args, **options):
8390
)
8491
self.storage = get_storage()
8592
self.no_drop = options.get("no_drop")
93+
self.pg_options = options.get("pg_options", "")
8694
self.schemas = options.get("schema")
8795
self._restore_backup()
8896
except StorageError as err:
@@ -141,4 +149,5 @@ def _restore_backup(self):
141149
if self.schemas:
142150
self.connector.schemas = self.schemas
143151
self.connector.drop = not self.no_drop
152+
self.connector.pg_options = self.pg_options
144153
self.connector.restore_dump(input_file)

dbbackup/tests/test_connectors/test_postgresql.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,27 @@ def test_create_dump_if_exists(self, mock_run_command):
223223
self.connector.restore_dump(dump)
224224
self.assertIn(" --if-exists", mock_run_command.call_args[0][0])
225225

226+
def test_pg_options(self, mock_run_command):
227+
dump = self.connector.create_dump()
228+
self.connector.pg_options = "--foo"
229+
self.connector.restore_dump(dump)
230+
cmd_args = mock_run_command.call_args[0][0]
231+
self.assertIn("--foo", cmd_args)
232+
233+
def test_restore_prefix(self, mock_run_command):
234+
dump = self.connector.create_dump()
235+
self.connector.restore_prefix = "foo"
236+
self.connector.restore_dump(dump)
237+
cmd_args = mock_run_command.call_args[0][0]
238+
self.assertTrue(cmd_args.startswith("foo "))
239+
240+
def test_restore_suffix(self, mock_run_command):
241+
dump = self.connector.create_dump()
242+
self.connector.restore_suffix = "foo"
243+
self.connector.restore_dump(dump)
244+
cmd_args = mock_run_command.call_args[0][0]
245+
self.assertTrue(cmd_args.endswith(" foo"))
246+
226247
@patch(
227248
"dbbackup.db.postgresql.PgDumpBinaryConnector.run_command",
228249
return_value=(BytesIO(), BytesIO()),

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44
Unreleased
55
----------
66

7+
* Add generic `--pg-options` to pass custom options to postgres.
78
* Add option `--if-exists` for pg_dump command
89
* Empty string as HOST for postgres unix domain socket connection is now supported.
910
* Support Python 3.13 and Django 5.2

0 commit comments

Comments
 (0)