summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/pathnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r--src/backend/optimizer/util/pathnode.c213
1 files changed, 35 insertions, 178 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 1395a211398..faca30b322d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1088,12 +1088,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
Path sort_path; /* dummy for result of cost_sort */
Path agg_path; /* dummy for result of cost_agg */
MemoryContext oldcontext;
- List *in_operators;
- List *uniq_exprs;
- bool all_btree;
- bool all_hash;
int numCols;
- ListCell *lc;
/* Caller made a mistake if subpath isn't cheapest_total ... */
Assert(subpath == rel->cheapest_total_path);
@@ -1106,8 +1101,8 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
if (rel->cheapest_unique_path)
return (UniquePath *) rel->cheapest_unique_path;
- /* If we previously failed, return NULL quickly */
- if (sjinfo->join_quals == NIL)
+ /* If it's not possible to unique-ify, return NULL */
+ if (!(sjinfo->semi_can_btree || sjinfo->semi_can_hash))
return NULL;
/*
@@ -1116,150 +1111,6 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
- /*----------
- * Look to see whether the semijoin's join quals consist of AND'ed
- * equality operators, with (only) RHS variables on only one side of
- * each one. If so, we can figure out how to enforce uniqueness for
- * the RHS.
- *
- * Note that the input join_quals list is the list of quals that are
- * *syntactically* associated with the semijoin, which in practice means
- * the synthesized comparison list for an IN or the WHERE of an EXISTS.
- * Particularly in the latter case, it might contain clauses that aren't
- * *semantically* associated with the join, but refer to just one side or
- * the other. We can ignore such clauses here, as they will just drop
- * down to be processed within one side or the other. (It is okay to
- * consider only the syntactically-associated clauses here because for a
- * semijoin, no higher-level quals could refer to the RHS, and so there
- * can be no other quals that are semantically associated with this join.
- * We do things this way because it is useful to be able to run this test
- * before we have extracted the list of quals that are actually
- * semantically associated with the particular join.)
- *
- * Note that the in_operators list consists of the joinqual operators
- * themselves (but commuted if needed to put the RHS value on the right).
- * These could be cross-type operators, in which case the operator
- * actually needed for uniqueness is a related single-type operator.
- * We assume here that that operator will be available from the btree
- * or hash opclass when the time comes ... if not, create_unique_plan()
- * will fail.
- *----------
- */
- in_operators = NIL;
- uniq_exprs = NIL;
- all_btree = true;
- all_hash = enable_hashagg; /* don't consider hash if not enabled */
- foreach(lc, sjinfo->join_quals)
- {
- OpExpr *op = (OpExpr *) lfirst(lc);
- Oid opno;
- Node *left_expr;
- Node *right_expr;
- Relids left_varnos;
- Relids right_varnos;
- Relids all_varnos;
- Oid opinputtype;
-
- /* Is it a binary opclause? */
- if (!IsA(op, OpExpr) ||
- list_length(op->args) != 2)
- {
- /* No, but does it reference both sides? */
- all_varnos = pull_varnos((Node *) op);
- if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
- bms_is_subset(all_varnos, sjinfo->syn_righthand))
- {
- /*
- * Clause refers to only one rel, so ignore it --- unless it
- * contains volatile functions, in which case we'd better
- * punt.
- */
- if (contain_volatile_functions((Node *) op))
- goto no_unique_path;
- continue;
- }
- /* Non-operator clause referencing both sides, must punt */
- goto no_unique_path;
- }
-
- /* Extract data from binary opclause */
- opno = op->opno;
- left_expr = linitial(op->args);
- right_expr = lsecond(op->args);
- left_varnos = pull_varnos(left_expr);
- right_varnos = pull_varnos(right_expr);
- all_varnos = bms_union(left_varnos, right_varnos);
- opinputtype = exprType(left_expr);
-
- /* Does it reference both sides? */
- if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
- bms_is_subset(all_varnos, sjinfo->syn_righthand))
- {
- /*
- * Clause refers to only one rel, so ignore it --- unless it
- * contains volatile functions, in which case we'd better punt.
- */
- if (contain_volatile_functions((Node *) op))
- goto no_unique_path;
- continue;
- }
-
- /* check rel membership of arguments */
- if (!bms_is_empty(right_varnos) &&
- bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
- !bms_overlap(left_varnos, sjinfo->syn_righthand))
- {
- /* typical case, right_expr is RHS variable */
- }
- else if (!bms_is_empty(left_varnos) &&
- bms_is_subset(left_varnos, sjinfo->syn_righthand) &&
- !bms_overlap(right_varnos, sjinfo->syn_righthand))
- {
- /* flipped case, left_expr is RHS variable */
- opno = get_commutator(opno);
- if (!OidIsValid(opno))
- goto no_unique_path;
- right_expr = left_expr;
- }
- else
- goto no_unique_path;
-
- /* all operators must be btree equality or hash equality */
- if (all_btree)
- {
- /* oprcanmerge is considered a hint... */
- if (!op_mergejoinable(opno, opinputtype) ||
- get_mergejoin_opfamilies(opno) == NIL)
- all_btree = false;
- }
- if (all_hash)
- {
- /* ... but oprcanhash had better be correct */
- if (!op_hashjoinable(opno, opinputtype))
- all_hash = false;
- }
- if (!(all_btree || all_hash))
- goto no_unique_path;
-
- /* so far so good, keep building lists */
- in_operators = lappend_oid(in_operators, opno);
- uniq_exprs = lappend(uniq_exprs, copyObject(right_expr));
- }
-
- /* Punt if we didn't find at least one column to unique-ify */
- if (uniq_exprs == NIL)
- goto no_unique_path;
-
- /*
- * The expressions we'd need to unique-ify mustn't be volatile.
- */
- if (contain_volatile_functions((Node *) uniq_exprs))
- goto no_unique_path;
-
- /*
- * If we get here, we can unique-ify using at least one of sorting and
- * hashing. Start building the result Path object.
- */
pathnode = makeNode(UniquePath);
pathnode->path.pathtype = T_Unique;
@@ -1273,18 +1124,19 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
pathnode->path.pathkeys = NIL;
pathnode->subpath = subpath;
- pathnode->in_operators = in_operators;
- pathnode->uniq_exprs = uniq_exprs;
+ pathnode->in_operators = sjinfo->semi_operators;
+ pathnode->uniq_exprs = sjinfo->semi_rhs_exprs;
/*
* If the input is a relation and it has a unique index that proves the
- * uniq_exprs are unique, then we don't need to do anything. Note that
- * relation_has_unique_index_for automatically considers restriction
+ * semi_rhs_exprs are unique, then we don't need to do anything. Note
+ * that relation_has_unique_index_for automatically considers restriction
* clauses for the rel, as well.
*/
- if (rel->rtekind == RTE_RELATION && all_btree &&
+ if (rel->rtekind == RTE_RELATION && sjinfo->semi_can_btree &&
relation_has_unique_index_for(root, rel, NIL,
- uniq_exprs, in_operators))
+ sjinfo->semi_rhs_exprs,
+ sjinfo->semi_operators))
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
@@ -1304,7 +1156,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
* don't need to do anything. The test for uniqueness has to consider
* exactly which columns we are extracting; for example "SELECT DISTINCT
* x,y" doesn't guarantee that x alone is distinct. So we cannot check for
- * this optimization unless uniq_exprs consists only of simple Vars
+ * this optimization unless semi_rhs_exprs consists only of simple Vars
* referencing subquery outputs. (Possibly we could do something with
* expressions in the subquery outputs, too, but for now keep it simple.)
*/
@@ -1316,11 +1168,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
{
List *sub_tlist_colnos;
- sub_tlist_colnos = translate_sub_tlist(uniq_exprs, rel->relid);
+ sub_tlist_colnos = translate_sub_tlist(sjinfo->semi_rhs_exprs,
+ rel->relid);
if (sub_tlist_colnos &&
query_is_distinct_for(rte->subquery,
- sub_tlist_colnos, in_operators))
+ sub_tlist_colnos,
+ sjinfo->semi_operators))
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
@@ -1338,10 +1192,12 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
}
/* Estimate number of output rows */
- pathnode->path.rows = estimate_num_groups(root, uniq_exprs, rel->rows);
- numCols = list_length(uniq_exprs);
+ pathnode->path.rows = estimate_num_groups(root,
+ sjinfo->semi_rhs_exprs,
+ rel->rows);
+ numCols = list_length(sjinfo->semi_rhs_exprs);
- if (all_btree)
+ if (sjinfo->semi_can_btree)
{
/*
* Estimate cost for sort+unique implementation
@@ -1363,7 +1219,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
sort_path.total_cost += cpu_operator_cost * rel->rows * numCols;
}
- if (all_hash)
+ if (sjinfo->semi_can_hash)
{
/*
* Estimate the overhead per hashtable entry at 64 bytes (same as in
@@ -1372,7 +1228,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
int hashentrysize = rel->width + 64;
if (hashentrysize * pathnode->path.rows > work_mem * 1024L)
- all_hash = false; /* don't try to hash */
+ {
+ /*
+ * We should not try to hash. Hack the SpecialJoinInfo to
+ * remember this, in case we come through here again.
+ */
+ sjinfo->semi_can_hash = false;
+ }
else
cost_agg(&agg_path, root,
AGG_HASHED, NULL,
@@ -1382,19 +1244,23 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
rel->rows);
}
- if (all_btree && all_hash)
+ if (sjinfo->semi_can_btree && sjinfo->semi_can_hash)
{
if (agg_path.total_cost < sort_path.total_cost)
pathnode->umethod = UNIQUE_PATH_HASH;
else
pathnode->umethod = UNIQUE_PATH_SORT;
}
- else if (all_btree)
+ else if (sjinfo->semi_can_btree)
pathnode->umethod = UNIQUE_PATH_SORT;
- else if (all_hash)
+ else if (sjinfo->semi_can_hash)
pathnode->umethod = UNIQUE_PATH_HASH;
else
- goto no_unique_path;
+ {
+ /* we can get here only if we abandoned hashing above */
+ MemoryContextSwitchTo(oldcontext);
+ return NULL;
+ }
if (pathnode->umethod == UNIQUE_PATH_HASH)
{
@@ -1412,15 +1278,6 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
MemoryContextSwitchTo(oldcontext);
return pathnode;
-
-no_unique_path: /* failure exit */
-
- /* Mark the SpecialJoinInfo as not unique-able */
- sjinfo->join_quals = NIL;
-
- MemoryContextSwitchTo(oldcontext);
-
- return NULL;
}
/*