1 /*-------------------------------------------------------------------------
4 * Routines to determine which TID conditions are usable for scanning
5 * a given relation, and create TidPaths accordingly.
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
13 * WHERE ctid IN (tid1, tid2, ...)
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.
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
28 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
29 * Portions Copyright (c) 1994, Regents of the University of California
35 *-------------------------------------------------------------------------
39 #include "access/sysattr.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_type.h"
42 #include "nodes/nodeFuncs.h"
43 #include "optimizer/clauses.h"
44 #include "optimizer/pathnode.h"
45 #include "optimizer/paths.h"
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
);
55 * Check to see if an opclause is of the form
56 * CTID = pseudoconstant
58 * pseudoconstant = CTID
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
65 IsTidEqualClause(OpExpr
*node
, int varno
)
72 /* Operator must be tideq */
73 if (node
->opno
!= TIDEqualOperator
)
75 if (list_length(node
->args
) != 2)
77 arg1
= linitial(node
->args
);
78 arg2
= lsecond(node
->args
);
80 /* Look for CTID as either argument */
82 if (arg1
&& IsA(arg1
, Var
))
85 if (var
->varattno
== SelfItemPointerAttributeNumber
&&
86 var
->vartype
== TIDOID
&&
87 var
->varno
== varno
&&
88 var
->varlevelsup
== 0)
91 if (!other
&& arg2
&& IsA(arg2
, Var
))
94 if (var
->varattno
== SelfItemPointerAttributeNumber
&&
95 var
->vartype
== TIDOID
&&
96 var
->varno
== varno
&&
97 var
->varlevelsup
== 0)
102 if (exprType(other
) != TIDOID
)
103 return false; /* probably can't happen */
105 /* The other argument must be a pseudoconstant */
106 if (!is_pseudo_constant_clause(other
))
109 return true; /* success */
113 * Check to see if a clause is of the form
114 * CTID = ANY (pseudoconstant_array)
117 IsTidEqualAnyClause(ScalarArrayOpExpr
*node
, int varno
)
122 /* Operator must be tideq */
123 if (node
->opno
!= TIDEqualOperator
)
127 Assert(list_length(node
->args
) == 2);
128 arg1
= linitial(node
->args
);
129 arg2
= lsecond(node
->args
);
131 /* CTID must be first argument */
132 if (arg1
&& IsA(arg1
, Var
))
134 Var
*var
= (Var
*) arg1
;
136 if (var
->varattno
== SelfItemPointerAttributeNumber
&&
137 var
->vartype
== TIDOID
&&
138 var
->varno
== varno
&&
139 var
->varlevelsup
== 0)
141 /* The other argument must be a pseudoconstant */
142 if (is_pseudo_constant_clause(arg2
))
143 return true; /* success */
151 * Extract a set of CTID conditions from the given qual expression
153 * Returns a List of CTID qual expressions (with implicit OR semantics
154 * across the list), or NIL if there are no usable conditions.
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.
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.
166 TidQualFromExpr(Node
*expr
, int varno
)
171 if (is_opclause(expr
))
173 /* base case: check for tideq opclause */
174 if (IsTidEqualClause((OpExpr
*) expr
, varno
))
175 rlst
= list_make1(expr
);
177 else if (expr
&& IsA(expr
, ScalarArrayOpExpr
))
179 /* another base case: check for tid = ANY clause */
180 if (IsTidEqualAnyClause((ScalarArrayOpExpr
*) expr
, varno
))
181 rlst
= list_make1(expr
);
183 else if (expr
&& IsA(expr
, CurrentOfExpr
))
185 /* another base case: check for CURRENT OF on this rel */
186 if (((CurrentOfExpr
*) expr
)->cvarno
== varno
)
187 rlst
= list_make1(expr
);
189 else if (and_clause(expr
))
191 foreach(l
, ((BoolExpr
*) expr
)->args
)
193 rlst
= TidQualFromExpr((Node
*) lfirst(l
), varno
);
198 else if (or_clause(expr
))
200 foreach(l
, ((BoolExpr
*) expr
)->args
)
202 List
*frtn
= TidQualFromExpr((Node
*) lfirst(l
), varno
);
205 rlst
= list_concat(rlst
, frtn
);
219 * Extract a set of CTID conditions from the given restrictinfo list
221 * This is essentially identical to the AND case of TidQualFromExpr,
222 * except for the format of the input.
225 TidQualFromRestrictinfo(List
*restrictinfo
, int varno
)
230 foreach(l
, restrictinfo
)
232 RestrictInfo
*rinfo
= (RestrictInfo
*) lfirst(l
);
234 if (!IsA(rinfo
, RestrictInfo
))
235 continue; /* probably should never happen */
236 rlst
= TidQualFromExpr((Node
*) rinfo
->clause
, varno
);
244 * create_tidscan_paths
245 * Create paths corresponding to direct TID scans of the given rel.
247 * Candidate paths are added to the rel's pathlist (using add_path).
250 create_tidscan_paths(PlannerInfo
*root
, RelOptInfo
*rel
)
254 tidquals
= TidQualFromRestrictinfo(rel
->baserestrictinfo
, rel
->relid
);
257 add_path(rel
, (Path
*) create_tidscan_path(root
, rel
, tidquals
));