Skip to content

Commit f01313b

Browse files
committed
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.
1 parent f4fea44 commit f01313b

File tree

1 file changed

+60
-8
lines changed

1 file changed

+60
-8
lines changed

src/backend/optimizer/util/pathnode.c

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.149 2009/01/01 17:23:44 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.150 2009/02/27 00:06:27 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -765,19 +765,34 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
765765
*/
766766
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
767767

768-
/*
768+
/*----------
769769
* Look to see whether the semijoin's join quals consist of AND'ed
770770
* equality operators, with (only) RHS variables on only one side of
771771
* each one. If so, we can figure out how to enforce uniqueness for
772772
* the RHS.
773773
*
774+
* Note that the input join_quals list is the list of quals that are
775+
* *syntactically* associated with the semijoin, which in practice means
776+
* the synthesized comparison list for an IN or the WHERE of an EXISTS.
777+
* Particularly in the latter case, it might contain clauses that aren't
778+
* *semantically* associated with the join, but refer to just one side or
779+
* the other. We can ignore such clauses here, as they will just drop
780+
* down to be processed within one side or the other. (It is okay to
781+
* consider only the syntactically-associated clauses here because for a
782+
* semijoin, no higher-level quals could refer to the RHS, and so there
783+
* can be no other quals that are semantically associated with this join.
784+
* We do things this way because it is useful to be able to run this test
785+
* before we have extracted the list of quals that are actually
786+
* semantically associated with the particular join.)
787+
*
774788
* Note that the in_operators list consists of the joinqual operators
775789
* themselves (but commuted if needed to put the RHS value on the right).
776790
* These could be cross-type operators, in which case the operator
777791
* actually needed for uniqueness is a related single-type operator.
778792
* We assume here that that operator will be available from the btree
779793
* or hash opclass when the time comes ... if not, create_unique_plan()
780794
* will fail.
795+
*----------
781796
*/
782797
in_operators = NIL;
783798
uniq_exprs = NIL;
@@ -791,19 +806,52 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
791806
Node *right_expr;
792807
Relids left_varnos;
793808
Relids right_varnos;
809+
Relids all_varnos;
794810

795-
/* must be binary opclause... */
796-
if (!IsA(op, OpExpr))
797-
goto no_unique_path;
798-
if (list_length(op->args) != 2)
811+
/* Is it a binary opclause? */
812+
if (!IsA(op, OpExpr) ||
813+
list_length(op->args) != 2)
814+
{
815+
/* No, but does it reference both sides? */
816+
all_varnos = pull_varnos((Node *) op);
817+
if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
818+
bms_is_subset(all_varnos, sjinfo->syn_righthand))
819+
{
820+
/*
821+
* Clause refers to only one rel, so ignore it --- unless it
822+
* contains volatile functions, in which case we'd better
823+
* punt.
824+
*/
825+
if (contain_volatile_functions((Node *) op))
826+
goto no_unique_path;
827+
continue;
828+
}
829+
/* Non-operator clause referencing both sides, must punt */
799830
goto no_unique_path;
831+
}
832+
833+
/* Extract data from binary opclause */
800834
opno = op->opno;
801835
left_expr = linitial(op->args);
802836
right_expr = lsecond(op->args);
803-
804-
/* check rel membership of arguments */
805837
left_varnos = pull_varnos(left_expr);
806838
right_varnos = pull_varnos(right_expr);
839+
all_varnos = bms_union(left_varnos, right_varnos);
840+
841+
/* Does it reference both sides? */
842+
if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
843+
bms_is_subset(all_varnos, sjinfo->syn_righthand))
844+
{
845+
/*
846+
* Clause refers to only one rel, so ignore it --- unless it
847+
* contains volatile functions, in which case we'd better punt.
848+
*/
849+
if (contain_volatile_functions((Node *) op))
850+
goto no_unique_path;
851+
continue;
852+
}
853+
854+
/* check rel membership of arguments */
807855
if (!bms_is_empty(right_varnos) &&
808856
bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
809857
!bms_overlap(left_varnos, sjinfo->syn_righthand))
@@ -845,6 +893,10 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
845893
uniq_exprs = lappend(uniq_exprs, copyObject(right_expr));
846894
}
847895

896+
/* Punt if we didn't find at least one column to unique-ify */
897+
if (uniq_exprs == NIL)
898+
goto no_unique_path;
899+
848900
/*
849901
* The expressions we'd need to unique-ify mustn't be volatile.
850902
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy