1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

pg_plan_advice: Add CHECK_FOR_INTERRUPTS() and check_stack_depth()

Without CHECK_FOR_INTERRUPTS(), backends might not respond to query
cancellations in a timely fashion, especially for JOIN_ORDER()
specifications, where matching against each join can take O(n^2) time
in the length of the join order list in degenerate cases.

Recursive functions should check_stack_depth().

Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://postgr.es/m/CA+TgmoYmXy-jiP5qDhqNEiYFEBzQsArO6O2d9E8szNZqi1bePQ@mail.gmail.com
Backpatch-through: 19
This commit is contained in:
Robert Haas
2026-09-09 17:48:54 -04:00
parent d2b65863f0
commit 1129570f65
3 changed files with 24 additions and 0 deletions

View File

@@ -15,6 +15,7 @@
#include "pgpa_ast.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
@@ -169,6 +170,8 @@ pgpa_parse_advice_tag(const char *tag, bool *fail)
void
pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
{
check_stack_depth();
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
bool first = true;
@@ -234,6 +237,8 @@ pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
bool
pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
{
check_stack_depth();
/* For non-identifiers, check all descendants. */
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
@@ -288,6 +293,13 @@ pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
bool all_targets_used;
bool *rids_used = palloc0_array(bool, nrids);
/*
* This function is called from within various loops within pgpa_planner.c;
* to avoid needing a separate CHECK_FOR_INTERRUPTS() in each one, we check
* here instead.
*/
CHECK_FOR_INTERRUPTS();
all_targets_used =
pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
@@ -330,6 +342,8 @@ pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
{
bool result = false;
check_stack_depth();
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
result = true;

View File

@@ -25,6 +25,7 @@
#include "commands/defrem.h"
#include "common/hashfn_unstable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/extendplan.h"
#include "optimizer/pathnode.h"
@@ -1649,6 +1650,8 @@ pgpa_planner_apply_scan_advice(RelOptInfo *rel,
pgpa_trove_entry *my_entry = &scan_entries[i];
uint64 my_scan_type = all_scan_mask;
CHECK_FOR_INTERRUPTS();
/* Translate our advice tags to a scan strategy advice value. */
if (my_entry->tag == PGPA_TAG_DO_NOT_SCAN)
my_scan_type = 0;
@@ -1711,6 +1714,8 @@ pgpa_planner_apply_scan_advice(RelOptInfo *rel,
uint64 my_gather_mask = 0;
bool just_one_rel;
CHECK_FOR_INTERRUPTS();
just_one_rel = my_entry->target->ttype == PGPA_TARGET_IDENTIFIER
|| list_length(my_entry->target->children) == 1;
@@ -1882,6 +1887,8 @@ pgpa_planner_append_feedback(List *list, pgpa_trove *trove,
pgpa_trove_entry *entry = &entries[i];
DefElem *item;
CHECK_FOR_INTERRUPTS();
/*
* If this entry was fully matched, check whether generating advice
* from this plan would produce such an entry. If not, label the entry

View File

@@ -27,6 +27,7 @@
#include "pgpa_trove.h"
#include "common/hashfn_unstable.h"
#include "miscadmin.h"
/*
* An advice trove is organized into a series of "slices", each of which
@@ -402,6 +403,8 @@ pgpa_trove_add_to_hash(pgpa_trove_entry_hash *hash, pgpa_advice_target *target,
pgpa_trove_entry_element *element;
bool found;
check_stack_depth();
/* For non-identifiers, add entries for all descendants. */
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{