mirror of
https://github.com/postgres/postgres.git
synced 2025-05-31 00:01:57 -04:00
Improve create_unique_path to not be fooled by unrelated clauses that happen
to be syntactically part of a semijoin clause. For example given WHERE EXISTS(SELECT ... WHERE upper.var = lower.var AND some-condition) where some-condition is just a restriction on the lower relation, we can use unique-ification on lower.var after having applied some-condition within the scan on lower.
This commit is contained in:
parent
f4fea44ce1
commit
f01313bc0d
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.149 2009/01/01 17:23:44 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.150 2009/02/27 00:06:27 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -765,12 +765,26 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
|
|||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
||||||
|
|
||||||
/*
|
/*----------
|
||||||
* Look to see whether the semijoin's join quals consist of AND'ed
|
* Look to see whether the semijoin's join quals consist of AND'ed
|
||||||
* equality operators, with (only) RHS variables on only one side of
|
* equality operators, with (only) RHS variables on only one side of
|
||||||
* each one. If so, we can figure out how to enforce uniqueness for
|
* each one. If so, we can figure out how to enforce uniqueness for
|
||||||
* the RHS.
|
* 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
|
* Note that the in_operators list consists of the joinqual operators
|
||||||
* themselves (but commuted if needed to put the RHS value on the right).
|
* themselves (but commuted if needed to put the RHS value on the right).
|
||||||
* These could be cross-type operators, in which case the operator
|
* These could be cross-type operators, in which case the operator
|
||||||
@ -778,6 +792,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
|
|||||||
* We assume here that that operator will be available from the btree
|
* We assume here that that operator will be available from the btree
|
||||||
* or hash opclass when the time comes ... if not, create_unique_plan()
|
* or hash opclass when the time comes ... if not, create_unique_plan()
|
||||||
* will fail.
|
* will fail.
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
in_operators = NIL;
|
in_operators = NIL;
|
||||||
uniq_exprs = NIL;
|
uniq_exprs = NIL;
|
||||||
@ -791,19 +806,52 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
|
|||||||
Node *right_expr;
|
Node *right_expr;
|
||||||
Relids left_varnos;
|
Relids left_varnos;
|
||||||
Relids right_varnos;
|
Relids right_varnos;
|
||||||
|
Relids all_varnos;
|
||||||
|
|
||||||
/* must be binary opclause... */
|
/* Is it a binary opclause? */
|
||||||
if (!IsA(op, OpExpr))
|
if (!IsA(op, OpExpr) ||
|
||||||
goto no_unique_path;
|
list_length(op->args) != 2)
|
||||||
if (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;
|
goto no_unique_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract data from binary opclause */
|
||||||
opno = op->opno;
|
opno = op->opno;
|
||||||
left_expr = linitial(op->args);
|
left_expr = linitial(op->args);
|
||||||
right_expr = lsecond(op->args);
|
right_expr = lsecond(op->args);
|
||||||
|
|
||||||
/* check rel membership of arguments */
|
|
||||||
left_varnos = pull_varnos(left_expr);
|
left_varnos = pull_varnos(left_expr);
|
||||||
right_varnos = pull_varnos(right_expr);
|
right_varnos = pull_varnos(right_expr);
|
||||||
|
all_varnos = bms_union(left_varnos, right_varnos);
|
||||||
|
|
||||||
|
/* 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) &&
|
if (!bms_is_empty(right_varnos) &&
|
||||||
bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
|
bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
|
||||||
!bms_overlap(left_varnos, sjinfo->syn_righthand))
|
!bms_overlap(left_varnos, sjinfo->syn_righthand))
|
||||||
@ -845,6 +893,10 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
|
|||||||
uniq_exprs = lappend(uniq_exprs, copyObject(right_expr));
|
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.
|
* The expressions we'd need to unique-ify mustn't be volatile.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user