LCOV - code coverage report
Current view: top level - src/backend/optimizer/path - tidpath.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 96.9 % 226 219
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 81.8 % 187 153

             Branch data     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 and TidRangePaths accordingly.
       6                 :             :  *
       7                 :             :  * For TidPaths, we look for 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                 :             :  * As with indexscans, our definition of "pseudoconstant" is pretty liberal:
      16                 :             :  * we allow anything that doesn't involve a volatile function or a Var of
      17                 :             :  * the relation under consideration.  Vars belonging to other relations of
      18                 :             :  * the query are allowed, giving rise to parameterized TID scans.
      19                 :             :  *
      20                 :             :  * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr),
      21                 :             :  * which amount to "CTID = run-time-determined-TID".  These could in
      22                 :             :  * theory be translated to a simple comparison of CTID to the result of
      23                 :             :  * a function, but in practice it works better to keep the special node
      24                 :             :  * representation all the way through to execution.
      25                 :             :  *
      26                 :             :  * Additionally, TidRangePaths may be created for conditions of the form
      27                 :             :  * "CTID relop pseudoconstant", where relop is one of >,>=,<,<=, and
      28                 :             :  * AND-clauses composed of such conditions.
      29                 :             :  *
      30                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      31                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      32                 :             :  *
      33                 :             :  *
      34                 :             :  * IDENTIFICATION
      35                 :             :  *        src/backend/optimizer/path/tidpath.c
      36                 :             :  *
      37                 :             :  *-------------------------------------------------------------------------
      38                 :             :  */
      39                 :             : #include "postgres.h"
      40                 :             : 
      41                 :             : #include "access/sysattr.h"
      42                 :             : #include "catalog/pg_operator.h"
      43                 :             : #include "catalog/pg_type.h"
      44                 :             : #include "nodes/nodeFuncs.h"
      45                 :             : #include "optimizer/cost.h"
      46                 :             : #include "optimizer/optimizer.h"
      47                 :             : #include "optimizer/pathnode.h"
      48                 :             : #include "optimizer/paths.h"
      49                 :             : #include "optimizer/restrictinfo.h"
      50                 :             : 
      51                 :             : 
      52                 :             : /*
      53                 :             :  * Does this Var represent the CTID column of the specified baserel?
      54                 :             :  */
      55                 :             : static inline bool
      56                 :       87637 : IsCTIDVar(Var *var, RelOptInfo *rel)
      57                 :             : {
      58                 :             :         /* The vartype check is strictly paranoia */
      59         [ +  + ]:       87637 :         if (var->varattno == SelfItemPointerAttributeNumber &&
      60         [ +  - ]:         796 :                 var->vartype == TIDOID &&
      61         [ +  + ]:         796 :                 var->varno == rel->relid &&
      62   [ +  -  -  + ]:         779 :                 var->varnullingrels == NULL &&
      63                 :         779 :                 var->varlevelsup == 0)
      64                 :         779 :                 return true;
      65                 :       86858 :         return false;
      66                 :       87637 : }
      67                 :             : 
      68                 :             : /*
      69                 :             :  * Check to see if a RestrictInfo is of the form
      70                 :             :  *              CTID OP pseudoconstant
      71                 :             :  * or
      72                 :             :  *              pseudoconstant OP CTID
      73                 :             :  * where OP is a binary operation, the CTID Var belongs to relation "rel",
      74                 :             :  * and nothing on the other side of the clause does.
      75                 :             :  */
      76                 :             : static bool
      77                 :       82907 : IsBinaryTidClause(RestrictInfo *rinfo, RelOptInfo *rel)
      78                 :             : {
      79                 :       82907 :         OpExpr     *node;
      80                 :       82907 :         Node       *arg1,
      81                 :             :                            *arg2,
      82                 :             :                            *other;
      83                 :       82907 :         Relids          other_relids;
      84                 :             : 
      85                 :             :         /* Must be an OpExpr */
      86         [ +  + ]:       82907 :         if (!is_opclause(rinfo->clause))
      87                 :       14589 :                 return false;
      88                 :       68318 :         node = (OpExpr *) rinfo->clause;
      89                 :             : 
      90                 :             :         /* OpExpr must have two arguments */
      91         [ +  + ]:       68318 :         if (list_length(node->args) != 2)
      92                 :           8 :                 return false;
      93                 :       68310 :         arg1 = linitial(node->args);
      94                 :       68310 :         arg2 = lsecond(node->args);
      95                 :             : 
      96                 :             :         /* Look for CTID as either argument */
      97                 :       68310 :         other = NULL;
      98                 :       68310 :         other_relids = NULL;
      99   [ +  -  +  +  :       68310 :         if (arg1 && IsA(arg1, Var) &&
                   +  + ]
     100                 :       63578 :                 IsCTIDVar((Var *) arg1, rel))
     101                 :             :         {
     102                 :         715 :                 other = arg2;
     103                 :         715 :                 other_relids = rinfo->right_relids;
     104                 :         715 :         }
     105   [ +  +  +  -  :       68310 :         if (!other && arg2 && IsA(arg2, Var) &&
             +  +  +  + ]
     106                 :       10711 :                 IsCTIDVar((Var *) arg2, rel))
     107                 :             :         {
     108                 :          35 :                 other = arg1;
     109                 :          35 :                 other_relids = rinfo->left_relids;
     110                 :          35 :         }
     111         [ +  + ]:       68310 :         if (!other)
     112                 :       67560 :                 return false;
     113                 :             : 
     114                 :             :         /* The other argument must be a pseudoconstant */
     115   [ +  -  -  + ]:         750 :         if (bms_is_member(rel->relid, other_relids) ||
     116                 :         750 :                 contain_volatile_functions(other))
     117                 :           0 :                 return false;
     118                 :             : 
     119                 :         750 :         return true;                            /* success */
     120                 :       82907 : }
     121                 :             : 
     122                 :             : /*
     123                 :             :  * Check to see if a RestrictInfo is of the form
     124                 :             :  *              CTID = pseudoconstant
     125                 :             :  * or
     126                 :             :  *              pseudoconstant = CTID
     127                 :             :  * where the CTID Var belongs to relation "rel", and nothing on the
     128                 :             :  * other side of the clause does.
     129                 :             :  */
     130                 :             : static bool
     131                 :       45767 : IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
     132                 :             : {
     133         [ +  + ]:       45767 :         if (!IsBinaryTidClause(rinfo, rel))
     134                 :       45367 :                 return false;
     135                 :             : 
     136         [ +  + ]:         400 :         if (((OpExpr *) rinfo->clause)->opno == TIDEqualOperator)
     137                 :          37 :                 return true;
     138                 :             : 
     139                 :         363 :         return false;
     140                 :       45767 : }
     141                 :             : 
     142                 :             : /*
     143                 :             :  * Check to see if a RestrictInfo is of the form
     144                 :             :  *              CTID OP pseudoconstant
     145                 :             :  * or
     146                 :             :  *              pseudoconstant OP CTID
     147                 :             :  * where OP is a range operator such as <, <=, >, or >=, the CTID Var belongs
     148                 :             :  * to relation "rel", and nothing on the other side of the clause does.
     149                 :             :  */
     150                 :             : static bool
     151                 :       37140 : IsTidRangeClause(RestrictInfo *rinfo, RelOptInfo *rel)
     152                 :             : {
     153                 :       37140 :         Oid                     opno;
     154                 :             : 
     155         [ +  + ]:       37140 :         if (!IsBinaryTidClause(rinfo, rel))
     156                 :       36790 :                 return false;
     157                 :         350 :         opno = ((OpExpr *) rinfo->clause)->opno;
     158                 :             : 
     159   [ +  +  +  + ]:         350 :         if (opno == TIDLessOperator || opno == TIDLessEqOperator ||
     160   [ +  +  +  + ]:         321 :                 opno == TIDGreaterOperator || opno == TIDGreaterEqOperator)
     161                 :         343 :                 return true;
     162                 :             : 
     163                 :           7 :         return false;
     164                 :       37140 : }
     165                 :             : 
     166                 :             : /*
     167                 :             :  * Check to see if a RestrictInfo is of the form
     168                 :             :  *              CTID = ANY (pseudoconstant_array)
     169                 :             :  * where the CTID Var belongs to relation "rel", and nothing on the
     170                 :             :  * other side of the clause does.
     171                 :             :  */
     172                 :             : static bool
     173                 :       35886 : IsTidEqualAnyClause(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
     174                 :             : {
     175                 :       35886 :         ScalarArrayOpExpr *node;
     176                 :       35886 :         Node       *arg1,
     177                 :             :                            *arg2;
     178                 :             : 
     179                 :             :         /* Must be a ScalarArrayOpExpr */
     180   [ +  -  +  + ]:       35886 :         if (!(rinfo->clause && IsA(rinfo->clause, ScalarArrayOpExpr)))
     181                 :       34012 :                 return false;
     182                 :        1874 :         node = (ScalarArrayOpExpr *) rinfo->clause;
     183                 :             : 
     184                 :             :         /* Operator must be tideq */
     185         [ +  + ]:        1874 :         if (node->opno != TIDEqualOperator)
     186                 :        1864 :                 return false;
     187         [ +  - ]:          10 :         if (!node->useOr)
     188                 :           0 :                 return false;
     189         [ +  - ]:          10 :         Assert(list_length(node->args) == 2);
     190                 :          10 :         arg1 = linitial(node->args);
     191                 :          10 :         arg2 = lsecond(node->args);
     192                 :             : 
     193                 :             :         /* CTID must be first argument */
     194   [ +  -  +  -  :          10 :         if (arg1 && IsA(arg1, Var) &&
                   -  + ]
     195                 :          10 :                 IsCTIDVar((Var *) arg1, rel))
     196                 :             :         {
     197                 :             :                 /* The other argument must be a pseudoconstant */
     198   [ +  -  -  + ]:          10 :                 if (bms_is_member(rel->relid, pull_varnos(root, arg2)) ||
     199                 :          10 :                         contain_volatile_functions(arg2))
     200                 :           0 :                         return false;
     201                 :             : 
     202                 :          10 :                 return true;                    /* success */
     203                 :             :         }
     204                 :             : 
     205                 :           0 :         return false;
     206                 :       35886 : }
     207                 :             : 
     208                 :             : /*
     209                 :             :  * Check to see if a RestrictInfo is a CurrentOfExpr referencing "rel".
     210                 :             :  */
     211                 :             : static bool
     212                 :       35962 : IsCurrentOfClause(RestrictInfo *rinfo, RelOptInfo *rel)
     213                 :             : {
     214                 :       35962 :         CurrentOfExpr *node;
     215                 :             : 
     216                 :             :         /* Must be a CurrentOfExpr */
     217   [ +  -  +  + ]:       35962 :         if (!(rinfo->clause && IsA(rinfo->clause, CurrentOfExpr)))
     218                 :       35830 :                 return false;
     219                 :         132 :         node = (CurrentOfExpr *) rinfo->clause;
     220                 :             : 
     221                 :             :         /* If it references this rel, we're good */
     222         [ +  - ]:         132 :         if (node->cvarno == rel->relid)
     223                 :         132 :                 return true;
     224                 :             : 
     225                 :           0 :         return false;
     226                 :       35962 : }
     227                 :             : 
     228                 :             : /*
     229                 :             :  * Is the RestrictInfo usable as a CTID qual for the specified rel?
     230                 :             :  *
     231                 :             :  * This function considers only base cases; AND/OR combination is handled
     232                 :             :  * below.
     233                 :             :  */
     234                 :             : static bool
     235                 :       37350 : RestrictInfoIsTidQual(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
     236                 :             : {
     237                 :             :         /*
     238                 :             :          * We may ignore pseudoconstant clauses (they can't contain Vars, so could
     239                 :             :          * not match anyway).
     240                 :             :          */
     241         [ +  + ]:       37350 :         if (rinfo->pseudoconstant)
     242                 :        1141 :                 return false;
     243                 :             : 
     244                 :             :         /*
     245                 :             :          * If clause must wait till after some lower-security-level restriction
     246                 :             :          * clause, reject it.
     247                 :             :          */
     248         [ +  + ]:       36209 :         if (!restriction_is_securely_promotable(rinfo, rel))
     249                 :         309 :                 return false;
     250                 :             : 
     251                 :             :         /*
     252                 :             :          * Check all base cases.
     253                 :             :          */
     254         [ +  + ]:       35900 :         if (IsTidEqualClause(rinfo, rel) ||
     255   [ +  +  +  + ]:       35886 :                 IsTidEqualAnyClause(root, rinfo, rel) ||
     256                 :       35876 :                 IsCurrentOfClause(rinfo, rel))
     257                 :          90 :                 return true;
     258                 :             : 
     259                 :       35810 :         return false;
     260                 :       37350 : }
     261                 :             : 
     262                 :             : /*
     263                 :             :  * Extract a set of CTID conditions from implicit-AND List of RestrictInfos
     264                 :             :  *
     265                 :             :  * Returns a List of CTID qual RestrictInfos for the specified rel (with
     266                 :             :  * implicit OR semantics across the list), or NIL if there are no usable
     267                 :             :  * equality conditions.
     268                 :             :  *
     269                 :             :  * This function is mainly concerned with handling AND/OR recursion.
     270                 :             :  * However, we do have a special rule to enforce: if there is a CurrentOfExpr
     271                 :             :  * qual, we *must* return that and only that, else the executor may fail.
     272                 :             :  * Ordinarily a CurrentOfExpr would be all alone anyway because of grammar
     273                 :             :  * restrictions, but it is possible for RLS quals to appear AND'ed with it.
     274                 :             :  * It's even possible (if fairly useless) for the RLS quals to be CTID quals.
     275                 :             :  * So we must scan the whole rlist to see if there's a CurrentOfExpr.  Since
     276                 :             :  * we have to do that, we also apply some very-trivial preference rules about
     277                 :             :  * which of the other possibilities should be chosen, in the unlikely event
     278                 :             :  * that there's more than one choice.
     279                 :             :  */
     280                 :             : static List *
     281                 :       43289 : TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel,
     282                 :             :                                                         bool *isCurrentOf)
     283                 :             : {
     284                 :       43289 :         RestrictInfo *tidclause = NULL; /* best simple CTID qual so far */
     285                 :       43289 :         List       *orlist = NIL;       /* best OR'ed CTID qual so far */
     286                 :       43289 :         ListCell   *l;
     287                 :             : 
     288                 :       43289 :         *isCurrentOf = false;
     289                 :             : 
     290   [ +  +  +  +  :       80732 :         foreach(l, rlist)
             +  +  +  + ]
     291                 :             :         {
     292                 :       37443 :                 RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
     293                 :             : 
     294         [ +  + ]:       37443 :                 if (restriction_is_or_clause(rinfo))
     295                 :             :                 {
     296                 :         588 :                         List       *rlst = NIL;
     297                 :         588 :                         ListCell   *j;
     298                 :             : 
     299                 :             :                         /*
     300                 :             :                          * We must be able to extract a CTID condition from every
     301                 :             :                          * sub-clause of an OR, or we can't use it.
     302                 :             :                          */
     303   [ +  -  +  +  :        1180 :                         foreach(j, ((BoolExpr *) rinfo->orclause)->args)
                   +  + ]
     304                 :             :                         {
     305                 :         592 :                                 Node       *orarg = (Node *) lfirst(j);
     306                 :         592 :                                 List       *sublist;
     307                 :             : 
     308                 :             :                                 /* OR arguments should be ANDs or sub-RestrictInfos */
     309         [ +  + ]:         592 :                                 if (is_andclause(orarg))
     310                 :             :                                 {
     311                 :          97 :                                         List       *andargs = ((BoolExpr *) orarg)->args;
     312                 :          97 :                                         bool            sublistIsCurrentOf;
     313                 :             : 
     314                 :             :                                         /* Recurse in case there are sub-ORs */
     315                 :          97 :                                         sublist = TidQualFromRestrictInfoList(root, andargs, rel,
     316                 :             :                                                                                                                   &sublistIsCurrentOf);
     317         [ +  - ]:          97 :                                         if (sublistIsCurrentOf)
     318   [ #  #  #  # ]:           0 :                                                 elog(ERROR, "IS CURRENT OF within OR clause");
     319                 :          97 :                                 }
     320                 :             :                                 else
     321                 :             :                                 {
     322                 :         495 :                                         RestrictInfo *ri = castNode(RestrictInfo, orarg);
     323                 :             : 
     324         [ -  + ]:         495 :                                         Assert(!restriction_is_or_clause(ri));
     325         [ +  + ]:         495 :                                         if (RestrictInfoIsTidQual(root, ri, rel))
     326                 :           4 :                                                 sublist = list_make1(ri);
     327                 :             :                                         else
     328                 :         491 :                                                 sublist = NIL;
     329                 :         495 :                                 }
     330                 :             : 
     331                 :             :                                 /*
     332                 :             :                                  * If nothing found in this arm, we can't do anything with
     333                 :             :                                  * this OR clause.
     334                 :             :                                  */
     335         [ +  + ]:         592 :                                 if (sublist == NIL)
     336                 :             :                                 {
     337                 :         584 :                                         rlst = NIL; /* forget anything we had */
     338                 :         584 :                                         break;          /* out of loop over OR args */
     339                 :             :                                 }
     340                 :             : 
     341                 :             :                                 /*
     342                 :             :                                  * OK, continue constructing implicitly-OR'ed result list.
     343                 :             :                                  */
     344                 :           8 :                                 rlst = list_concat(rlst, sublist);
     345         [ +  + ]:         592 :                         }
     346                 :             : 
     347         [ +  + ]:         588 :                         if (rlst)
     348                 :             :                         {
     349                 :             :                                 /*
     350                 :             :                                  * Accept the OR'ed list if it's the first one, or if it's
     351                 :             :                                  * shorter than the previous one.
     352                 :             :                                  */
     353   [ -  +  #  # ]:           4 :                                 if (orlist == NIL || list_length(rlst) < list_length(orlist))
     354                 :           4 :                                         orlist = rlst;
     355                 :           4 :                         }
     356                 :         588 :                 }
     357                 :             :                 else
     358                 :             :                 {
     359                 :             :                         /* Not an OR clause, so handle base cases */
     360         [ +  + ]:       36855 :                         if (RestrictInfoIsTidQual(root, rinfo, rel))
     361                 :             :                         {
     362                 :             :                                 /* We can stop immediately if it's a CurrentOfExpr */
     363         [ +  + ]:          86 :                                 if (IsCurrentOfClause(rinfo, rel))
     364                 :             :                                 {
     365                 :          66 :                                         *isCurrentOf = true;
     366                 :          66 :                                         return list_make1(rinfo);
     367                 :             :                                 }
     368                 :             : 
     369                 :             :                                 /*
     370                 :             :                                  * Otherwise, remember the first non-OR CTID qual.  We could
     371                 :             :                                  * try to apply some preference order if there's more than
     372                 :             :                                  * one, but such usage seems very unlikely, so don't bother.
     373                 :             :                                  */
     374         [ -  + ]:          20 :                                 if (tidclause == NULL)
     375                 :          20 :                                         tidclause = rinfo;
     376                 :          20 :                         }
     377                 :             :                 }
     378         [ +  + ]:       37443 :         }
     379                 :             : 
     380                 :             :         /*
     381                 :             :          * Prefer any singleton CTID qual to an OR'ed list.  Again, it seems
     382                 :             :          * unlikely to be worth thinking harder than that.
     383                 :             :          */
     384         [ +  + ]:       43223 :         if (tidclause)
     385                 :          18 :                 return list_make1(tidclause);
     386                 :       43205 :         return orlist;
     387                 :       43289 : }
     388                 :             : 
     389                 :             : /*
     390                 :             :  * Extract a set of CTID range conditions from implicit-AND List of RestrictInfos
     391                 :             :  *
     392                 :             :  * Returns a List of CTID range qual RestrictInfos for the specified rel
     393                 :             :  * (with implicit AND semantics across the list), or NIL if there are no
     394                 :             :  * usable range conditions or if the rel's table AM does not support TID range
     395                 :             :  * scans.
     396                 :             :  */
     397                 :             : static List *
     398                 :       43098 : TidRangeQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
     399                 :             : {
     400                 :       43098 :         List       *rlst = NIL;
     401                 :       43098 :         ListCell   *l;
     402                 :             : 
     403         [ -  + ]:       43098 :         if ((rel->amflags & AMFLAG_HAS_TID_RANGE) == 0)
     404                 :           0 :                 return NIL;
     405                 :             : 
     406   [ +  +  +  +  :       80238 :         foreach(l, rlist)
                   +  + ]
     407                 :             :         {
     408                 :       37140 :                 RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
     409                 :             : 
     410         [ +  + ]:       37140 :                 if (IsTidRangeClause(rinfo, rel))
     411                 :         343 :                         rlst = lappend(rlst, rinfo);
     412                 :       37140 :         }
     413                 :             : 
     414                 :       43098 :         return rlst;
     415                 :       43098 : }
     416                 :             : 
     417                 :             : /*
     418                 :             :  * Given a list of join clauses involving our rel, create a parameterized
     419                 :             :  * TidPath for each one that is a suitable TidEqual clause.
     420                 :             :  *
     421                 :             :  * In principle we could combine clauses that reference the same outer rels,
     422                 :             :  * but it doesn't seem like such cases would arise often enough to be worth
     423                 :             :  * troubling over.
     424                 :             :  */
     425                 :             : static void
     426                 :       56387 : BuildParameterizedTidPaths(PlannerInfo *root, RelOptInfo *rel, List *clauses)
     427                 :             : {
     428                 :       56387 :         ListCell   *l;
     429                 :             : 
     430   [ +  +  +  +  :       67874 :         foreach(l, clauses)
                   +  + ]
     431                 :             :         {
     432                 :       11487 :                 RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
     433                 :       11487 :                 List       *tidquals;
     434                 :       11487 :                 Relids          required_outer;
     435                 :             : 
     436                 :             :                 /*
     437                 :             :                  * Validate whether each clause is actually usable; we must check this
     438                 :             :                  * even when examining clauses generated from an EquivalenceClass,
     439                 :             :                  * since they might not satisfy the restriction on not having Vars of
     440                 :             :                  * our rel on the other side, or somebody might've built an operator
     441                 :             :                  * class that accepts type "tid" but has other operators in it.
     442                 :             :                  *
     443                 :             :                  * We currently consider only TidEqual join clauses.  In principle we
     444                 :             :                  * might find a suitable ScalarArrayOpExpr in the rel's joininfo list,
     445                 :             :                  * but it seems unlikely to be worth expending the cycles to check.
     446                 :             :                  * And we definitely won't find a CurrentOfExpr here.  Hence, we don't
     447                 :             :                  * use RestrictInfoIsTidQual; but this must match that function
     448                 :             :                  * otherwise.
     449                 :             :                  */
     450         [ +  + ]:       11487 :                 if (rinfo->pseudoconstant ||
     451   [ +  -  +  + ]:        9867 :                         !restriction_is_securely_promotable(rinfo, rel) ||
     452                 :        9867 :                         !IsTidEqualClause(rinfo, rel))
     453                 :       11464 :                         continue;
     454                 :             : 
     455                 :             :                 /*
     456                 :             :                  * Check if clause can be moved to this rel; this is probably
     457                 :             :                  * redundant when considering EC-derived clauses, but we must check it
     458                 :             :                  * for "loose" join clauses.
     459                 :             :                  */
     460         [ +  + ]:          23 :                 if (!join_clause_is_movable_to(rinfo, rel))
     461                 :           2 :                         continue;
     462                 :             : 
     463                 :             :                 /* OK, make list of clauses for this path */
     464                 :          21 :                 tidquals = list_make1(rinfo);
     465                 :             : 
     466                 :             :                 /* Compute required outer rels for this path */
     467                 :          21 :                 required_outer = bms_union(rinfo->required_relids, rel->lateral_relids);
     468                 :          21 :                 required_outer = bms_del_member(required_outer, rel->relid);
     469                 :             : 
     470                 :          42 :                 add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
     471                 :          21 :                                                                                                    required_outer));
     472      [ -  +  + ]:       11487 :         }
     473                 :       56387 : }
     474                 :             : 
     475                 :             : /*
     476                 :             :  * Test whether an EquivalenceClass member matches our rel's CTID Var.
     477                 :             :  *
     478                 :             :  * This is a callback for use by generate_implied_equalities_for_column.
     479                 :             :  */
     480                 :             : static bool
     481                 :       13946 : ec_member_matches_ctid(PlannerInfo *root, RelOptInfo *rel,
     482                 :             :                                            EquivalenceClass *ec, EquivalenceMember *em,
     483                 :             :                                            void *arg)
     484                 :             : {
     485   [ +  -  +  +  :       13946 :         if (em->em_expr && IsA(em->em_expr, Var) &&
                   +  + ]
     486                 :       13338 :                 IsCTIDVar((Var *) em->em_expr, rel))
     487                 :          19 :                 return true;
     488                 :       13927 :         return false;
     489                 :       13946 : }
     490                 :             : 
     491                 :             : /*
     492                 :             :  * create_tidscan_paths
     493                 :             :  *        Create paths corresponding to direct TID scans of the given rel and add
     494                 :             :  *        them to the corresponding path list via add_path or add_partial_path.
     495                 :             :  */
     496                 :             : bool
     497                 :       43192 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
     498                 :             : {
     499                 :       43192 :         List       *tidquals;
     500                 :       43192 :         List       *tidrangequals;
     501                 :       43192 :         bool            isCurrentOf;
     502                 :       43192 :         bool            enabled = (rel->pgs_mask & PGS_TIDSCAN) != 0;
     503                 :             : 
     504                 :             :         /*
     505                 :             :          * If any suitable quals exist in the rel's baserestrict list, generate a
     506                 :             :          * plain (unparameterized) TidPath with them.
     507                 :             :          *
     508                 :             :          * We skip this when TID scans are disabled, except when the qual is
     509                 :             :          * CurrentOfExpr. In that case, a TID scan is the only correct path.
     510                 :             :          */
     511                 :       43192 :         tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel,
     512                 :             :                                                                                    &isCurrentOf);
     513                 :             : 
     514   [ +  +  +  +  :       43192 :         if (tidquals != NIL && (enabled || isCurrentOf))
                   -  + ]
     515                 :             :         {
     516                 :             :                 /*
     517                 :             :                  * This path uses no join clauses, but it could still have required
     518                 :             :                  * parameterization due to LATERAL refs in its tlist.
     519                 :             :                  */
     520                 :          83 :                 Relids          required_outer = rel->lateral_relids;
     521                 :             : 
     522                 :         166 :                 add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
     523                 :          83 :                                                                                                    required_outer));
     524                 :             : 
     525                 :             :                 /*
     526                 :             :                  * When the qual is CurrentOfExpr, the path that we just added is the
     527                 :             :                  * only one the executor can handle, so we should return before adding
     528                 :             :                  * any others. Returning true lets the caller know not to add any
     529                 :             :                  * others, either.
     530                 :             :                  */
     531         [ +  + ]:          83 :                 if (isCurrentOf)
     532                 :          66 :                         return true;
     533         [ +  + ]:          83 :         }
     534                 :             : 
     535                 :             :         /* Skip the rest if TID scans are disabled. */
     536         [ +  + ]:       43126 :         if (!enabled)
     537                 :          28 :                 return false;
     538                 :             : 
     539                 :             :         /*
     540                 :             :          * If there are range quals in the baserestrict list, generate a
     541                 :             :          * TidRangePath.
     542                 :             :          */
     543                 :       86196 :         tidrangequals = TidRangeQualFromRestrictInfoList(rel->baserestrictinfo,
     544                 :       43098 :                                                                                                          rel);
     545                 :             : 
     546         [ +  + ]:       43098 :         if (tidrangequals != NIL)
     547                 :             :         {
     548                 :             :                 /*
     549                 :             :                  * This path uses no join clauses, but it could still have required
     550                 :             :                  * parameterization due to LATERAL refs in its tlist.
     551                 :             :                  */
     552                 :         334 :                 Relids          required_outer = rel->lateral_relids;
     553                 :             : 
     554                 :         668 :                 add_path(rel, (Path *) create_tidrangescan_path(root, rel,
     555                 :         334 :                                                                                                                 tidrangequals,
     556                 :         334 :                                                                                                                 required_outer,
     557                 :             :                                                                                                                 0));
     558                 :             : 
     559                 :             :                 /* If appropriate, consider parallel tid range scan. */
     560   [ +  +  -  + ]:         334 :                 if (rel->consider_parallel && required_outer == NULL)
     561                 :             :                 {
     562                 :          37 :                         int                     parallel_workers;
     563                 :             : 
     564                 :          74 :                         parallel_workers = compute_parallel_worker(rel, rel->pages, -1,
     565                 :          37 :                                                                                                            max_parallel_workers_per_gather);
     566                 :             : 
     567         [ +  + ]:          37 :                         if (parallel_workers > 0)
     568                 :          16 :                                 add_partial_path(rel, (Path *) create_tidrangescan_path(root,
     569                 :           8 :                                                                                                                                                 rel,
     570                 :           8 :                                                                                                                                                 tidrangequals,
     571                 :           8 :                                                                                                                                                 required_outer,
     572                 :           8 :                                                                                                                                                 parallel_workers));
     573                 :          37 :                 }
     574                 :         334 :         }
     575                 :             : 
     576                 :             :         /*
     577                 :             :          * Try to generate parameterized TidPaths using equality clauses extracted
     578                 :             :          * from EquivalenceClasses.  (This is important since simple "t1.ctid =
     579                 :             :          * t2.ctid" clauses will turn into ECs.)
     580                 :             :          */
     581         [ +  + ]:       43098 :         if (rel->has_eclass_joins)
     582                 :             :         {
     583                 :       13289 :                 List       *clauses;
     584                 :             : 
     585                 :             :                 /* Generate clauses, skipping any that join to lateral_referencers */
     586                 :       26578 :                 clauses = generate_implied_equalities_for_column(root,
     587                 :       13289 :                                                                                                                  rel,
     588                 :             :                                                                                                                  ec_member_matches_ctid,
     589                 :             :                                                                                                                  NULL,
     590                 :       13289 :                                                                                                                  rel->lateral_referencers);
     591                 :             : 
     592                 :             :                 /* Generate a path for each usable join clause */
     593                 :       13289 :                 BuildParameterizedTidPaths(root, rel, clauses);
     594                 :       13289 :         }
     595                 :             : 
     596                 :             :         /*
     597                 :             :          * Also consider parameterized TidPaths using "loose" join quals.  Quals
     598                 :             :          * of the form "t1.ctid = t2.ctid" would turn into these if they are outer
     599                 :             :          * join quals, for example.
     600                 :             :          */
     601                 :       43098 :         BuildParameterizedTidPaths(root, rel, rel->joininfo);
     602                 :             : 
     603                 :       43098 :         return false;
     604                 :       43192 : }
        

Generated by: LCOV version 2.3.2-1