diff options
author | Amjith Ramanujam <amjith.r@gmail.com> | 2023-03-17 19:05:10 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-17 19:05:10 -0700 |
commit | 5e34e0b55766099a2965eaa61e77795fa577f9f4 (patch) | |
tree | 551f3456e866b920bcee3de454ec622e4f032bed | |
parent | 8ef5392fd1f50591aaaa0a6ea204f7545edceddd (diff) | |
parent | a93442aed71d573b7bac56d2e14ad7f7f9a66ddc (diff) |
Merge pull request #1397 from dbaty/dbaty/require_transaction_for_destructive_statement
feature: Add config option to require a transaction for destructive statements
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | changelog.rst | 2 | ||||
-rw-r--r-- | pgcli/main.py | 40 | ||||
-rw-r--r-- | pgcli/pgclirc | 8 |
4 files changed, 38 insertions, 13 deletions
@@ -126,6 +126,7 @@ Contributors: * Rigo Neri (rigoneri) * Anna Glasgall (annathyst) * Andy Schoenberger (andyscho) + * Damien Baty (dbaty) Creator: -------- diff --git a/changelog.rst b/changelog.rst index a8b268b0..af4d6db4 100644 --- a/changelog.rst +++ b/changelog.rst @@ -4,6 +4,8 @@ Upcoming Features: --------- +* New `destructive_statements_require_transaction` config option to refuse to execute a + destructive SQL statement if outside a transaction. This option is off by default. * Changed the `destructive_warning` config to be a list of commands that are considered destructive. This would allow you to be warned on `create`, `grant`, or `insert` queries. * Destructive warnings will now include the alias dsn connection string name if provided (-D option). diff --git a/pgcli/main.py b/pgcli/main.py index 5626a5ed..5a263f56 100644 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -64,15 +64,16 @@ from .config import ( from .key_bindings import pgcli_bindings from .packages.formatter.sqlformatter import register_new_formatter from .packages.prompt_utils import confirm, confirm_destructive_query +from .packages.parseutils import is_destructive from .packages.parseutils import parse_destructive_warning from .__init__ import __version__ click.disable_unicode_literals_warning = True try: - from urlparse import urlparse, unquote, parse_qs + from urlparse import urlparse except ImportError: - from urllib.parse import urlparse, unquote, parse_qs + from urllib.parse import urlparse from getpass import getuser @@ -234,6 +235,9 @@ class PGCli: self.destructive_warning_restarts_connection = c["main"].as_bool( "destructive_warning_restarts_connection" ) + self.destructive_statements_require_transaction = c["main"].as_bool( + "destructive_statements_require_transaction" + ) self.less_chatty = bool(less_chatty) or c["main"].as_bool("less_chatty") self.null_string = c["main"].get("null_string", "<null>") @@ -432,15 +436,20 @@ class PGCli: except OSError as e: return [(None, None, None, str(e), "", False, True)] - if ( - self.destructive_warning - and confirm_destructive_query( + if self.destructive_warning: + if ( + self.destructive_statements_require_transaction + and not self.pgexecute.valid_transaction() + and is_destructive(query, self.destructive_warning) + ): + message = "Destructive statements must be run within a transaction. Command execution stopped." + return [(None, None, None, message)] + destroy = confirm_destructive_query( query, self.destructive_warning, self.dsn_alias ) - is False - ): - message = "Wise choice. Command execution stopped." - return [(None, None, None, message)] + if destroy is False: + message = "Wise choice. Command execution stopped." + return [(None, None, None, message)] on_error_resume = self.on_error == "RESUME" return self.pgexecute.run( @@ -706,7 +715,16 @@ class PGCli: try: if self.destructive_warning: - destroy = confirm = confirm_destructive_query( + if ( + self.destructive_statements_require_transaction + and not self.pgexecute.valid_transaction() + and is_destructive(text, self.destructive_warning) + ): + click.secho( + "Destructive statements must be run within a transaction." + ) + raise KeyboardInterrupt + destroy = confirm_destructive_query( text, self.destructive_warning, self.dsn_alias ) if destroy is False: @@ -735,7 +753,7 @@ class PGCli: click.secho(str(e), err=True, fg="red") if handle_closed_connection: self._handle_server_closed_connection(text) - except (PgCliQuitError, EOFError) as e: + except (PgCliQuitError, EOFError): raise except Exception as e: logger.error("sql: %r, error: %r", text, e) diff --git a/pgcli/pgclirc b/pgcli/pgclirc index b32eda0d..2d0563f1 100644 --- a/pgcli/pgclirc +++ b/pgcli/pgclirc @@ -28,17 +28,21 @@ multi_line_mode = psql # Destructive warning will alert you before executing a sql statement # that may cause harm to the database such as "drop table", "drop database", -# "shutdown", "delete", or "update". +# "shutdown", "delete", or "update". # You can pass a list of destructive commands or leave it empty if you want to skip all warnings. # "unconditional_update" will warn you of update statements that don't have a where clause destructive_warning = drop, shutdown, delete, truncate, alter, update, unconditional_update # Destructive warning can restart the connection if this is enabled and the # user declines. This means that any current uncommitted transaction can be -# aborted if the user doesn't want to proceed with a destructive_warning +# aborted if the user doesn't want to proceed with a destructive_warning # statement. destructive_warning_restarts_connection = False +# When this option is on (and if `destructive_warning` is not empty), +# destructive statements are not executed when outside of a transaction. +destructive_statements_require_transaction = False + # Enables expand mode, which is similar to `\x` in psql. expand = False |