For the MariaDB and MySQL hooks, add "events", "routines", and "tablespaces" options for disabling dumping of scheduled events, stored routines, and tablespaces, respectively (#1324).

This commit is contained in:
Dan Helfman
2026-06-17 16:26:01 -07:00
parent b14425b2af
commit 07d5a0129c
6 changed files with 377 additions and 21 deletions
+2
View File
@@ -8,6 +8,8 @@
"/".
* #1322: Fix for the "restore" action sometimes failing to find a database dump that was dumped
with a default port.
* #1324: For the MariaDB and MySQL hooks, add "events", "routines", and "tablespaces" options for
disabling dumping of scheduled events, stored routines, and tablespaces, respectively.
* Fix a bug in which the "compact" action does not pass a compact threshold of zero to Borg.
2.1.6
+42
View File
@@ -1849,6 +1849,27 @@ properties:
client and restore server. The default varies based on
the MariaDB version.
example: false
events:
type: boolean
description: |
Whether to include scheduled events within the dump.
Disable if your database user doesn't have the
permissions to dump events. Defaults to true.
example: false
routines:
type: boolean
description: |
Whether to include stored routines within the dump.
Disable if your user database doesn't have the
permissions to dump routines. Defaults to true.
example: false
tablespaces:
type: boolean
description: |
Whether to include tablespaces within the dump. Disable
if your database user doesn't have the permissions to
dump tablespaces. Defaults to true.
example: false
mariadb_dump_command:
type: string
description: |
@@ -2057,6 +2078,27 @@ properties:
client and restore server. The default varies based on
the MySQL installation.
example: false
events:
type: boolean
description: |
Whether to include scheduled events within the dump.
Disable if your database user doesn't have the
permissions to dump events. Defaults to true.
example: false
routines:
type: boolean
description: |
Whether to include stored routines within the dump.
Disable if your database user doesn't have the
permissions to dump routines. Defaults to true.
example: false
tablespaces:
type: boolean
description: |
Whether to include tablespaces within the dump. Disable
if your database user doesn't have the permissions to
dump tablespaces. Defaults to true.
example: false
mysql_dump_command:
type: string
description: |
+4 -1
View File
@@ -241,8 +241,11 @@ def execute_dump_command(
+ (('--user', username) if username and password_transport == 'environment' else ())
+ (('--ssl',) if database.get('tls') is True else ())
+ (('--skip-ssl',) if database.get('tls') is False else ())
+ ('--databases', '--events', '--routines', '--all-tablespaces')
+ (('--events',) if database.get('events', True) else ())
+ (('--routines',) if database.get('routines', True) else ())
+ (('--all-tablespaces',) if database.get('tablespaces', True) else ())
+ (('--system=users,udfs,servers',) if SYSTEM_DATABASE_NAME in database_names else ())
+ ('--databases',)
+ tuple(name for name in database_names if name != SYSTEM_DATABASE_NAME)
+ ('--result-file', dump_filename)
)
+4 -1
View File
@@ -165,7 +165,10 @@ def execute_dump_command(
+ (('--user', username) if username and password_transport == 'environment' else ())
+ (('--ssl',) if database.get('tls') is True else ())
+ (('--skip-ssl',) if database.get('tls') is False else ())
+ ('--databases', '--events', '--routines', '--all-tablespaces')
+ (('--events',) if database.get('events', True) else ())
+ (('--routines',) if database.get('routines', True) else ())
+ (('--all-tablespaces',) if database.get('tablespaces', True) else ())
+ ('--databases',)
+ database_names
+ ('--result-file', dump_filename)
)
+163 -10
View File
@@ -855,10 +855,10 @@ def test_execute_dump_command_runs_mariadb_dump():
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -907,11 +907,11 @@ def test_execute_dump_command_substitutes_system_flag_for_system_database_name()
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--system=users,udfs,servers',
'--databases',
'--result-file',
'dump',
),
@@ -956,10 +956,10 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
'--single-transaction',
'--user',
'root',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1007,10 +1007,10 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
'mariadb-dump',
'--defaults-extra-file=/dev/fd/99',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1067,10 +1067,10 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
'5433',
'--protocol',
'tcp',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1120,10 +1120,10 @@ def test_execute_dump_command_runs_mariadb_dump_with_tls():
'--add-drop-database',
'--single-transaction',
'--ssl',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1173,10 +1173,10 @@ def test_execute_dump_command_runs_mariadb_dump_without_tls():
'--add-drop-database',
'--single-transaction',
'--skip-ssl',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1202,6 +1202,159 @@ def test_execute_dump_command_runs_mariadb_dump_without_tls():
)
def test_execute_dump_command_runs_mariadb_dump_without_events():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module).should_receive('parse_extra_options').and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module).should_receive('make_defaults_file_options').with_args(
'root',
'trustsome1',
None,
).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mariadb-dump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'events': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mariadb_dump_without_routines():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module).should_receive('parse_extra_options').and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module).should_receive('make_defaults_file_options').with_args(
'root',
'trustsome1',
None,
).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mariadb-dump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--events',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'routines': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mariadb_dump_without_tablespaces():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module).should_receive('parse_extra_options').and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module).should_receive('make_defaults_file_options').with_args(
'root',
'trustsome1',
None,
).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mariadb-dump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--events',
'--routines',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'tablespaces': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
@@ -1225,10 +1378,10 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1278,10 +1431,10 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
'--stuff=such',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1331,10 +1484,10 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
'--stuff=such',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
+162 -9
View File
@@ -718,10 +718,10 @@ def test_execute_dump_command_runs_mysqldump():
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -771,10 +771,10 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
'--single-transaction',
'--user',
'root',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -822,10 +822,10 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
'mysqldump',
'--defaults-extra-file=/dev/fd/99',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -882,10 +882,10 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
'5433',
'--protocol',
'tcp',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -935,10 +935,10 @@ def test_execute_dump_command_runs_mysqldump_with_tls():
'--add-drop-database',
'--single-transaction',
'--ssl',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -988,10 +988,10 @@ def test_execute_dump_command_runs_mysqldump_without_tls():
'--add-drop-database',
'--single-transaction',
'--skip-ssl',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1017,6 +1017,159 @@ def test_execute_dump_command_runs_mysqldump_without_tls():
)
def test_execute_dump_command_runs_mysqldump_without_events():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'parse_extra_options',
).and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'make_defaults_file_options',
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mysqldump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'events': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mysqldump_without_routines():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'parse_extra_options',
).and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'make_defaults_file_options',
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mysqldump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--events',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'routines': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mysqldump_without_tablespaces():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential',
).replace_with(lambda value, config: value)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'parse_extra_options',
).and_return((), None)
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
'make_defaults_file_options',
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module).should_receive('execute_command').with_args(
(
'mysqldump',
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--events',
'--routines',
'--databases',
'foo',
'--result-file',
'dump',
),
environment=None,
run_to_completion=False,
working_directory=None,
).and_return(process).once()
assert (
module.execute_dump_command(
database={'name': 'foo', 'tablespaces': False},
config={},
username='root',
password='trustsome1',
dump_path=flexmock(),
database_names=('foo',),
environment=None,
dry_run=False,
dry_run_label='',
)
== process
)
def test_execute_dump_command_runs_mysqldump_with_username_and_password():
process = flexmock()
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
@@ -1040,10 +1193,10 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1093,10 +1246,10 @@ def test_execute_dump_command_runs_mysqldump_with_options():
'--stuff=such',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',
@@ -1145,10 +1298,10 @@ def test_execute_dump_command_runs_non_default_mysqldump():
'--defaults-extra-file=/dev/fd/99',
'--add-drop-database',
'--single-transaction',
'--databases',
'--events',
'--routines',
'--all-tablespaces',
'--databases',
'foo',
'--result-file',
'dump',