From 5ff016ab8d8b75c64e6a51cadd981cc0fb09a964 Mon Sep 17 00:00:00 2001 From: "Andrey M. Borodin" Date: Thu, 2 Feb 2023 09:50:21 -0800 Subject: [PATCH] Add GUCs to preven some accidental massive DML Add 2 new GUCs prevent_unqualified_deletes and prevent_unqualified_updates to prevent user from accidental DELETEs and UPDATEs accordingly. Both are off by default. --- src/backend/optimizer/plan/planner.c | 23 ++++++++++++++++++- src/backend/utils/misc/guc_tables.c | 18 +++++++++++++++ src/backend/utils/misc/postgresql.conf.sample | 3 +++ src/include/optimizer/planmain.h | 2 ++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index db5ff6fdca..441bd23f44 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -71,7 +71,9 @@ /* GUC parameters */ double cursor_tuple_fraction = DEFAULT_CURSOR_TUPLE_FRACTION; int force_parallel_mode = FORCE_PARALLEL_OFF; -bool parallel_leader_participation = true; +bool parallel_leader_participation = true; +bool prevent_unqualified_deletes = false; +bool prevent_unqualified_updates = false; /* Hook for plugins to get control in planner() */ planner_hook_type planner_hook = NULL; @@ -279,6 +281,25 @@ planner(Query *parse, const char *query_string, int cursorOptions, result = (*planner_hook) (parse, query_string, cursorOptions, boundParams); else result = standard_planner(parse, query_string, cursorOptions, boundParams); + if (!superuser() && parse->jointree != NULL) + { + if (prevent_unqualified_deletes && parse->commandType == CMD_DELETE) + { + if (parse->jointree->quals == NULL) + { + elog(ERROR,"DELETE query with empty WHERE is forbidden by" + " some_fancy_guc"); + } + } + if (prevent_unqualified_updates && parse->commandType == CMD_UPDATE) + { + if (parse->jointree->quals == NULL) + { + elog(ERROR,"UPDATE query with empty WHERE is forbidden by" + " some_fancy_guc"); + } + } + } return result; } diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index b46e3b8c55..df5f6d9ee4 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -875,6 +875,24 @@ struct config_bool ConfigureNamesBool[] = true, NULL, NULL, NULL }, + { + {"prevent_unqualified_deletes", PGC_USERSET, UNGROUPED, + gettext_noop("Some proper description."), + NULL + }, + &prevent_unqualified_deletes, + false, + NULL, NULL, NULL + }, + { + {"prevent_unqualified_updates", PGC_USERSET, UNGROUPED, + gettext_noop("Some more proper description."), + NULL + }, + &prevent_unqualified_updates, + false, + NULL, NULL, NULL + }, { {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of memoization."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d06074b86f..66aa9be3af 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -391,6 +391,9 @@ #enable_sort = on #enable_tidscan = on +#enable prevent_unqualified_deletes = off +#enable prevent_unqualified_updates = off + # - Planner Cost Constants - #seq_page_cost = 1.0 # measured on an arbitrary scale diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 5fc900737d..098a555dc4 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -20,6 +20,8 @@ /* GUC parameters */ #define DEFAULT_CURSOR_TUPLE_FRACTION 0.1 extern PGDLLIMPORT double cursor_tuple_fraction; +extern PGDLLIMPORT bool prevent_unqualified_deletes; +extern PGDLLIMPORT bool prevent_unqualified_updates; /* query_planner callback to compute query_pathkeys */ typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra); -- 2.37.3