Header And Logo

PostgreSQL
| The world's most advanced open source database.

tidpath.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tidpath.c
00004  *    Routines to determine which TID conditions are usable for scanning
00005  *    a given relation, and create TidPaths accordingly.
00006  *
00007  * What we are looking for here is WHERE conditions of the form
00008  * "CTID = pseudoconstant", which can be implemented by just fetching
00009  * the tuple directly via heap_fetch().  We can also handle OR'd conditions
00010  * such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
00011  * conditions of the form CTID = ANY(pseudoconstant_array).  In particular
00012  * this allows
00013  *      WHERE ctid IN (tid1, tid2, ...)
00014  *
00015  * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr),
00016  * which amount to "CTID = run-time-determined-TID".  These could in
00017  * theory be translated to a simple comparison of CTID to the result of
00018  * a function, but in practice it works better to keep the special node
00019  * representation all the way through to execution.
00020  *
00021  * There is currently no special support for joins involving CTID; in
00022  * particular nothing corresponding to best_inner_indexscan().  Since it's
00023  * not very useful to store TIDs of one table in another table, there
00024  * doesn't seem to be enough use-case to justify adding a lot of code
00025  * for that.
00026  *
00027  *
00028  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00029  * Portions Copyright (c) 1994, Regents of the University of California
00030  *
00031  *
00032  * IDENTIFICATION
00033  *    src/backend/optimizer/path/tidpath.c
00034  *
00035  *-------------------------------------------------------------------------
00036  */
00037 #include "postgres.h"
00038 
00039 #include "access/sysattr.h"
00040 #include "catalog/pg_operator.h"
00041 #include "catalog/pg_type.h"
00042 #include "nodes/nodeFuncs.h"
00043 #include "optimizer/clauses.h"
00044 #include "optimizer/pathnode.h"
00045 #include "optimizer/paths.h"
00046 
00047 
00048 static bool IsTidEqualClause(OpExpr *node, int varno);
00049 static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
00050 static List *TidQualFromExpr(Node *expr, int varno);
00051 static List *TidQualFromRestrictinfo(List *restrictinfo, int varno);
00052 
00053 
00054 /*
00055  * Check to see if an opclause is of the form
00056  *      CTID = pseudoconstant
00057  * or
00058  *      pseudoconstant = CTID
00059  *
00060  * We check that the CTID Var belongs to relation "varno".  That is probably
00061  * redundant considering this is only applied to restriction clauses, but
00062  * let's be safe.
00063  */
00064 static bool
00065 IsTidEqualClause(OpExpr *node, int varno)
00066 {
00067     Node       *arg1,
00068                *arg2,
00069                *other;
00070     Var        *var;
00071 
00072     /* Operator must be tideq */
00073     if (node->opno != TIDEqualOperator)
00074         return false;
00075     if (list_length(node->args) != 2)
00076         return false;
00077     arg1 = linitial(node->args);
00078     arg2 = lsecond(node->args);
00079 
00080     /* Look for CTID as either argument */
00081     other = NULL;
00082     if (arg1 && IsA(arg1, Var))
00083     {
00084         var = (Var *) arg1;
00085         if (var->varattno == SelfItemPointerAttributeNumber &&
00086             var->vartype == TIDOID &&
00087             var->varno == varno &&
00088             var->varlevelsup == 0)
00089             other = arg2;
00090     }
00091     if (!other && arg2 && IsA(arg2, Var))
00092     {
00093         var = (Var *) arg2;
00094         if (var->varattno == SelfItemPointerAttributeNumber &&
00095             var->vartype == TIDOID &&
00096             var->varno == varno &&
00097             var->varlevelsup == 0)
00098             other = arg1;
00099     }
00100     if (!other)
00101         return false;
00102     if (exprType(other) != TIDOID)
00103         return false;           /* probably can't happen */
00104 
00105     /* The other argument must be a pseudoconstant */
00106     if (!is_pseudo_constant_clause(other))
00107         return false;
00108 
00109     return true;                /* success */
00110 }
00111 
00112 /*
00113  * Check to see if a clause is of the form
00114  *      CTID = ANY (pseudoconstant_array)
00115  */
00116 static bool
00117 IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
00118 {
00119     Node       *arg1,
00120                *arg2;
00121 
00122     /* Operator must be tideq */
00123     if (node->opno != TIDEqualOperator)
00124         return false;
00125     if (!node->useOr)
00126         return false;
00127     Assert(list_length(node->args) == 2);
00128     arg1 = linitial(node->args);
00129     arg2 = lsecond(node->args);
00130 
00131     /* CTID must be first argument */
00132     if (arg1 && IsA(arg1, Var))
00133     {
00134         Var        *var = (Var *) arg1;
00135 
00136         if (var->varattno == SelfItemPointerAttributeNumber &&
00137             var->vartype == TIDOID &&
00138             var->varno == varno &&
00139             var->varlevelsup == 0)
00140         {
00141             /* The other argument must be a pseudoconstant */
00142             if (is_pseudo_constant_clause(arg2))
00143                 return true;    /* success */
00144         }
00145     }
00146 
00147     return false;
00148 }
00149 
00150 /*
00151  *  Extract a set of CTID conditions from the given qual expression
00152  *
00153  *  Returns a List of CTID qual expressions (with implicit OR semantics
00154  *  across the list), or NIL if there are no usable conditions.
00155  *
00156  *  If the expression is an AND clause, we can use a CTID condition
00157  *  from any sub-clause.  If it is an OR clause, we must be able to
00158  *  extract a CTID condition from every sub-clause, or we can't use it.
00159  *
00160  *  In theory, in the AND case we could get CTID conditions from different
00161  *  sub-clauses, in which case we could try to pick the most efficient one.
00162  *  In practice, such usage seems very unlikely, so we don't bother; we
00163  *  just exit as soon as we find the first candidate.
00164  */
00165 static List *
00166 TidQualFromExpr(Node *expr, int varno)
00167 {
00168     List       *rlst = NIL;
00169     ListCell   *l;
00170 
00171     if (is_opclause(expr))
00172     {
00173         /* base case: check for tideq opclause */
00174         if (IsTidEqualClause((OpExpr *) expr, varno))
00175             rlst = list_make1(expr);
00176     }
00177     else if (expr && IsA(expr, ScalarArrayOpExpr))
00178     {
00179         /* another base case: check for tid = ANY clause */
00180         if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
00181             rlst = list_make1(expr);
00182     }
00183     else if (expr && IsA(expr, CurrentOfExpr))
00184     {
00185         /* another base case: check for CURRENT OF on this rel */
00186         if (((CurrentOfExpr *) expr)->cvarno == varno)
00187             rlst = list_make1(expr);
00188     }
00189     else if (and_clause(expr))
00190     {
00191         foreach(l, ((BoolExpr *) expr)->args)
00192         {
00193             rlst = TidQualFromExpr((Node *) lfirst(l), varno);
00194             if (rlst)
00195                 break;
00196         }
00197     }
00198     else if (or_clause(expr))
00199     {
00200         foreach(l, ((BoolExpr *) expr)->args)
00201         {
00202             List       *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
00203 
00204             if (frtn)
00205                 rlst = list_concat(rlst, frtn);
00206             else
00207             {
00208                 if (rlst)
00209                     list_free(rlst);
00210                 rlst = NIL;
00211                 break;
00212             }
00213         }
00214     }
00215     return rlst;
00216 }
00217 
00218 /*
00219  *  Extract a set of CTID conditions from the given restrictinfo list
00220  *
00221  *  This is essentially identical to the AND case of TidQualFromExpr,
00222  *  except for the format of the input.
00223  */
00224 static List *
00225 TidQualFromRestrictinfo(List *restrictinfo, int varno)
00226 {
00227     List       *rlst = NIL;
00228     ListCell   *l;
00229 
00230     foreach(l, restrictinfo)
00231     {
00232         RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
00233 
00234         if (!IsA(rinfo, RestrictInfo))
00235             continue;           /* probably should never happen */
00236         rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
00237         if (rlst)
00238             break;
00239     }
00240     return rlst;
00241 }
00242 
00243 /*
00244  * create_tidscan_paths
00245  *    Create paths corresponding to direct TID scans of the given rel.
00246  *
00247  *    Candidate paths are added to the rel's pathlist (using add_path).
00248  */
00249 void
00250 create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
00251 {
00252     Relids      required_outer;
00253     List       *tidquals;
00254 
00255     /*
00256      * We don't support pushing join clauses into the quals of a tidscan, but
00257      * it could still have required parameterization due to LATERAL refs in
00258      * its tlist.  (That can only happen if the tidscan is on a relation
00259      * pulled up out of a UNION ALL appendrel.)
00260      */
00261     required_outer = rel->lateral_relids;
00262 
00263     tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
00264 
00265     if (tidquals)
00266         add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
00267                                                    required_outer));
00268 }