OSDN Git Service

Support UPDATE/DELETE WHERE CURRENT OF cursor_name, per SQL standard.
[pg-rex/syncrep.git] / src / backend / optimizer / path / tidpath.c
1 /*-------------------------------------------------------------------------
2  *
3  * tidpath.c
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'd conditions
10  * such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
11  * conditions of the form CTID = ANY(pseudoconstant_array).  In particular
12  * this allows
13  *              WHERE ctid IN (tid1, tid2, ...)
14  *
15  * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr),
16  * which amount to "CTID = run-time-determined-TID".  These could in
17  * theory be translated to a simple comparison of CTID to the result of
18  * a function, but in practice it works better to keep the special node
19  * representation all the way through to execution.
20  *
21  * There is currently no special support for joins involving CTID; in
22  * particular nothing corresponding to best_inner_indexscan().  Since it's
23  * not very useful to store TIDs of one table in another table, there
24  * doesn't seem to be enough use-case to justify adding a lot of code
25  * for that.
26  *
27  *
28  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
29  * Portions Copyright (c) 1994, Regents of the University of California
30  *
31  *
32  * IDENTIFICATION
33  *        $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.30 2007/06/11 01:16:22 tgl Exp $
34  *
35  *-------------------------------------------------------------------------
36  */
37 #include "postgres.h"
38
39 #include "access/htup.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_type.h"
42 #include "optimizer/clauses.h"
43 #include "optimizer/pathnode.h"
44 #include "optimizer/paths.h"
45 #include "parser/parse_expr.h"
46
47
48 static bool IsTidEqualClause(OpExpr *node, int varno);
49 static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
50 static List *TidQualFromExpr(Node *expr, int varno);
51 static List *TidQualFromRestrictinfo(List *restrictinfo, int varno);
52
53
54 /*
55  * Check to see if an opclause is of the form
56  *              CTID = pseudoconstant
57  * or
58  *              pseudoconstant = CTID
59  *
60  * We check that the CTID Var belongs to relation "varno".      That is probably
61  * redundant considering this is only applied to restriction clauses, but
62  * let's be safe.
63  */
64 static bool
65 IsTidEqualClause(OpExpr *node, int varno)
66 {
67         Node       *arg1,
68                            *arg2,
69                            *other;
70         Var                *var;
71
72         /* Operator must be tideq */
73         if (node->opno != TIDEqualOperator)
74                 return false;
75         if (list_length(node->args) != 2)
76                 return false;
77         arg1 = linitial(node->args);
78         arg2 = lsecond(node->args);
79
80         /* Look for CTID as either argument */
81         other = NULL;
82         if (arg1 && IsA(arg1, Var))
83         {
84                 var = (Var *) arg1;
85                 if (var->varattno == SelfItemPointerAttributeNumber &&
86                         var->vartype == TIDOID &&
87                         var->varno == varno &&
88                         var->varlevelsup == 0)
89                         other = arg2;
90         }
91         if (!other && arg2 && IsA(arg2, Var))
92         {
93                 var = (Var *) arg2;
94                 if (var->varattno == SelfItemPointerAttributeNumber &&
95                         var->vartype == TIDOID &&
96                         var->varno == varno &&
97                         var->varlevelsup == 0)
98                         other = arg1;
99         }
100         if (!other)
101                 return false;
102         if (exprType(other) != TIDOID)
103                 return false;                   /* probably can't happen */
104
105         /* The other argument must be a pseudoconstant */
106         if (!is_pseudo_constant_clause(other))
107                 return false;
108
109         return true;                            /* success */
110 }
111
112 /*
113  * Check to see if a clause is of the form
114  *              CTID = ANY (pseudoconstant_array)
115  */
116 static bool
117 IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
118 {
119         Node       *arg1,
120                            *arg2;
121
122         /* Operator must be tideq */
123         if (node->opno != TIDEqualOperator)
124                 return false;
125         if (!node->useOr)
126                 return false;
127         Assert(list_length(node->args) == 2);
128         arg1 = linitial(node->args);
129         arg2 = lsecond(node->args);
130
131         /* CTID must be first argument */
132         if (arg1 && IsA(arg1, Var))
133         {
134                 Var                *var = (Var *) arg1;
135
136                 if (var->varattno == SelfItemPointerAttributeNumber &&
137                         var->vartype == TIDOID &&
138                         var->varno == varno &&
139                         var->varlevelsup == 0)
140                 {
141                         /* The other argument must be a pseudoconstant */
142                         if (is_pseudo_constant_clause(arg2))
143                                 return true;    /* success */
144                 }
145         }
146
147         return false;
148 }
149
150 /*
151  *      Extract a set of CTID conditions from the given qual expression
152  *
153  *      Returns a List of CTID qual expressions (with implicit OR semantics
154  *      across the list), or NIL if there are no usable conditions.
155  *
156  *      If the expression is an AND clause, we can use a CTID condition
157  *      from any sub-clause.  If it is an OR clause, we must be able to
158  *      extract a CTID condition from every sub-clause, or we can't use it.
159  *
160  *      In theory, in the AND case we could get CTID conditions from different
161  *      sub-clauses, in which case we could try to pick the most efficient one.
162  *      In practice, such usage seems very unlikely, so we don't bother; we
163  *      just exit as soon as we find the first candidate.
164  */
165 static List *
166 TidQualFromExpr(Node *expr, int varno)
167 {
168         List       *rlst = NIL;
169         ListCell   *l;
170
171         if (is_opclause(expr))
172         {
173                 /* base case: check for tideq opclause */
174                 if (IsTidEqualClause((OpExpr *) expr, varno))
175                         rlst = list_make1(expr);
176         }
177         else if (expr && IsA(expr, ScalarArrayOpExpr))
178         {
179                 /* another base case: check for tid = ANY clause */
180                 if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
181                         rlst = list_make1(expr);
182         }
183         else if (expr && IsA(expr, CurrentOfExpr))
184         {
185                 /* another base case: check for CURRENT OF on this rel */
186                 if (((CurrentOfExpr *) expr)->cvarno == varno)
187                         rlst = list_make1(expr);
188         }
189         else if (and_clause(expr))
190         {
191                 foreach(l, ((BoolExpr *) expr)->args)
192                 {
193                         rlst = TidQualFromExpr((Node *) lfirst(l), varno);
194                         if (rlst)
195                                 break;
196                 }
197         }
198         else if (or_clause(expr))
199         {
200                 foreach(l, ((BoolExpr *) expr)->args)
201                 {
202                         List       *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
203
204                         if (frtn)
205                                 rlst = list_concat(rlst, frtn);
206                         else
207                         {
208                                 if (rlst)
209                                         list_free(rlst);
210                                 rlst = NIL;
211                                 break;
212                         }
213                 }
214         }
215         return rlst;
216 }
217
218 /*
219  *      Extract a set of CTID conditions from the given restrictinfo list
220  *
221  *      This is essentially identical to the AND case of TidQualFromExpr,
222  *      except for the format of the input.
223  */
224 static List *
225 TidQualFromRestrictinfo(List *restrictinfo, int varno)
226 {
227         List       *rlst = NIL;
228         ListCell   *l;
229
230         foreach(l, restrictinfo)
231         {
232                 RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
233
234                 if (!IsA(rinfo, RestrictInfo))
235                         continue;                       /* probably should never happen */
236                 rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
237                 if (rlst)
238                         break;
239         }
240         return rlst;
241 }
242
243 /*
244  * create_tidscan_paths
245  *        Create paths corresponding to direct TID scans of the given rel.
246  *
247  *        Candidate paths are added to the rel's pathlist (using add_path).
248  */
249 void
250 create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
251 {
252         List       *tidquals;
253
254         tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
255
256         if (tidquals)
257                 add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals));
258 }