diff options
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 192 |
1 files changed, 20 insertions, 172 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index d129f8d65ea..319e8b2c379 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -22,8 +22,9 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" -#include "optimizer/tlist.h" +#include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" #include "utils/selfuncs.h" @@ -38,8 +39,6 @@ typedef enum } PathCostComparison; static List *translate_sub_tlist(List *tlist, int relid); -static bool query_is_distinct_for(Query *query, List *colnos, List *opids); -static Oid distinct_col_search(int colno, List *colnos, List *opids); /***************************************************************************** @@ -1312,25 +1311,29 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, if (rel->rtekind == RTE_SUBQUERY) { RangeTblEntry *rte = planner_rt_fetch(rel->relid, root); - List *sub_tlist_colnos; - sub_tlist_colnos = translate_sub_tlist(uniq_exprs, rel->relid); - - if (sub_tlist_colnos && - query_is_distinct_for(rte->subquery, - sub_tlist_colnos, in_operators)) + if (query_supports_distinctness(rte->subquery)) { - pathnode->umethod = UNIQUE_PATH_NOOP; - pathnode->path.rows = rel->rows; - pathnode->path.startup_cost = subpath->startup_cost; - pathnode->path.total_cost = subpath->total_cost; - pathnode->path.pathkeys = subpath->pathkeys; + List *sub_tlist_colnos; + + sub_tlist_colnos = translate_sub_tlist(uniq_exprs, rel->relid); + + if (sub_tlist_colnos && + query_is_distinct_for(rte->subquery, + sub_tlist_colnos, in_operators)) + { + pathnode->umethod = UNIQUE_PATH_NOOP; + pathnode->path.rows = rel->rows; + pathnode->path.startup_cost = subpath->startup_cost; + pathnode->path.total_cost = subpath->total_cost; + pathnode->path.pathkeys = subpath->pathkeys; - rel->cheapest_unique_path = (Path *) pathnode; + rel->cheapest_unique_path = (Path *) pathnode; - MemoryContextSwitchTo(oldcontext); + MemoryContextSwitchTo(oldcontext); - return pathnode; + return pathnode; + } } } @@ -1451,161 +1454,6 @@ translate_sub_tlist(List *tlist, int relid) } /* - * query_is_distinct_for - does query never return duplicates of the - * specified columns? - * - * colnos is an integer list of output column numbers (resno's). We are - * interested in whether rows consisting of just these columns are certain - * to be distinct. "Distinctness" is defined according to whether the - * corresponding upper-level equality operators listed in opids would think - * the values are distinct. (Note: the opids entries could be cross-type - * operators, and thus not exactly the equality operators that the subquery - * would use itself. We use equality_ops_are_compatible() to check - * compatibility. That looks at btree or hash opfamily membership, and so - * should give trustworthy answers for all operators that we might need - * to deal with here.) - */ -static bool -query_is_distinct_for(Query *query, List *colnos, List *opids) -{ - ListCell *l; - Oid opid; - - Assert(list_length(colnos) == list_length(opids)); - - /* - * A set-returning function in the query's targetlist can result in - * returning duplicate rows, if the SRF is evaluated after the - * de-duplication step; so we play it safe and say "no" if there are any - * SRFs. (We could be certain that it's okay if SRFs appear only in the - * specified columns, since those must be evaluated before de-duplication; - * but it doesn't presently seem worth the complication to check that.) - */ - if (expression_returns_set((Node *) query->targetList)) - return false; - - /* - * DISTINCT (including DISTINCT ON) guarantees uniqueness if all the - * columns in the DISTINCT clause appear in colnos and operator semantics - * match. - */ - if (query->distinctClause) - { - foreach(l, query->distinctClause) - { - SortGroupClause *sgc = (SortGroupClause *) lfirst(l); - TargetEntry *tle = get_sortgroupclause_tle(sgc, - query->targetList); - - opid = distinct_col_search(tle->resno, colnos, opids); - if (!OidIsValid(opid) || - !equality_ops_are_compatible(opid, sgc->eqop)) - break; /* exit early if no match */ - } - if (l == NULL) /* had matches for all? */ - return true; - } - - /* - * Similarly, GROUP BY guarantees uniqueness if all the grouped columns - * appear in colnos and operator semantics match. - */ - if (query->groupClause) - { - foreach(l, query->groupClause) - { - SortGroupClause *sgc = (SortGroupClause *) lfirst(l); - TargetEntry *tle = get_sortgroupclause_tle(sgc, - query->targetList); - - opid = distinct_col_search(tle->resno, colnos, opids); - if (!OidIsValid(opid) || - !equality_ops_are_compatible(opid, sgc->eqop)) - break; /* exit early if no match */ - } - if (l == NULL) /* had matches for all? */ - return true; - } - else - { - /* - * If we have no GROUP BY, but do have aggregates or HAVING, then the - * result is at most one row so it's surely unique, for any operators. - */ - if (query->hasAggs || query->havingQual) - return true; - } - - /* - * UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row, - * except with ALL. - */ - if (query->setOperations) - { - SetOperationStmt *topop = (SetOperationStmt *) query->setOperations; - - Assert(IsA(topop, SetOperationStmt)); - Assert(topop->op != SETOP_NONE); - - if (!topop->all) - { - ListCell *lg; - - /* We're good if all the nonjunk output columns are in colnos */ - lg = list_head(topop->groupClauses); - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - SortGroupClause *sgc; - - if (tle->resjunk) - continue; /* ignore resjunk columns */ - - /* non-resjunk columns should have grouping clauses */ - Assert(lg != NULL); - sgc = (SortGroupClause *) lfirst(lg); - lg = lnext(lg); - - opid = distinct_col_search(tle->resno, colnos, opids); - if (!OidIsValid(opid) || - !equality_ops_are_compatible(opid, sgc->eqop)) - break; /* exit early if no match */ - } - if (l == NULL) /* had matches for all? */ - return true; - } - } - - /* - * XXX Are there any other cases in which we can easily see the result - * must be distinct? - */ - - return false; -} - -/* - * distinct_col_search - subroutine for query_is_distinct_for - * - * If colno is in colnos, return the corresponding element of opids, - * else return InvalidOid. (We expect colnos does not contain duplicates, - * so the result is well-defined.) - */ -static Oid -distinct_col_search(int colno, List *colnos, List *opids) -{ - ListCell *lc1, - *lc2; - - forboth(lc1, colnos, lc2, opids) - { - if (colno == lfirst_int(lc1)) - return lfirst_oid(lc2); - } - return InvalidOid; -} - -/* * create_subqueryscan_path * Creates a path corresponding to a sequential scan of a subquery, * returning the pathnode. |