mirror of
https://github.com/hanzoai/postgres.git
synced 2026-10-30 18:33:07 +00:00
Provide a facility that (1) can be used to stabilize certain plan choices so that the planner cannot reverse course without authorization and (2) can be used by knowledgeable users to insist on plan choices contrary to what the planner believes best. In both cases, terrible outcomes are possible: users should think twice and perhaps three times before constraining the planner's ability to do as it thinks best; nevertheless, there are problems that are much more easily solved with these facilities than without them. This patch takes the approach of analyzing a finished plan to produce textual output, which we call "plan advice", that describes key decisions made during plan; if that plan advice is provided during future planning cycles, it will force those key decisions to be made in the same way. Not all planner decisions can be controlled using advice; for example, decisions about how to perform aggregation are currently out of scope, as is choice of sort order. Plan advice can also be edited by the user, or even written from scratch in simple cases, making it possible to generate outcomes that the planner would not have produced. Partial advice can be provided to control some planner outcomes but not others. Currently, plan advice is focused only on specific outcomes, such as the choice to use a sequential scan for a particular relation, and not on estimates that might contribute to those outcomes, such as a possibly-incorrect selectivity estimate. While it would be useful to users to be able to provide plan advice that affects selectivity estimates or other aspects of costing, that is out of scope for this commit. Reviewed-by: Lukas Fittl <[email protected]> Reviewed-by: Jakub Wartak <[email protected]> Reviewed-by: Greg Burd <[email protected]> Reviewed-by: Jacob Champion <[email protected]> Reviewed-by: Haibo Yan <[email protected]> Reviewed-by: Dian Fay <[email protected]> Reviewed-by: Ajay Pal <[email protected]> Reviewed-by: John Naylor <[email protected]> Reviewed-by: Alexandra Wang <[email protected]> Discussion: http://postgr.es/m/CA+TgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4=ZHoKPzw@mail.gmail.com
106 lines
3.4 KiB
C
106 lines
3.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pgpa_join.h
|
|
* analysis of joins in Plan trees
|
|
*
|
|
* Copyright (c) 2016-2026, PostgreSQL Global Development Group
|
|
*
|
|
* contrib/pg_plan_advice/pgpa_join.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef PGPA_JOIN_H
|
|
#define PGPA_JOIN_H
|
|
|
|
#include "nodes/plannodes.h"
|
|
|
|
typedef struct pgpa_plan_walker_context pgpa_plan_walker_context;
|
|
typedef struct pgpa_join_unroller pgpa_join_unroller;
|
|
typedef struct pgpa_unrolled_join pgpa_unrolled_join;
|
|
|
|
/*
|
|
* Although there are three main join strategies, we try to classify things
|
|
* more precisely here: merge joins have the option of using materialization
|
|
* on the inner side, and nested loops can use either materialization or
|
|
* memoization.
|
|
*/
|
|
typedef enum
|
|
{
|
|
JSTRAT_MERGE_JOIN_PLAIN = 0,
|
|
JSTRAT_MERGE_JOIN_MATERIALIZE,
|
|
JSTRAT_NESTED_LOOP_PLAIN,
|
|
JSTRAT_NESTED_LOOP_MATERIALIZE,
|
|
JSTRAT_NESTED_LOOP_MEMOIZE,
|
|
JSTRAT_HASH_JOIN
|
|
/* update NUM_PGPA_JOIN_STRATEGY if you add anything here */
|
|
} pgpa_join_strategy;
|
|
|
|
#define NUM_PGPA_JOIN_STRATEGY ((int) JSTRAT_HASH_JOIN + 1)
|
|
|
|
/*
|
|
* In an outer-deep join tree, every member of an unrolled join will be a scan,
|
|
* but join trees with other shapes can contain unrolled joins.
|
|
*
|
|
* The plan node we store here will be the inner or outer child of the join
|
|
* node, as appropriate, except that we look through subnodes that we regard as
|
|
* part of the join method itself. For instance, for a Nested Loop that
|
|
* materializes the inner input, we'll store the child of the Materialize node,
|
|
* not the Materialize node itself.
|
|
*
|
|
* If setrefs processing elided one or more nodes from the plan tree, then
|
|
* we'll store details about the topmost of those in elided_node; otherwise,
|
|
* it will be NULL.
|
|
*
|
|
* Exactly one of scan and unrolled_join will be non-NULL.
|
|
*/
|
|
typedef struct
|
|
{
|
|
Plan *plan;
|
|
ElidedNode *elided_node;
|
|
struct pgpa_scan *scan;
|
|
pgpa_unrolled_join *unrolled_join;
|
|
} pgpa_join_member;
|
|
|
|
/*
|
|
* We convert outer-deep join trees to a flat structure; that is, ((A JOIN B)
|
|
* JOIN C) JOIN D gets converted to outer = A, inner = <B C D>. When joins
|
|
* aren't outer-deep, substructure is required, e.g. (A JOIN B) JOIN (C JOIN D)
|
|
* is represented as outer = A, inner = <B X>, where X is a pgpa_unrolled_join
|
|
* covering C-D.
|
|
*/
|
|
struct pgpa_unrolled_join
|
|
{
|
|
/* Outermost member; must not itself be an unrolled join. */
|
|
pgpa_join_member outer;
|
|
|
|
/* Number of inner members. Length of the strategy and inner arrays. */
|
|
unsigned ninner;
|
|
|
|
/* Array of strategies, one per non-outermost member. */
|
|
pgpa_join_strategy *strategy;
|
|
|
|
/* Array of members, excluding the outermost. Deepest first. */
|
|
pgpa_join_member *inner;
|
|
};
|
|
|
|
/*
|
|
* Does this plan node inherit from Join?
|
|
*/
|
|
static inline bool
|
|
pgpa_is_join(Plan *plan)
|
|
{
|
|
return IsA(plan, NestLoop) || IsA(plan, MergeJoin) || IsA(plan, HashJoin);
|
|
}
|
|
|
|
extern pgpa_join_unroller *pgpa_create_join_unroller(void);
|
|
extern void pgpa_unroll_join(pgpa_plan_walker_context *walker,
|
|
Plan *plan, bool beneath_any_gather,
|
|
pgpa_join_unroller *join_unroller,
|
|
pgpa_join_unroller **outer_join_unroller,
|
|
pgpa_join_unroller **inner_join_unroller);
|
|
extern pgpa_unrolled_join *pgpa_build_unrolled_join(pgpa_plan_walker_context *walker,
|
|
pgpa_join_unroller *join_unroller);
|
|
extern void pgpa_destroy_join_unroller(pgpa_join_unroller *join_unroller);
|
|
|
|
#endif
|