Fix the PostgreSQL database hook to properly parse "*options" values containing quoted spaces.

This commit is contained in:
Dan Helfman
2026-04-24 19:24:44 -07:00
parent 524d8263a7
commit 468af1de0b
3 changed files with 27 additions and 10 deletions
+1
View File
@@ -5,6 +5,7 @@
* For the MariaDB and MySQL database hooks, escape quotes in passwords when the
"password_transport" option is "pipe".
* Add a development script for upgrading pinned dependencies.
* Fix the PostgreSQL database hook to properly parse "*options" values containing quoted spaces.
2.1.5
* #1229: Document the permissions needed for the PostgreSQL database hook:
+8 -4
View File
@@ -100,7 +100,11 @@ def database_names_to_dump(database, config, environment, dry_run):
if 'username' in database
else ()
)
+ (tuple(database['list_options'].split(' ')) if 'list_options' in database else ())
+ (
tuple(shlex.quote(part) for part in shlex.split(database['list_options']))
if 'list_options' in database
else ()
)
)
logger.debug('Querying for "all" PostgreSQL databases to dump')
list_lines = execute_command_and_capture_output(
@@ -226,7 +230,7 @@ def dump_data_sources(
+ (('--compress', shlex.quote(str(compression))) if compression is not None else ())
+ (('--file', shlex.quote(dump_filename)) if dump_format == 'directory' else ())
+ (
tuple(shlex.quote(option) for option in database['options'].split(' '))
tuple(shlex.quote(part) for part in shlex.split(database['options']))
if 'options' in database
else ()
)
@@ -393,7 +397,7 @@ def restore_data_source_dump(
+ (('--username', username) if username else ())
+ (('--dbname', data_source['name']) if not all_databases else ())
+ (
tuple(data_source['analyze_options'].split(' '))
tuple(shlex.quote(part) for part in shlex.split(data_source['analyze_options']))
if 'analyze_options' in data_source
else ()
)
@@ -414,7 +418,7 @@ def restore_data_source_dump(
+ (('--username', username) if username else ())
+ (('--no-owner',) if data_source.get('no_owner', False) else ())
+ (
tuple(data_source['restore_options'].split(' '))
tuple(shlex.quote(part) for part in shlex.split(data_source['restore_options']))
if 'restore_options' in data_source
else ()
)
@@ -172,13 +172,22 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_usernam
def test_database_names_to_dump_with_all_and_format_lists_databases_with_options():
database = {'name': 'all', 'format': 'custom', 'list_options': '--harder'}
database = {'name': 'all', 'format': 'custom', 'list_options': '--harder "foo bar"'}
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
(
'psql',
'--list',
'--no-password',
'--no-psqlrc',
'--csv',
'--tuples-only',
'--harder',
"'foo bar'",
),
environment=object,
working_directory=None,
).and_yield('foo,test,', 'bar,test,"stuff and such"')
@@ -803,7 +812,7 @@ def test_dump_data_sources_runs_pg_dump_with_integer_compression():
def test_dump_data_sources_runs_pg_dump_with_options():
databases = [{'name': 'foo', 'options': '--stuff=such'}]
databases = [{'name': 'foo', 'options': '--stuff "foo bar"'}]
process = flexmock()
flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
flexmock(module).should_receive('make_dump_path').and_return('')
@@ -826,7 +835,8 @@ def test_dump_data_sources_runs_pg_dump_with_options():
'--if-exists',
'--format',
'custom',
'--stuff=such',
'--stuff',
"'foo bar'",
'foo',
'>',
'databases/localhost/foo',
@@ -1340,8 +1350,8 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
hook_config = [
{
'name': 'foo',
'restore_options': '--harder',
'analyze_options': '--smarter',
'restore_options': '--harder "foo bar"',
'analyze_options': '--smarter "baz quux"',
'schemas': None,
},
]
@@ -1364,6 +1374,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
'--dbname',
'foo',
'--harder',
"'foo bar'",
),
processes=[extract_process],
output_log_level=logging.DEBUG,
@@ -1381,6 +1392,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
'--dbname',
'foo',
'--smarter',
"'baz quux'",
'--command',
'ANALYZE',
),