PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
tidpath.c
Go to the documentation of this file.
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-2017, PostgreSQL Global Development Group
29  * Portions Copyright (c) 1994, Regents of the University of California
30  *
31  *
32  * IDENTIFICATION
33  * src/backend/optimizer/path/tidpath.c
34  *
35  *-------------------------------------------------------------------------
36  */
37 #include "postgres.h"
38 
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"
46 #include "optimizer/restrictinfo.h"
47 
48 
49 static bool IsTidEqualClause(OpExpr *node, int varno);
50 static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
51 static List *TidQualFromExpr(Node *expr, int varno);
53 
54 
55 /*
56  * Check to see if an opclause is of the form
57  * CTID = pseudoconstant
58  * or
59  * pseudoconstant = CTID
60  *
61  * We check that the CTID Var belongs to relation "varno". That is probably
62  * redundant considering this is only applied to restriction clauses, but
63  * let's be safe.
64  */
65 static bool
66 IsTidEqualClause(OpExpr *node, int varno)
67 {
68  Node *arg1,
69  *arg2,
70  *other;
71  Var *var;
72 
73  /* Operator must be tideq */
74  if (node->opno != TIDEqualOperator)
75  return false;
76  if (list_length(node->args) != 2)
77  return false;
78  arg1 = linitial(node->args);
79  arg2 = lsecond(node->args);
80 
81  /* Look for CTID as either argument */
82  other = NULL;
83  if (arg1 && IsA(arg1, Var))
84  {
85  var = (Var *) arg1;
87  var->vartype == TIDOID &&
88  var->varno == varno &&
89  var->varlevelsup == 0)
90  other = arg2;
91  }
92  if (!other && arg2 && IsA(arg2, Var))
93  {
94  var = (Var *) arg2;
96  var->vartype == TIDOID &&
97  var->varno == varno &&
98  var->varlevelsup == 0)
99  other = arg1;
100  }
101  if (!other)
102  return false;
103  if (exprType(other) != TIDOID)
104  return false; /* probably can't happen */
105 
106  /* The other argument must be a pseudoconstant */
107  if (!is_pseudo_constant_clause(other))
108  return false;
109 
110  return true; /* success */
111 }
112 
113 /*
114  * Check to see if a clause is of the form
115  * CTID = ANY (pseudoconstant_array)
116  */
117 static bool
119 {
120  Node *arg1,
121  *arg2;
122 
123  /* Operator must be tideq */
124  if (node->opno != TIDEqualOperator)
125  return false;
126  if (!node->useOr)
127  return false;
128  Assert(list_length(node->args) == 2);
129  arg1 = linitial(node->args);
130  arg2 = lsecond(node->args);
131 
132  /* CTID must be first argument */
133  if (arg1 && IsA(arg1, Var))
134  {
135  Var *var = (Var *) arg1;
136 
138  var->vartype == TIDOID &&
139  var->varno == varno &&
140  var->varlevelsup == 0)
141  {
142  /* The other argument must be a pseudoconstant */
143  if (is_pseudo_constant_clause(arg2))
144  return true; /* success */
145  }
146  }
147 
148  return false;
149 }
150 
151 /*
152  * Extract a set of CTID conditions from the given qual expression
153  *
154  * Returns a List of CTID qual expressions (with implicit OR semantics
155  * across the list), or NIL if there are no usable conditions.
156  *
157  * If the expression is an AND clause, we can use a CTID condition
158  * from any sub-clause. If it is an OR clause, we must be able to
159  * extract a CTID condition from every sub-clause, or we can't use it.
160  *
161  * In theory, in the AND case we could get CTID conditions from different
162  * sub-clauses, in which case we could try to pick the most efficient one.
163  * In practice, such usage seems very unlikely, so we don't bother; we
164  * just exit as soon as we find the first candidate.
165  */
166 static List *
167 TidQualFromExpr(Node *expr, int varno)
168 {
169  List *rlst = NIL;
170  ListCell *l;
171 
172  if (is_opclause(expr))
173  {
174  /* base case: check for tideq opclause */
175  if (IsTidEqualClause((OpExpr *) expr, varno))
176  rlst = list_make1(expr);
177  }
178  else if (expr && IsA(expr, ScalarArrayOpExpr))
179  {
180  /* another base case: check for tid = ANY clause */
181  if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
182  rlst = list_make1(expr);
183  }
184  else if (expr && IsA(expr, CurrentOfExpr))
185  {
186  /* another base case: check for CURRENT OF on this rel */
187  if (((CurrentOfExpr *) expr)->cvarno == varno)
188  rlst = list_make1(expr);
189  }
190  else if (and_clause(expr))
191  {
192  foreach(l, ((BoolExpr *) expr)->args)
193  {
194  rlst = TidQualFromExpr((Node *) lfirst(l), varno);
195  if (rlst)
196  break;
197  }
198  }
199  else if (or_clause(expr))
200  {
201  foreach(l, ((BoolExpr *) expr)->args)
202  {
203  List *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
204 
205  if (frtn)
206  rlst = list_concat(rlst, frtn);
207  else
208  {
209  if (rlst)
210  list_free(rlst);
211  rlst = NIL;
212  break;
213  }
214  }
215  }
216  return rlst;
217 }
218 
219 /*
220  * Extract a set of CTID conditions from the rel's baserestrictinfo list
221  */
222 static List *
224 {
225  List *rlst = NIL;
226  ListCell *l;
227 
228  foreach(l, rel->baserestrictinfo)
229  {
230  RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
231 
232  /*
233  * If clause must wait till after some lower-security-level
234  * restriction clause, reject it.
235  */
236  if (!restriction_is_securely_promotable(rinfo, rel))
237  continue;
238 
239  rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid);
240  if (rlst)
241  break;
242  }
243  return rlst;
244 }
245 
246 /*
247  * create_tidscan_paths
248  * Create paths corresponding to direct TID scans of the given rel.
249  *
250  * Candidate paths are added to the rel's pathlist (using add_path).
251  */
252 void
254 {
255  Relids required_outer;
256  List *tidquals;
257 
258  /*
259  * We don't support pushing join clauses into the quals of a tidscan, but
260  * it could still have required parameterization due to LATERAL refs in
261  * its tlist.
262  */
263  required_outer = rel->lateral_relids;
264 
265  tidquals = TidQualFromBaseRestrictinfo(rel);
266 
267  if (tidquals)
268  add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
269  required_outer));
270 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:559
Index varlevelsup
Definition: primnodes.h:151
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:412
List * baserestrictinfo
Definition: relation.h:544
Definition: nodes.h:508
AttrNumber varattno
Definition: primnodes.h:146
List * list_concat(List *list1, List *list2)
Definition: list.c:321
Definition: primnodes.h:141
static List * TidQualFromBaseRestrictinfo(RelOptInfo *rel)
Definition: tidpath.c:223
#define lsecond(l)
Definition: pg_list.h:114
static List * TidQualFromExpr(Node *expr, int varno)
Definition: tidpath.c:167
#define list_make1(x1)
Definition: pg_list.h:133
Relids lateral_relids
Definition: relation.h:515
#define TIDOID
Definition: pg_type.h:332
void create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
Definition: tidpath.c:253
#define linitial(l)
Definition: pg_list.h:110
static bool IsTidEqualClause(OpExpr *node, int varno)
Definition: tidpath.c:66
#define is_opclause(clause)
Definition: clauses.h:20
Oid vartype
Definition: primnodes.h:148
bool and_clause(Node *clause)
Definition: clauses.c:313
static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
Definition: tidpath.c:118
bool is_pseudo_constant_clause(Node *clause)
Definition: clauses.c:2141
Index relid
Definition: relation.h:518
Expr * clause
Definition: relation.h:1637
Index varno
Definition: primnodes.h:144
bool or_clause(Node *clause)
Definition: clauses.c:279
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:671
#define lfirst(lc)
Definition: pg_list.h:106
bool restriction_is_securely_promotable(RestrictInfo *restrictinfo, RelOptInfo *rel)
Definition: restrictinfo.c:308
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:89
TidPath * create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals, Relids required_outer)
Definition: pathnode.c:1171
void list_free(List *list)
Definition: list.c:1133
Oid opno
Definition: primnodes.h:473
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
List * args
Definition: primnodes.h:479
Definition: pg_list.h:45
#define TIDEqualOperator
Definition: pg_operator.h:162
Definition: relation.h:888