LCOV - code coverage report
Current view: top level - src/backend/optimizer/path - tidpath.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 82 88 93.2 %
Date: 2017-09-29 15:12:54 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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);
      52             : static List *TidQualFromBaseRestrictinfo(RelOptInfo *rel);
      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       10879 : IsTidEqualClause(OpExpr *node, int varno)
      67             : {
      68             :     Node       *arg1,
      69             :                *arg2,
      70             :                *other;
      71             :     Var        *var;
      72             : 
      73             :     /* Operator must be tideq */
      74       10879 :     if (node->opno != TIDEqualOperator)
      75       10869 :         return false;
      76          10 :     if (list_length(node->args) != 2)
      77           0 :         return false;
      78          10 :     arg1 = linitial(node->args);
      79          10 :     arg2 = lsecond(node->args);
      80             : 
      81             :     /* Look for CTID as either argument */
      82          10 :     other = NULL;
      83          10 :     if (arg1 && IsA(arg1, Var))
      84             :     {
      85           8 :         var = (Var *) arg1;
      86          12 :         if (var->varattno == SelfItemPointerAttributeNumber &&
      87           8 :             var->vartype == TIDOID &&
      88           8 :             var->varno == varno &&
      89           4 :             var->varlevelsup == 0)
      90           4 :             other = arg2;
      91             :     }
      92          10 :     if (!other && arg2 && IsA(arg2, Var))
      93             :     {
      94           2 :         var = (Var *) arg2;
      95           4 :         if (var->varattno == SelfItemPointerAttributeNumber &&
      96           4 :             var->vartype == TIDOID &&
      97           4 :             var->varno == varno &&
      98           2 :             var->varlevelsup == 0)
      99           2 :             other = arg1;
     100             :     }
     101          10 :     if (!other)
     102           4 :         return false;
     103           6 :     if (exprType(other) != TIDOID)
     104           0 :         return false;           /* probably can't happen */
     105             : 
     106             :     /* The other argument must be a pseudoconstant */
     107           6 :     if (!is_pseudo_constant_clause(other))
     108           0 :         return false;
     109             : 
     110           6 :     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
     118         480 : IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
     119             : {
     120             :     Node       *arg1,
     121             :                *arg2;
     122             : 
     123             :     /* Operator must be tideq */
     124         480 :     if (node->opno != TIDEqualOperator)
     125         475 :         return false;
     126           5 :     if (!node->useOr)
     127           0 :         return false;
     128           5 :     Assert(list_length(node->args) == 2);
     129           5 :     arg1 = linitial(node->args);
     130           5 :     arg2 = lsecond(node->args);
     131             : 
     132             :     /* CTID must be first argument */
     133           5 :     if (arg1 && IsA(arg1, Var))
     134             :     {
     135           5 :         Var        *var = (Var *) arg1;
     136             : 
     137          10 :         if (var->varattno == SelfItemPointerAttributeNumber &&
     138          10 :             var->vartype == TIDOID &&
     139          10 :             var->varno == varno &&
     140           5 :             var->varlevelsup == 0)
     141             :         {
     142             :             /* The other argument must be a pseudoconstant */
     143           5 :             if (is_pseudo_constant_clause(arg2))
     144           5 :                 return true;    /* success */
     145             :         }
     146             :     }
     147             : 
     148           0 :     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       13982 : TidQualFromExpr(Node *expr, int varno)
     168             : {
     169       13982 :     List       *rlst = NIL;
     170             :     ListCell   *l;
     171             : 
     172       13982 :     if (is_opclause(expr))
     173             :     {
     174             :         /* base case: check for tideq opclause */
     175       21758 :         if (IsTidEqualClause((OpExpr *) expr, varno))
     176           6 :             rlst = list_make1(expr);
     177             :     }
     178        3103 :     else if (expr && IsA(expr, ScalarArrayOpExpr))
     179             :     {
     180             :         /* another base case: check for tid = ANY clause */
     181         960 :         if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
     182           5 :             rlst = list_make1(expr);
     183             :     }
     184        2623 :     else if (expr && IsA(expr, CurrentOfExpr))
     185             :     {
     186             :         /* another base case: check for CURRENT OF on this rel */
     187         114 :         if (((CurrentOfExpr *) expr)->cvarno == varno)
     188          57 :             rlst = list_make1(expr);
     189             :     }
     190        2566 :     else if (and_clause(expr))
     191             :     {
     192          34 :         foreach(l, ((BoolExpr *) expr)->args)
     193             :         {
     194          25 :             rlst = TidQualFromExpr((Node *) lfirst(l), varno);
     195          25 :             if (rlst)
     196           4 :                 break;
     197             :         }
     198             :     }
     199        2553 :     else if (or_clause(expr))
     200             :     {
     201         192 :         foreach(l, ((BoolExpr *) expr)->args)
     202             :         {
     203         190 :             List       *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
     204             : 
     205         190 :             if (frtn)
     206           4 :                 rlst = list_concat(rlst, frtn);
     207             :             else
     208             :             {
     209         186 :                 if (rlst)
     210           0 :                     list_free(rlst);
     211         186 :                 rlst = NIL;
     212         186 :                 break;
     213             :             }
     214             :         }
     215             :     }
     216       13982 :     return rlst;
     217             : }
     218             : 
     219             : /*
     220             :  *  Extract a set of CTID conditions from the rel's baserestrictinfo list
     221             :  */
     222             : static List *
     223       15511 : TidQualFromBaseRestrictinfo(RelOptInfo *rel)
     224             : {
     225       15511 :     List       *rlst = NIL;
     226             :     ListCell   *l;
     227             : 
     228       29447 :     foreach(l, rel->baserestrictinfo)
     229             :     {
     230       14002 :         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       14002 :         if (!restriction_is_securely_promotable(rinfo, rel))
     237         235 :             continue;
     238             : 
     239       13767 :         rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid);
     240       13767 :         if (rlst)
     241          66 :             break;
     242             :     }
     243       15511 :     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
     253       15511 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
     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       15511 :     required_outer = rel->lateral_relids;
     264             : 
     265       15511 :     tidquals = TidQualFromBaseRestrictinfo(rel);
     266             : 
     267       15511 :     if (tidquals)
     268          66 :         add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
     269             :                                                    required_outer));
     270       15511 : }

Generated by: LCOV version 1.11