11/*-------------------------------------------------------------------------
22 *
33 * tidpath.c
4- * Routines to determine which tids are usable for scanning a
5- * given relation, and create TidPaths accordingly.
4+ * Routines to determine which TID conditions are usable for scanning
5+ * a given relation, and create TidPaths accordingly.
6+ *
7+ * What we are looking for here is WHERE conditions of the form
8+ * "CTID = pseudoconstant", which can be implemented by just fetching
9+ * the tuple directly via heap_fetch(). We can also handle OR conditions
10+ * if each OR arm contains such a condition; in particular this allows
11+ *WHERE ctid IN (tid1, tid2, ...)
12+ *
13+ * There is currently no special support for joins involving CTID; in
14+ * particular nothing corresponding to best_inner_indexscan(). Since it's
15+ * not very useful to store TIDs of one table in another table, there
16+ * doesn't seem to be enough use-case to justify adding a lot of code
17+ * for that.
18+ *
619 *
720 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
821 * Portions Copyright (c) 1994, Regents of the University of California
922 *
1023 *
1124 * IDENTIFICATION
12- * $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.23 2005/06/05 22:32:55 tgl Exp $
25+ * $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.24 2005/08/23 20:49:47 tgl Exp $
1326 *
1427 *-------------------------------------------------------------------------
1528 */
1629#include "postgres.h"
1730
18- #include <math.h>
19-
31+ #include "access/htup.h"
2032#include "catalog/pg_operator.h"
33+ #include "catalog/pg_type.h"
2134#include "optimizer/clauses.h"
22- #include "optimizer/cost.h"
2335#include "optimizer/pathnode.h"
2436#include "optimizer/paths.h"
25- #include "parser/parse_coerce.h"
26- #include "utils/lsyscache.h"
27-
37+ #include "parser/parse_expr.h"
2838
29- static List * TidqualFromRestrictinfo (Relids relids ,List * restrictinfo );
30- static bool isEvaluable (int varno ,Node * node );
31- static Node * TidequalClause (int varno ,OpExpr * node );
32- static List * TidqualFromExpr (int varno ,Expr * expr );
3339
40+ static Node * IsTidEqualClause (int varno ,OpExpr * node );
41+ static List * TidQualFromExpr (int varno ,Node * expr );
42+ static List * TidQualFromRestrictinfo (int varno ,List * restrictinfo );
3443
35- static bool
36- isEvaluable (int varno ,Node * node )
37- {
38- ListCell * l ;
39- FuncExpr * expr ;
40-
41- if (IsA (node ,Const ))
42- return true;
43- if (IsA (node ,Param ))
44- return true;
45- if (IsA (node ,Var ))
46- {
47- Var * var = (Var * )node ;
48-
49- if (var -> varno == varno )
50- return false;
51- return true;
52- }
53- if (!is_funcclause (node ))
54- return false;
55- expr = (FuncExpr * )node ;
56- foreach (l ,expr -> args )
57- {
58- if (!isEvaluable (varno ,lfirst (l )))
59- return false;
60- }
61-
62- return true;
63- }
6444
6545/*
66- *The 2nd parameter should be an opclause
67- *Extract the right node if the opclause is CTID= ....
68- * orthe left node if the opclause is ....=CTID
46+ * Check to see if an opclause is of the form
47+ *CTID = pseudoconstant
48+ * or
49+ *pseudoconstant = CTID
50+ *
51+ * If it is, return the pseudoconstant subnode; if not, return NULL.
52+ *
53+ * We check that the CTID Var belongs to relation "varno". That is probably
54+ * redundant considering this is only applied to restriction clauses, but
55+ * let's be safe.
6956 */
7057static Node *
71- TidequalClause (int varno ,OpExpr * node )
58+ IsTidEqualClause (int varno ,OpExpr * node )
7259{
73- Node * rnode = NULL ,
74- * arg1 ,
60+ Node * arg1 ,
7561* arg2 ,
76- * arg ;
62+ * other ;
7763Var * var ;
78- Const * aconst ;
79- Param * param ;
80- FuncExpr * expr ;
8164
65+ /* Operator must be tideq */
8266if (node -> opno != TIDEqualOperator )
83- return rnode ;
67+ return NULL ;
8468if (list_length (node -> args )!= 2 )
85- return rnode ;
69+ return NULL ;
8670arg1 = linitial (node -> args );
8771arg2 = lsecond (node -> args );
8872
89- arg = NULL ;
90- if (IsA (arg1 ,Var ))
73+ /* Look for CTID as either argument */
74+ other = NULL ;
75+ if (arg1 && IsA (arg1 ,Var ))
9176{
9277var = (Var * )arg1 ;
93- if (var -> varno == varno &&
94- var -> varattno == SelfItemPointerAttributeNumber &&
95- var -> vartype == TIDOID )
96- arg = arg2 ;
97- else if (var -> varnoold == varno &&
98- var -> varoattno == SelfItemPointerAttributeNumber &&
99- var -> vartype == TIDOID )
100- arg = arg2 ;
78+ if (var -> varattno == SelfItemPointerAttributeNumber &&
79+ var -> vartype == TIDOID &&
80+ var -> varno == varno &&
81+ var -> varlevelsup == 0 )
82+ other = arg2 ;
10183}
102- if ((! arg ) && IsA (arg2 ,Var ))
84+ if (! other && arg2 && IsA (arg2 ,Var ))
10385{
10486var = (Var * )arg2 ;
105- if (var -> varno == varno &&
106- var -> varattno == SelfItemPointerAttributeNumber &&
107- var -> vartype == TIDOID )
108- arg = arg1 ;
87+ if (var -> varattno == SelfItemPointerAttributeNumber &&
88+ var -> vartype == TIDOID &&
89+ var -> varno == varno &&
90+ var -> varlevelsup == 0 )
91+ other = arg1 ;
10992}
110- if (!arg )
111- return rnode ;
112- switch (nodeTag (arg ))
113- {
114- case T_Const :
115- aconst = (Const * )arg ;
116- if (aconst -> consttype != TIDOID )
117- return rnode ;
118- if (aconst -> constbyval )
119- return rnode ;
120- rnode = arg ;
121- break ;
122- case T_Param :
123- param = (Param * )arg ;
124- if (param -> paramtype != TIDOID )
125- return rnode ;
126- rnode = arg ;
127- break ;
128- case T_Var :
129- var = (Var * )arg ;
130- if (var -> varno == varno ||
131- var -> vartype != TIDOID )
132- return rnode ;
133- rnode = arg ;
134- break ;
135- case T_FuncExpr :
136- expr = (FuncExpr * )arg ;
137- if (expr -> funcresulttype != TIDOID )
138- return rnode ;
139- if (isEvaluable (varno , (Node * )expr ))
140- rnode = arg ;
141- break ;
142- default :
143- break ;
144- }
145- return rnode ;
93+ if (!other )
94+ return NULL ;
95+ if (exprType (other )!= TIDOID )
96+ return NULL ;/* probably can't happen */
97+
98+ /* The other argument must be a pseudoconstant */
99+ if (!is_pseudo_constant_clause (other ))
100+ return NULL ;
101+
102+ return other ;/* success */
146103}
147104
148105/*
149- *Extract the list of CTID values from a specified expr node.
150- *When the expr node is an or_clause,we try to extract CTID
151- *values from all member nodes. However we would discard them
152- *all if we couldn't extract CTID values from a member node.
153- *When the expr node is an and_clause,we return the list of
154- *CTID values if we could extract the CTID values from a member
155- *node.
106+ *Extract a set of CTID conditions from the given qual expression
107+ *
108+ *If the expression is an AND clause, we can use a CTID condition
109+ *from any sub-clause. If it is an OR clause, we must be able to
110+ *extract a CTID condition from every sub-clause, or we can't use it.
111+ *
112+ *In theory, in the AND case we could get CTID conditions from different
113+ *sub-clauses, in which case we could try to pick the most efficient one.
114+ *In practice, such usage seems very unlikely, so we don't bother; we
115+ *just exit as soon as we find the first candidate.
116+ *
117+ *Returns a List of pseudoconstant TID expressions, or NIL if no match.
118+ *(Has to be a list for the OR case.)
156119 */
157120static List *
158- TidqualFromExpr (int varno ,Expr * expr )
121+ TidQualFromExpr (int varno ,Node * expr )
159122{
160123List * rlst = NIL ,
161124* frtn ;
162125ListCell * l ;
163- Node * node = (Node * )expr ,
164- * rnode ;
126+ Node * rnode ;
165127
166- if (is_opclause (node ))
128+ if (is_opclause (expr ))
167129{
168- rnode = TidequalClause (varno , (OpExpr * )expr );
130+ /* base case: check for tideq opclause */
131+ rnode = IsTidEqualClause (varno , (OpExpr * )expr );
169132if (rnode )
170- rlst = lcons (rnode , rlst );
133+ rlst = list_make1 (rnode );
171134}
172- else if (and_clause (node ))
135+ else if (and_clause (expr ))
173136{
174137foreach (l , ((BoolExpr * )expr )-> args )
175138{
176- node = (Node * )lfirst (l );
177- rlst = TidqualFromExpr (varno , (Expr * )node );
139+ rlst = TidQualFromExpr (varno , (Node * )lfirst (l ));
178140if (rlst )
179141break ;
180142}
181143}
182- else if (or_clause (node ))
144+ else if (or_clause (expr ))
183145{
184146foreach (l , ((BoolExpr * )expr )-> args )
185147{
186- node = (Node * )lfirst (l );
187- frtn = TidqualFromExpr (varno , (Expr * )node );
148+ frtn = TidQualFromExpr (varno , (Node * )lfirst (l ));
188149if (frtn )
189150rlst = list_concat (rlst ,frtn );
190151else
@@ -199,25 +160,25 @@ TidqualFromExpr(int varno, Expr *expr)
199160return rlst ;
200161}
201162
163+ /*
164+ *Extract a set of CTID conditions from the given restrictinfo list
165+ *
166+ *This is essentially identical to the AND case of TidQualFromExpr,
167+ *except for the format of the input.
168+ */
202169static List *
203- TidqualFromRestrictinfo ( Relids relids ,List * restrictinfo )
170+ TidQualFromRestrictinfo ( int varno ,List * restrictinfo )
204171{
205- ListCell * l ;
206172List * rlst = NIL ;
207- int varno ;
208- Node * node ;
209- Expr * expr ;
173+ ListCell * l ;
210174
211- if (bms_membership (relids )!= BMS_SINGLETON )
212- return NIL ;
213- varno = bms_singleton_member (relids );
214175foreach (l ,restrictinfo )
215176{
216- node = (Node * )lfirst (l );
217- if (! IsA ( node , RestrictInfo ))
218- continue ;
219- expr = (( RestrictInfo * ) node ) -> clause ;
220- rlst = TidqualFromExpr (varno ,expr );
177+ RestrictInfo * rinfo = (RestrictInfo * )lfirst (l );
178+
179+ if (! IsA ( rinfo , RestrictInfo ))
180+ continue ; /* probably should never happen */
181+ rlst = TidQualFromExpr (varno ,( Node * ) rinfo -> clause );
221182if (rlst )
222183break ;
223184}
@@ -226,14 +187,16 @@ TidqualFromRestrictinfo(Relids relids, List *restrictinfo)
226187
227188/*
228189 * create_tidscan_paths
229- * Creates paths corresponding to tid direct scans of the given rel.
190+ * Create paths corresponding to direct TID scans of the given rel.
191+ *
230192 * Candidate paths are added to the rel's pathlist (using add_path).
231193 */
232194void
233195create_tidscan_paths (PlannerInfo * root ,RelOptInfo * rel )
234196{
235- List * tideval = TidqualFromRestrictinfo (rel -> relids ,
236- rel -> baserestrictinfo );
197+ List * tideval ;
198+
199+ tideval = TidQualFromRestrictinfo (rel -> relid ,rel -> baserestrictinfo );
237200
238201if (tideval )
239202add_path (rel , (Path * )create_tidscan_path (root ,rel ,tideval ));