mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-19 04:46:59 +08:00
Restore foreign-key-aware estimation of join relation sizes.
This patch provides a new implementation of the logic added by commit 137805f89 and later removed by 77ba61080. It differs from the original primarily in expending much less effort per joinrel in large queries, which it accomplishes by doing most of the matching work once per query not once per joinrel. Hopefully, it's also less buggy and better commented. The never-documented enable_fkey_estimates GUC remains gone. There remains work to be done to make the selectivity estimates account for nulls in FK referencing columns; but that was true of the original patch as well. We may be able to address this point later in beta. In the meantime, any error should be in the direction of overestimating rather than underestimating joinrel sizes, which seems like the direction we want to err in. Tomas Vondra and Tom Lane Discussion: <31041.1465069446@sss.pgh.pa.us>
This commit is contained in:
@ -223,6 +223,7 @@ typedef enum NodeTag
|
||||
T_PlannerGlobal,
|
||||
T_RelOptInfo,
|
||||
T_IndexOptInfo,
|
||||
T_ForeignKeyOptInfo,
|
||||
T_ParamPathInfo,
|
||||
T_Path,
|
||||
T_IndexPath,
|
||||
@ -478,7 +479,8 @@ typedef enum NodeTag
|
||||
T_InlineCodeBlock, /* in nodes/parsenodes.h */
|
||||
T_FdwRoutine, /* in foreign/fdwapi.h */
|
||||
T_IndexAmRoutine, /* in access/amapi.h */
|
||||
T_TsmRoutine /* in access/tsmapi.h */
|
||||
T_TsmRoutine, /* in access/tsmapi.h */
|
||||
T_ForeignKeyCacheInfo /* in utils/rel.h */
|
||||
} NodeTag;
|
||||
|
||||
/*
|
||||
|
||||
@ -251,6 +251,8 @@ typedef struct PlannerInfo
|
||||
|
||||
List *placeholder_list; /* list of PlaceHolderInfos */
|
||||
|
||||
List *fkey_list; /* list of ForeignKeyOptInfos */
|
||||
|
||||
List *query_pathkeys; /* desired pathkeys for query_planner() */
|
||||
|
||||
List *group_pathkeys; /* groupClause pathkeys, if any */
|
||||
@ -622,6 +624,36 @@ typedef struct IndexOptInfo
|
||||
void (*amcostestimate) (); /* AM's cost estimator */
|
||||
} IndexOptInfo;
|
||||
|
||||
/*
|
||||
* ForeignKeyOptInfo
|
||||
* Per-foreign-key information for planning/optimization
|
||||
*
|
||||
* The per-FK-column arrays can be fixed-size because we allow at most
|
||||
* INDEX_MAX_KEYS columns in a foreign key constraint. Each array has
|
||||
* nkeys valid entries.
|
||||
*/
|
||||
typedef struct ForeignKeyOptInfo
|
||||
{
|
||||
NodeTag type;
|
||||
|
||||
/* Basic data about the foreign key (fetched from catalogs): */
|
||||
Index con_relid; /* RT index of the referencing table */
|
||||
Index ref_relid; /* RT index of the referenced table */
|
||||
int nkeys; /* number of columns in the foreign key */
|
||||
AttrNumber conkey[INDEX_MAX_KEYS]; /* cols in referencing table */
|
||||
AttrNumber confkey[INDEX_MAX_KEYS]; /* cols in referenced table */
|
||||
Oid conpfeqop[INDEX_MAX_KEYS]; /* PK = FK operator OIDs */
|
||||
|
||||
/* Derived info about whether FK's equality conditions match the query: */
|
||||
int nmatched_ec; /* # of FK cols matched by ECs */
|
||||
int nmatched_rcols; /* # of FK cols matched by non-EC rinfos */
|
||||
int nmatched_ri; /* total # of non-EC rinfos matched to FK */
|
||||
/* Pointer to eclass matching each column's condition, if there is one */
|
||||
struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
|
||||
/* List of non-EC RestrictInfos matching each column's condition */
|
||||
List *rinfos[INDEX_MAX_KEYS];
|
||||
} ForeignKeyOptInfo;
|
||||
|
||||
|
||||
/*
|
||||
* EquivalenceClasses
|
||||
|
||||
@ -167,8 +167,8 @@ extern double get_parameterized_baserel_size(PlannerInfo *root,
|
||||
List *param_clauses);
|
||||
extern double get_parameterized_joinrel_size(PlannerInfo *root,
|
||||
RelOptInfo *rel,
|
||||
double outer_rows,
|
||||
double inner_rows,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
List *restrict_clauses);
|
||||
extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
|
||||
|
||||
@ -140,6 +140,9 @@ extern List *generate_join_implied_equalities_for_ecs(PlannerInfo *root,
|
||||
Relids outer_relids,
|
||||
RelOptInfo *inner_rel);
|
||||
extern bool exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2);
|
||||
extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root,
|
||||
ForeignKeyOptInfo *fkinfo,
|
||||
int colno);
|
||||
extern void add_child_rel_equivalences(PlannerInfo *root,
|
||||
AppendRelInfo *appinfo,
|
||||
RelOptInfo *parent_rel,
|
||||
|
||||
@ -95,6 +95,7 @@ extern RestrictInfo *build_implied_join_equality(Oid opno,
|
||||
Expr *item2,
|
||||
Relids qualscope,
|
||||
Relids nullable_relids);
|
||||
extern void match_foreign_keys_to_quals(PlannerInfo *root);
|
||||
|
||||
/*
|
||||
* prototypes for plan/analyzejoins.c
|
||||
|
||||
@ -90,6 +90,10 @@ typedef struct RelationData
|
||||
/* use "struct" here to avoid needing to include rowsecurity.h: */
|
||||
struct RowSecurityDesc *rd_rsdesc; /* row security policies, or NULL */
|
||||
|
||||
/* data managed by RelationGetFKeyList: */
|
||||
List *rd_fkeylist; /* list of ForeignKeyCacheInfo (see below) */
|
||||
bool rd_fkeyvalid; /* true if list has been computed */
|
||||
|
||||
/* data managed by RelationGetIndexList: */
|
||||
List *rd_indexlist; /* list of OIDs of indexes on relation */
|
||||
Oid rd_oidindex; /* OID of unique index on OID, if any */
|
||||
@ -170,6 +174,34 @@ typedef struct RelationData
|
||||
struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
|
||||
} RelationData;
|
||||
|
||||
|
||||
/*
|
||||
* ForeignKeyCacheInfo
|
||||
* Information the relcache can cache about foreign key constraints
|
||||
*
|
||||
* This is basically just an image of relevant columns from pg_constraint.
|
||||
* We make it a subclass of Node so that copyObject() can be used on a list
|
||||
* of these, but we also ensure it is a "flat" object without substructure,
|
||||
* so that list_free_deep() is sufficient to free such a list.
|
||||
* The per-FK-column arrays can be fixed-size because we allow at most
|
||||
* INDEX_MAX_KEYS columns in a foreign key constraint.
|
||||
*
|
||||
* Currently, we only cache fields of interest to the planner, but the
|
||||
* set of fields could be expanded in future.
|
||||
*/
|
||||
typedef struct ForeignKeyCacheInfo
|
||||
{
|
||||
NodeTag type;
|
||||
Oid conrelid; /* relation constrained by the foreign key */
|
||||
Oid confrelid; /* relation referenced by the foreign key */
|
||||
int nkeys; /* number of columns in the foreign key */
|
||||
/* these arrays each have nkeys valid entries: */
|
||||
AttrNumber conkey[INDEX_MAX_KEYS]; /* cols in referencing table */
|
||||
AttrNumber confkey[INDEX_MAX_KEYS]; /* cols in referenced table */
|
||||
Oid conpfeqop[INDEX_MAX_KEYS]; /* PK = FK operator OIDs */
|
||||
} ForeignKeyCacheInfo;
|
||||
|
||||
|
||||
/*
|
||||
* StdRdOptions
|
||||
* Standard contents of rd_options for heaps and generic indexes.
|
||||
|
||||
@ -37,6 +37,7 @@ extern void RelationClose(Relation relation);
|
||||
/*
|
||||
* Routines to compute/retrieve additional cached information
|
||||
*/
|
||||
extern List *RelationGetFKeyList(Relation relation);
|
||||
extern List *RelationGetIndexList(Relation relation);
|
||||
extern Oid RelationGetOidIndex(Relation relation);
|
||||
extern Oid RelationGetReplicaIndex(Relation relation);
|
||||
|
||||
Reference in New Issue
Block a user