diff --git a/NEWS b/NEWS index 0b1e303c..50063cbf 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ 1.7.7 + * #642: Add MySQL database hook "add_drop_database" configuration option to control whether dumped + MySQL databases get dropped right before restore. * #643: Fix for potential data loss (data not getting backed up) when dumping large "directory" format PostgreSQL/MongoDB databases. Prior to the fix, these dumps would not finish writing to disk before Borg consumed them. Now, the dumping process completes before Borg starts. This only diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 2a46da93..02bd5d6a 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -892,6 +892,13 @@ properties: file of that format, allowing more convenient restores of individual databases. example: directory + add_drop_database: + type: boolean + description: | + Use the "--add-drop-database" flag with + mysqldump, causing the database to be dropped + right before restore. Defaults to true. + example: false options: type: string description: | diff --git a/borgmatic/hooks/mysql.py b/borgmatic/hooks/mysql.py index 9c7871cb..ef2c3366 100644 --- a/borgmatic/hooks/mysql.py +++ b/borgmatic/hooks/mysql.py @@ -81,7 +81,7 @@ def execute_dump_command( dump_command = ( ('mysqldump',) + (tuple(database['options'].split(' ')) if 'options' in database else ()) - + ('--add-drop-database',) + + (('--add-drop-database',) if database.get('add_drop_database', True) else ()) + (('--host', database['hostname']) if 'hostname' in database else ()) + (('--port', str(database['port'])) if 'port' in database else ()) + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ()) diff --git a/tests/unit/hooks/test_mysql.py b/tests/unit/hooks/test_mysql.py index 9722bdf5..3befab70 100644 --- a/tests/unit/hooks/test_mysql.py +++ b/tests/unit/hooks/test_mysql.py @@ -159,6 +159,33 @@ def test_execute_dump_command_runs_mysqldump(): ) +def test_execute_dump_command_runs_mysqldump_without_add_drop_database(): + process = flexmock() + flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump') + flexmock(module.os.path).should_receive('exists').and_return(False) + flexmock(module.dump).should_receive('create_named_pipe_for_dump') + + flexmock(module).should_receive('execute_command').with_args( + ('mysqldump', '--databases', 'foo', '>', 'dump',), + shell=True, + extra_environment=None, + run_to_completion=False, + ).and_return(process).once() + + assert ( + module.execute_dump_command( + database={'name': 'foo', 'add_drop_database': False}, + log_prefix='log', + dump_path=flexmock(), + database_names=('foo',), + extra_environment=None, + dry_run=False, + dry_run_label='', + ) + == process + ) + + def test_execute_dump_command_runs_mysqldump_with_hostname_and_port(): process = flexmock() flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')