mirror of
https://github.com/postgres/postgres.git
synced 2025-08-24 09:27:52 +03:00
If a potential equivalence clause references a variable from the nullable side of an outer join, the planner needs to take care that derived clauses are not pushed to below the outer join; else they may use the wrong value for the variable. (The problem arises only with non-strict clauses, since if an upper clause can be proven strict then the outer join will get simplified to a plain join.) The planner attempted to prevent this type of error by checking that potential equivalence clauses aren't outerjoin-delayed as a whole, but actually we have to check each side separately, since the two sides of the clause will get moved around separately if it's treated as an equivalence. Bugs of this type can be demonstrated as far back as 7.4, even though releases before 8.3 had only a very ad-hoc notion of equivalence clauses. In addition, we neglected to account for the possibility that such clauses might have nonempty nullable_relids even when not outerjoin-delayed; so the equivalence-class machinery lacked logic to compute correct nullable_relids values for clauses it constructs. This oversight was harmless before 9.2 because we were only using RestrictInfo.nullable_relids for OR clauses; but as of 9.2 it could result in pushing constructed equivalence clauses to incorrect places. (This accounts for bug #7604 from Bill MacArthur.) Fix the first problem by adding a new test check_equivalence_delay() in distribute_qual_to_rels, and fix the second one by adding code in equivclass.c and called functions to set correct nullable_relids for generated clauses. Although I believe the second part of this is not currently necessary before 9.2, I chose to back-patch it anyway, partly to keep the logic similar across branches and partly because it seems possible we might find other reasons why we need valid values of nullable_relids in the older branches. Add regression tests illustrating these problems. In 9.0 and up, also add test cases checking that we can push constants through outer joins, since we've broken that optimization before and I nearly broke it again with an overly simplistic patch for this problem.
136 lines
5.0 KiB
C
136 lines
5.0 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* planmain.h
|
|
* prototypes for various files in optimizer/plan
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/optimizer/planmain.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef PLANMAIN_H
|
|
#define PLANMAIN_H
|
|
|
|
#include "nodes/plannodes.h"
|
|
#include "nodes/relation.h"
|
|
|
|
/* GUC parameters */
|
|
#define DEFAULT_CURSOR_TUPLE_FRACTION 0.1
|
|
extern double cursor_tuple_fraction;
|
|
|
|
/*
|
|
* prototypes for plan/planmain.c
|
|
*/
|
|
extern void query_planner(PlannerInfo *root, List *tlist,
|
|
double tuple_fraction, double limit_tuples,
|
|
Path **cheapest_path, Path **sorted_path,
|
|
double *num_groups);
|
|
|
|
/*
|
|
* prototypes for plan/planagg.c
|
|
*/
|
|
extern void preprocess_minmax_aggregates(PlannerInfo *root, List *tlist);
|
|
extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
|
|
const AggClauseCosts *aggcosts, Path *best_path);
|
|
|
|
/*
|
|
* prototypes for plan/createplan.c
|
|
*/
|
|
extern Plan *create_plan(PlannerInfo *root, Path *best_path);
|
|
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
|
|
Index scanrelid, Plan *subplan);
|
|
extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
|
|
Index scanrelid, List *fdw_exprs, List *fdw_private);
|
|
extern Append *make_append(List *appendplans, List *tlist);
|
|
extern RecursiveUnion *make_recursive_union(List *tlist,
|
|
Plan *lefttree, Plan *righttree, int wtParam,
|
|
List *distinctList, long numGroups);
|
|
extern Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
|
|
List *pathkeys, double limit_tuples);
|
|
extern Sort *make_sort_from_sortclauses(PlannerInfo *root, List *sortcls,
|
|
Plan *lefttree);
|
|
extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls,
|
|
AttrNumber *grpColIdx, Plan *lefttree);
|
|
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
|
|
AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
|
|
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
|
|
long numGroups,
|
|
Plan *lefttree);
|
|
extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist,
|
|
List *windowFuncs, Index winref,
|
|
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
|
|
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
|
|
int frameOptions, Node *startOffset, Node *endOffset,
|
|
Plan *lefttree);
|
|
extern Group *make_group(PlannerInfo *root, List *tlist, List *qual,
|
|
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
|
|
double numGroups,
|
|
Plan *lefttree);
|
|
extern Plan *materialize_finished_plan(Plan *subplan);
|
|
extern Unique *make_unique(Plan *lefttree, List *distinctList);
|
|
extern LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
|
|
extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
|
|
int64 offset_est, int64 count_est);
|
|
extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
|
|
List *distinctList, AttrNumber flagColIdx, int firstFlag,
|
|
long numGroups, double outputRows);
|
|
extern Result *make_result(PlannerInfo *root, List *tlist,
|
|
Node *resconstantqual, Plan *subplan);
|
|
extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
|
|
List *resultRelations, List *subplans, List *returningLists,
|
|
List *rowMarks, int epqParam);
|
|
extern bool is_projection_capable_plan(Plan *plan);
|
|
|
|
/*
|
|
* prototypes for plan/initsplan.c
|
|
*/
|
|
extern int from_collapse_limit;
|
|
extern int join_collapse_limit;
|
|
|
|
extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
|
|
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
|
|
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
|
|
Relids where_needed, bool create_new_ph);
|
|
extern void find_lateral_references(PlannerInfo *root);
|
|
extern void create_lateral_join_info(PlannerInfo *root);
|
|
extern List *deconstruct_jointree(PlannerInfo *root);
|
|
extern void distribute_restrictinfo_to_rels(PlannerInfo *root,
|
|
RestrictInfo *restrictinfo);
|
|
extern void process_implied_equality(PlannerInfo *root,
|
|
Oid opno,
|
|
Oid collation,
|
|
Expr *item1,
|
|
Expr *item2,
|
|
Relids qualscope,
|
|
Relids nullable_relids,
|
|
bool below_outer_join,
|
|
bool both_const);
|
|
extern RestrictInfo *build_implied_join_equality(Oid opno,
|
|
Oid collation,
|
|
Expr *item1,
|
|
Expr *item2,
|
|
Relids qualscope,
|
|
Relids nullable_relids);
|
|
|
|
/*
|
|
* prototypes for plan/analyzejoins.c
|
|
*/
|
|
extern List *remove_useless_joins(PlannerInfo *root, List *joinlist);
|
|
|
|
/*
|
|
* prototypes for plan/setrefs.c
|
|
*/
|
|
extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
|
|
extern void fix_opfuncids(Node *node);
|
|
extern void set_opfuncid(OpExpr *opexpr);
|
|
extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
|
|
extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid);
|
|
extern void extract_query_dependencies(Node *query,
|
|
List **relationOids,
|
|
List **invalItems);
|
|
|
|
#endif /* PLANMAIN_H */
|