text: /*-------------------------------------------------------------------------
   *
   * tidpath.c
   *      Routines to determine which TID conditions are usable for scanning
   *      a given relation, and create TidPaths accordingly.
   *
   * What we are looking for here is WHERE conditions of the form
   * "CTID = pseudoconstant", which can be implemented by just fetching
   * the tuple directly via heap_fetch().  We can also handle OR'd conditions
   * such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
   * conditions of the form CTID = ANY(pseudoconstant_array).  In particular
   * this allows
   *        WHERE ctid IN (tid1, tid2, ...)
   *
   * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr),
   * which amount to "CTID = run-time-determined-TID".  These could in
   * theory be translated to a simple comparison of CTID to the result of
   * a function, but in practice it works better to keep the special node
   * representation all the way through to execution.
   *
   * There is currently no special support for joins involving CTID; in
   * particular nothing corresponding to best_inner_indexscan().  Since it's
   * not very useful to store TIDs of one table in another table, there
   * doesn't seem to be enough use-case to justify adding a lot of code
   * for that.
   *
   *
   * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *      src/backend/optimizer/path/tidpath.c
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"
  
  #include "access/sysattr.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_type.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/clauses.h"
  #include "optimizer/pathnode.h"
  #include "optimizer/paths.h"
  #include "optimizer/restrictinfo.h"
  
  
  static bool IsTidEqualClause(OpExpr *node, int varno);
  static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
  static List *TidQualFromExpr(Node *expr, int varno);
  static List *TidQualFromBaseRestrictinfo(RelOptInfo *rel);
  
  
  /*
   * Check to see if an opclause is of the form
   *        CTID = pseudoconstant
   * or
   *        pseudoconstant = CTID
   *
   * We check that the CTID Var belongs to relation "varno".  That is probably
   * redundant considering this is only applied to restriction clauses, but
   * let's be safe.
   */
  static bool
  IsTidEqualClause(OpExpr *node, int varno)
  {// #lizard forgives
      Node       *arg1,
                 *arg2,
                 *other;
      Var           *var;
  
      /* Operator must be tideq */
      if (node->opno != TIDEqualOperator)
          return false;
      if (list_length(node->args) != 2)
          return false;
      arg1 = linitial(node->args);
      arg2 = lsecond(node->args);
  
      /* Look for CTID as either argument */
      other = NULL;
      if (arg1 && IsA(arg1, Var))
      {
          var = (Var *) arg1;
          if (var->varattno == SelfItemPointerAttributeNumber &&
              var->vartype == TIDOID &&
              var->varno == varno &&
              var->varlevelsup == 0)
              other = arg2;
      }
      if (!other && arg2 && IsA(arg2, Var))
      {
          var = (Var *) arg2;
          if (var->varattno == SelfItemPointerAttributeNumber &&
              var->vartype == TIDOID &&
              var->varno == varno &&
              var->varlevelsup == 0)
              other = arg1;
      }
      if (!other)
          return false;
      if (exprType(other) != TIDOID)
          return false;            /* probably can't happen */
  
      /* The other argument must be a pseudoconstant */
      if (!is_pseudo_constant_clause(other))
          return false;
  
      return true;                /* success */
  }
  
  /*
   * Check to see if a clause is of the form
   *        CTID = ANY (pseudoconstant_array)
   */
  static bool
  IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
  {// #lizard forgives
      Node       *arg1,
                 *arg2;
  
      /* Operator must be tideq */
      if (node->opno != TIDEqualOperator)
          return false;
      if (!node->useOr)
          return false;
      Assert(list_length(node->args) == 2);
      arg1 = linitial(node->args);
      arg2 = lsecond(node->args);
  
      /* CTID must be first argument */
      if (arg1 && IsA(arg1, Var))
      {
          Var           *var = (Var *) arg1;
  
          if (var->varattno == SelfItemPointerAttributeNumber &&
              var->vartype == TIDOID &&
              var->varno == varno &&
              var->varlevelsup == 0)
          {
              /* The other argument must be a pseudoconstant */
              if (is_pseudo_constant_clause(arg2))
                  return true;    /* success */
          }
      }
  
      return false;
  }
  
  /*
   *    Extract a set of CTID conditions from the given qual expression
   *
   *    Returns a List of CTID qual expressions (with implicit OR semantics
   *    across the list), or NIL if there are no usable conditions.
   *
   *    If the expression is an AND clause, we can use a CTID condition
   *    from any sub-clause.  If it is an OR clause, we must be able to
   *    extract a CTID condition from every sub-clause, or we can't use it.
   *
   *    In theory, in the AND case we could get CTID conditions from different
   *    sub-clauses, in which case we could try to pick the most efficient one.
   *    In practice, such usage seems very unlikely, so we don't bother; we
   *    just exit as soon as we find the first candidate.
   */
  static List *
  TidQualFromExpr(Node *expr, int varno)
  {// #lizard forgives
      List       *rlst = NIL;
      ListCell   *l;
  
      if (is_opclause(expr))
      {
          /* base case: check for tideq opclause */
          if (IsTidEqualClause((OpExpr *) expr, varno))
              rlst = list_make1(expr);
      }
      else if (expr && IsA(expr, ScalarArrayOpExpr))
      {
          /* another base case: check for tid = ANY clause */
          if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
              rlst = list_make1(expr);
      }
      else if (expr && IsA(expr, CurrentOfExpr))
      {
          /* another base case: check for CURRENT OF on this rel */
          if (((CurrentOfExpr *) expr)->cvarno == varno)
              rlst = list_make1(expr);
      }
      else if (and_clause(expr))
      {
          foreach(l, ((BoolExpr *) expr)->args)
          {
              rlst = TidQualFromExpr((Node *) lfirst(l), varno);
              if (rlst)
                  break;
          }
      }
      else if (or_clause(expr))
      {
          foreach(l, ((BoolExpr *) expr)->args)
          {
              List       *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
  
              if (frtn)
                  rlst = list_concat(rlst, frtn);
              else
              {
                  if (rlst)
                      list_free(rlst);
                  rlst = NIL;
                  break;
              }
          }
      }
      return rlst;
  }
  
  /*
   *    Extract a set of CTID conditions from the rel's baserestrictinfo list
   */
  static List *
  TidQualFromBaseRestrictinfo(RelOptInfo *rel)
  {
      List       *rlst = NIL;
      ListCell   *l;
  
      foreach(l, rel->baserestrictinfo)
      {
          RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
  
          /*
           * If clause must wait till after some lower-security-level
           * restriction clause, reject it.
           */
          if (!restriction_is_securely_promotable(rinfo, rel))
              continue;
  
          rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid);
          if (rlst)
              break;
      }
      return rlst;
  }
  
  /*
   * create_tidscan_paths
   *      Create paths corresponding to direct TID scans of the given rel.
   *
   *      Candidate paths are added to the rel's pathlist (using add_path).
   */
  void
  create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
  {
      Relids        required_outer;
      List       *tidquals;
  
      /*
       * We don't support pushing join clauses into the quals of a tidscan, but
       * it could still have required parameterization due to LATERAL refs in
       * its tlist.
       */
      required_outer = rel->lateral_relids;
  
      tidquals = TidQualFromBaseRestrictinfo(rel);
  
      if (tidquals)
          add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
                                                     required_outer));
  }
  