7
7
* Portions Copyright (c) 1994, Regents of the University of California
8
8
*
9
9
* IDENTIFICATION
10
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.133 2008/08/14 18:47:59 tgl Exp $
10
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.134 2008/08/17 01:20:00 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
@@ -725,18 +725,31 @@ hash_ok_operator(OpExpr *expr)
725
725
/*
726
726
* convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
727
727
*
728
- * The caller has found an ANY SubLink at the top level of WHERE, but has not
729
- * checked the properties of the SubLink further. Decide whether it is
730
- * appropriate to process this SubLink in join style. If not, return NULL.
731
- * If so, build the qual clause(s) to replace the SubLink, and return them.
732
- * The qual clauses are wrapped in a FlattenedSubLink node to help later
733
- * processing place them properly.
728
+ * The caller has found an ANY SubLink at the top level of one of the query's
729
+ * qual clauses, but has not checked the properties of the SubLink further.
730
+ * Decide whether it is appropriate to process this SubLink in join style.
731
+ * Return TRUE if so, FALSE if the SubLink cannot be converted.
732
+ *
733
+ * The only non-obvious input parameter is available_rels: this is the set
734
+ * of query rels that can safely be referenced in the sublink expression.
735
+ * (We must restrict this to avoid changing the semantics when a sublink
736
+ * is present in an outer join's ON qual.) The conversion must fail if
737
+ * the converted qual would reference any but these parent-query relids.
738
+ *
739
+ * On success, two output parameters are returned:
740
+ * *new_qual is set to the qual tree that should replace the SubLink in
741
+ * the parent query's qual tree. The qual clauses are wrapped in a
742
+ * FlattenedSubLink node to help later processing place them properly.
743
+ * *fromlist is set to a list of pulled-up jointree item(s) that must be
744
+ * added at the proper spot in the parent query's jointree.
734
745
*
735
746
* Side effects of a successful conversion include adding the SubLink's
736
747
* subselect to the query's rangetable.
737
748
*/
738
- Node *
739
- convert_ANY_sublink_to_join (PlannerInfo * root , SubLink * sublink )
749
+ bool
750
+ convert_ANY_sublink_to_join (PlannerInfo * root , SubLink * sublink ,
751
+ Relids available_rels ,
752
+ Node * * new_qual , List * * fromlist )
740
753
{
741
754
Query * parse = root -> parse ;
742
755
Query * subselect = (Query * ) sublink -> subselect ;
@@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
755
768
* higher levels should be okay, though.)
756
769
*/
757
770
if (contain_vars_of_level ((Node * ) subselect , 1 ))
758
- return NULL ;
771
+ return false ;
759
772
760
773
/*
761
774
* The test expression must contain some Vars of the current query,
@@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
764
777
*/
765
778
left_varnos = pull_varnos (sublink -> testexpr );
766
779
if (bms_is_empty (left_varnos ))
767
- return NULL ;
780
+ return false;
781
+
782
+ /*
783
+ * However, it can't refer to anything outside available_rels.
784
+ */
785
+ if (!bms_is_subset (left_varnos , available_rels ))
786
+ return false;
768
787
769
788
/*
770
789
* The combining operators and left-hand expressions mustn't be volatile.
771
790
*/
772
791
if (contain_volatile_functions (sublink -> testexpr ))
773
- return NULL ;
792
+ return false ;
774
793
775
794
/*
776
- * Okay, pull up the sub-select into top range table and jointree .
795
+ * Okay, pull up the sub-select into upper range table.
777
796
*
778
797
* We rely here on the assumption that the outer query has no references
779
798
* to the inner (necessarily true, other than the Vars that we build
@@ -786,16 +805,15 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
786
805
false);
787
806
parse -> rtable = lappend (parse -> rtable , rte );
788
807
rtindex = list_length (parse -> rtable );
789
- rtr = makeNode (RangeTblRef );
790
- rtr -> rtindex = rtindex ;
791
808
792
809
/*
793
- * We assume it's okay to add the pulled-up subquery to the topmost FROM
794
- * list. This should be all right for ANY clauses appearing in WHERE
795
- * or in upper-level plain JOIN/ON clauses. ANYs appearing below any
796
- * outer joins couldn't be placed there, however.
810
+ * Form a RangeTblRef for the pulled-up sub-select. This must be added
811
+ * to the upper jointree, but it is caller's responsibility to figure
812
+ * out where.
797
813
*/
798
- parse -> jointree -> fromlist = lappend (parse -> jointree -> fromlist , rtr );
814
+ rtr = makeNode (RangeTblRef );
815
+ rtr -> rtindex = rtindex ;
816
+ * fromlist = list_make1 (rtr );
799
817
800
818
/*
801
819
* Build a list of Vars representing the subselect outputs.
@@ -805,22 +823,24 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
805
823
rtindex );
806
824
807
825
/*
808
- * Build the result qual expression, replacing Params with these Vars.
826
+ * Build the replacement qual expression, replacing Params with these Vars.
809
827
*/
810
828
quals = (Expr * ) convert_testexpr (root ,
811
829
sublink -> testexpr ,
812
830
subquery_vars );
813
831
814
832
/*
815
- * Now build the FlattenedSubLink node.
833
+ * And finally, build the FlattenedSubLink node.
816
834
*/
817
835
fslink = makeNode (FlattenedSubLink );
818
836
fslink -> jointype = JOIN_SEMI ;
819
837
fslink -> lefthand = left_varnos ;
820
838
fslink -> righthand = bms_make_singleton (rtindex );
821
839
fslink -> quals = quals ;
822
840
823
- return (Node * ) fslink ;
841
+ * new_qual = (Node * ) fslink ;
842
+
843
+ return true;
824
844
}
825
845
826
846
/*
@@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query)
883
903
/*
884
904
* convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
885
905
*
886
- * The caller has found an EXISTS SubLink at the top level of WHERE, or just
887
- * underneath a NOT, but has not checked the properties of the SubLink
888
- * further. Decide whether it is appropriate to process this SubLink in join
889
- * style. If not, return NULL. If so, build the qual clause(s) to replace
890
- * the SubLink, and return them. (In the NOT case, the returned clauses are
891
- * intended to replace the NOT as well.) The qual clauses are wrapped in a
892
- * FlattenedSubLink node to help later processing place them properly.
893
- *
894
- * Side effects of a successful conversion include adding the SubLink's
895
- * subselect to the query's rangetable.
906
+ * The API of this function is identical to convert_ANY_sublink_to_join's,
907
+ * except that we also support the case where the caller has found NOT EXISTS,
908
+ * so we need an additional input parameter "under_not".
896
909
*/
897
- Node *
910
+ bool
898
911
convert_EXISTS_sublink_to_join (PlannerInfo * root , SubLink * sublink ,
899
- bool under_not )
912
+ bool under_not ,
913
+ Relids available_rels ,
914
+ Node * * new_qual , List * * fromlist )
900
915
{
901
916
Query * parse = root -> parse ;
902
917
Query * subselect = (Query * ) sublink -> subselect ;
@@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
924
939
* us with noplace to evaluate the targetlist.
925
940
*/
926
941
if (!simplify_EXISTS_query (subselect ))
927
- return NULL ;
942
+ return false ;
928
943
929
944
/*
930
945
* Separate out the WHERE clause. (We could theoretically also remove
@@ -939,31 +954,31 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
939
954
* query. (Vars of higher levels should be okay, though.)
940
955
*/
941
956
if (contain_vars_of_level ((Node * ) subselect , 1 ))
942
- return NULL ;
957
+ return false ;
943
958
944
959
/*
945
960
* On the other hand, the WHERE clause must contain some Vars of the
946
961
* parent query, else it's not gonna be a join.
947
962
*/
948
963
if (!contain_vars_of_level (whereClause , 1 ))
949
- return NULL ;
964
+ return false ;
950
965
951
966
/*
952
967
* We don't risk optimizing if the WHERE clause is volatile, either.
953
968
*/
954
969
if (contain_volatile_functions (whereClause ))
955
- return NULL ;
970
+ return false ;
956
971
957
972
/*
958
973
* Also disallow SubLinks within the WHERE clause. (XXX this could
959
974
* probably be supported, but it would complicate the transformation
960
975
* below, and it doesn't seem worth worrying about in a first pass.)
961
976
*/
962
977
if (contain_subplans (whereClause ))
963
- return NULL ;
978
+ return false ;
964
979
965
980
/*
966
- * Okay, pull up the sub-select into top range table and jointree .
981
+ * Prepare to pull up the sub-select into top range table.
967
982
*
968
983
* We rely here on the assumption that the outer query has no references
969
984
* to the inner (necessarily true). Therefore this is a lot easier than
@@ -973,7 +988,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
973
988
* to do. The machinations of simplify_EXISTS_query ensured that there
974
989
* is nothing interesting in the subquery except an rtable and jointree,
975
990
* and even the jointree FromExpr no longer has quals. So we can just
976
- * append the rtable to our own and append the fromlist to our own.
991
+ * append the rtable to our own and attach the fromlist to our own.
977
992
* But first, adjust all level-zero varnos in the subquery to account
978
993
* for the rtable merger.
979
994
*/
@@ -1007,32 +1022,39 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
1007
1022
bms_free (clause_varnos );
1008
1023
Assert (!bms_is_empty (left_varnos ));
1009
1024
1010
- /* Also identify all the rels syntactically within the subselect */
1011
- subselect_varnos = get_relids_in_jointree ((Node * ) subselect -> jointree );
1025
+ /*
1026
+ * Now that we've got the set of upper-level varnos, we can make the
1027
+ * last check: only available_rels can be referenced.
1028
+ */
1029
+ if (!bms_is_subset (left_varnos , available_rels ))
1030
+ return false;
1031
+
1032
+ /* Identify all the rels syntactically within the subselect */
1033
+ subselect_varnos = get_relids_in_jointree ((Node * ) subselect -> jointree ,
1034
+ true);
1012
1035
Assert (bms_is_subset (right_varnos , subselect_varnos ));
1013
1036
1014
1037
/* Now we can attach the modified subquery rtable to the parent */
1015
1038
parse -> rtable = list_concat (parse -> rtable , subselect -> rtable );
1016
1039
1017
1040
/*
1018
- * We assume it's okay to add the pulled-up subquery to the topmost FROM
1019
- * list. This should be all right for EXISTS clauses appearing in WHERE
1020
- * or in upper-level plain JOIN/ON clauses. EXISTS appearing below any
1021
- * outer joins couldn't be placed there, however.
1041
+ * Pass back the subquery fromlist to be attached to upper jointree
1042
+ * in a suitable place.
1022
1043
*/
1023
- parse -> jointree -> fromlist = list_concat (parse -> jointree -> fromlist ,
1024
- subselect -> jointree -> fromlist );
1044
+ * fromlist = subselect -> jointree -> fromlist ;
1025
1045
1026
1046
/*
1027
- * Now build the FlattenedSubLink node.
1047
+ * And finally, build the FlattenedSubLink node.
1028
1048
*/
1029
1049
fslink = makeNode (FlattenedSubLink );
1030
1050
fslink -> jointype = under_not ? JOIN_ANTI : JOIN_SEMI ;
1031
1051
fslink -> lefthand = left_varnos ;
1032
1052
fslink -> righthand = subselect_varnos ;
1033
1053
fslink -> quals = (Expr * ) whereClause ;
1034
1054
1035
- return (Node * ) fslink ;
1055
+ * new_qual = (Node * ) fslink ;
1056
+
1057
+ return true;
1036
1058
}
1037
1059
1038
1060
/*
0 commit comments