LCOV - differential code coverage report
Current view: top level - src/backend/optimizer/path - tidpath.c (source / functions) Coverage Total Hit UNC UBC GBC GNC CBC DUB DCB
Current: 0e5ff9b9b45a657aea12440478dc002e9b01f138 vs 0123ce131fca454009439dfa3b2266d1d40737d7 Lines: 95.8 % 168 161 7 1 7 153 2
Current Date: 2026-03-14 14:10:32 -0400 Functions: 100.0 % 12 12 1 11
Baseline: lcov-20260315-024220-baseline Branches: 82.2 % 174 143 2 29 12 131 4 4
Baseline Date: 2026-03-14 15:27:56 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 100.0 % 7 7 7
(360..) days: 95.7 % 161 154 7 1 153
Function coverage date bins:
(360..) days: 100.0 % 12 12 1 11
Branch coverage date bins:
(30,360] days: 85.7 % 14 12 2 12
(360..) days: 81.9 % 160 131 29 131

 Age         Owner                    Branch data    TLA  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
 2632 tgl@sss.pgh.pa.us          56                 :CBC      538853 : IsCTIDVar(Var *var, RelOptInfo *rel)
                                 57                 :                : {
                                 58                 :                :     /* The vartype check is strictly paranoia */
                                 59         [ +  + ]:         538853 :     if (var->varattno == SelfItemPointerAttributeNumber &&
                                 60         [ +  - ]:           2670 :         var->vartype == TIDOID &&
                                 61         [ +  + ]:           2670 :         var->varno == rel->relid &&
 1140                            62         [ +  - ]:           2610 :         var->varnullingrels == NULL &&
 2632                            63         [ +  - ]:           2610 :         var->varlevelsup == 0)
                                 64                 :           2610 :         return true;
                                 65                 :         536243 :     return false;
                                 66                 :                : }
                                 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
 1842 drowley@postgresql.o       77                 :         507027 : IsBinaryTidClause(RestrictInfo *rinfo, RelOptInfo *rel)
                                 78                 :                : {
                                 79                 :                :     OpExpr     *node;
                                 80                 :                :     Node       *arg1,
                                 81                 :                :                *arg2,
                                 82                 :                :                *other;
                                 83                 :                :     Relids      other_relids;
                                 84                 :                : 
                                 85                 :                :     /* Must be an OpExpr */
 2632 tgl@sss.pgh.pa.us          86         [ +  + ]:         507027 :     if (!is_opclause(rinfo->clause))
                                 87                 :          98378 :         return false;
                                 88                 :         408649 :     node = (OpExpr *) rinfo->clause;
                                 89                 :                : 
                                 90                 :                :     /* OpExpr must have two arguments */
 1842 drowley@postgresql.o       91         [ +  + ]:         408649 :     if (list_length(node->args) != 2)
 7414 tgl@sss.pgh.pa.us          92                 :             24 :         return false;
 7963 neilc@samurai.com          93                 :         408625 :     arg1 = linitial(node->args);
 9609 bruce@momjian.us           94                 :         408625 :     arg2 = lsecond(node->args);
                                 95                 :                : 
                                 96                 :                :     /* Look for CTID as either argument */
 7509 tgl@sss.pgh.pa.us          97                 :         408625 :     other = NULL;
 2632                            98                 :         408625 :     other_relids = NULL;
                                 99   [ +  -  +  +  :         800395 :     if (arg1 && IsA(arg1, Var) &&
                                              +  + ]
                                100                 :         391770 :         IsCTIDVar((Var *) arg1, rel))
                                101                 :                :     {
                                102                 :           2399 :         other = arg2;
                                103                 :           2399 :         other_relids = rinfo->right_relids;
                                104                 :                :     }
                                105   [ +  +  +  -  :         472620 :     if (!other && arg2 && IsA(arg2, Var) &&
                                        +  +  +  + ]
                                106                 :          63995 :         IsCTIDVar((Var *) arg2, rel))
                                107                 :                :     {
                                108                 :            114 :         other = arg1;
                                109                 :            114 :         other_relids = rinfo->left_relids;
                                110                 :                :     }
 7509                           111         [ +  + ]:         408625 :     if (!other)
 7414                           112                 :         406112 :         return false;
                                113                 :                : 
                                114                 :                :     /* The other argument must be a pseudoconstant */
 2632                           115   [ +  -  -  + ]:           5026 :     if (bms_is_member(rel->relid, other_relids) ||
                                116                 :           2513 :         contain_volatile_functions(other))
 7414 tgl@sss.pgh.pa.us         117                 :UBC           0 :         return false;
                                118                 :                : 
 7414 tgl@sss.pgh.pa.us         119                 :CBC        2513 :     return true;                /* success */
                                120                 :                : }
                                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
 1842 drowley@postgresql.o      131                 :         286126 : IsTidEqualClause(RestrictInfo *rinfo, RelOptInfo *rel)
                                132                 :                : {
                                133         [ +  + ]:         286126 :     if (!IsBinaryTidClause(rinfo, rel))
                                134                 :         284783 :         return false;
                                135                 :                : 
                                136         [ +  + ]:           1343 :     if (((OpExpr *) rinfo->clause)->opno == TIDEqualOperator)
                                137                 :            239 :         return true;
                                138                 :                : 
                                139                 :           1104 :     return false;
                                140                 :                : }
                                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                 :         220901 : IsTidRangeClause(RestrictInfo *rinfo, RelOptInfo *rel)
                                152                 :                : {
                                153                 :                :     Oid         opno;
                                154                 :                : 
                                155         [ +  + ]:         220901 :     if (!IsBinaryTidClause(rinfo, rel))
                                156                 :         219731 :         return false;
                                157                 :           1170 :     opno = ((OpExpr *) rinfo->clause)->opno;
                                158                 :                : 
                                159   [ +  +  +  +  :           1170 :     if (opno == TIDLessOperator || opno == TIDLessEqOperator ||
                                              +  + ]
                                160         [ +  + ]:           1036 :         opno == TIDGreaterOperator || opno == TIDGreaterEqOperator)
                                161                 :           1030 :         return true;
                                162                 :                : 
                                163                 :            140 :     return false;
                                164                 :                : }
                                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
 1879 tgl@sss.pgh.pa.us         173                 :         218678 : IsTidEqualAnyClause(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
                                174                 :                : {
                                175                 :                :     ScalarArrayOpExpr *node;
                                176                 :                :     Node       *arg1,
                                177                 :                :                *arg2;
                                178                 :                : 
                                179                 :                :     /* Must be a ScalarArrayOpExpr */
 2632                           180   [ +  -  +  + ]:         218678 :     if (!(rinfo->clause && IsA(rinfo->clause, ScalarArrayOpExpr)))
                                181                 :         208733 :         return false;
                                182                 :           9945 :     node = (ScalarArrayOpExpr *) rinfo->clause;
                                183                 :                : 
                                184                 :                :     /* Operator must be tideq */
 7414                           185         [ +  + ]:           9945 :     if (node->opno != TIDEqualOperator)
                                186                 :           9914 :         return false;
                                187         [ -  + ]:             31 :     if (!node->useOr)
 7414 tgl@sss.pgh.pa.us         188                 :UBC           0 :         return false;
 7414 tgl@sss.pgh.pa.us         189         [ -  + ]:CBC          31 :     Assert(list_length(node->args) == 2);
                                190                 :             31 :     arg1 = linitial(node->args);
                                191                 :             31 :     arg2 = lsecond(node->args);
                                192                 :                : 
                                193                 :                :     /* CTID must be first argument */
 2632                           194   [ +  -  +  -  :             62 :     if (arg1 && IsA(arg1, Var) &&
                                              +  - ]
                                195                 :             31 :         IsCTIDVar((Var *) arg1, rel))
                                196                 :                :     {
                                197                 :                :         /* The other argument must be a pseudoconstant */
 1879                           198   [ +  -  -  + ]:             62 :         if (bms_is_member(rel->relid, pull_varnos(root, arg2)) ||
 2632                           199                 :             31 :             contain_volatile_functions(arg2))
 2632 tgl@sss.pgh.pa.us         200                 :UBC           0 :             return false;
                                201                 :                : 
 2632 tgl@sss.pgh.pa.us         202                 :CBC          31 :         return true;            /* success */
                                203                 :                :     }
                                204                 :                : 
 7414 tgl@sss.pgh.pa.us         205                 :UBC           0 :     return false;
                                206                 :                : }
                                207                 :                : 
                                208                 :                : /*
                                209                 :                :  * Check to see if a RestrictInfo is a CurrentOfExpr referencing "rel".
                                210                 :                :  */
                                211                 :                : static bool
 2632 tgl@sss.pgh.pa.us         212                 :CBC      219027 : IsCurrentOfClause(RestrictInfo *rinfo, RelOptInfo *rel)
                                213                 :                : {
                                214                 :                :     CurrentOfExpr *node;
                                215                 :                : 
                                216                 :                :     /* Must be a CurrentOfExpr */
                                217   [ +  -  +  + ]:         219027 :     if (!(rinfo->clause && IsA(rinfo->clause, CurrentOfExpr)))
                                218                 :         218623 :         return false;
                                219                 :            404 :     node = (CurrentOfExpr *) rinfo->clause;
                                220                 :                : 
                                221                 :                :     /* If it references this rel, we're good */
                                222         [ +  - ]:            404 :     if (node->cvarno == rel->relid)
                                223                 :            404 :         return true;
                                224                 :                : 
 2632 tgl@sss.pgh.pa.us         225                 :UBC           0 :     return false;
                                226                 :                : }
                                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
  677 tgl@sss.pgh.pa.us         235                 :CBC      221733 : 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                 :                :      */
 2632                           241         [ +  + ]:         221733 :     if (rinfo->pseudoconstant)
  677                           242                 :           1963 :         return false;
                                243                 :                : 
                                244                 :                :     /*
                                245                 :                :      * If clause must wait till after some lower-security-level restriction
                                246                 :                :      * clause, reject it.
                                247                 :                :      */
 2632                           248         [ +  + ]:         219770 :     if (!restriction_is_securely_promotable(rinfo, rel))
  677                           249                 :            931 :         return false;
                                250                 :                : 
                                251                 :                :     /*
                                252                 :                :      * Check all base cases.
                                253                 :                :      */
 2632                           254   [ +  +  +  + ]:         437517 :     if (IsTidEqualClause(rinfo, rel) ||
 1879                           255         [ +  + ]:         437325 :         IsTidEqualAnyClause(root, rinfo, rel) ||
 2632                           256                 :         218647 :         IsCurrentOfClause(rinfo, rel))
  677                           257                 :            394 :         return true;
                                258                 :                : 
                                259                 :         218445 :     return false;
                                260                 :                : }
                                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 *
  601 rhaas@postgresql.org      281                 :         229455 : TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel,
                                282                 :                :                             bool *isCurrentOf)
                                283                 :                : {
  677 tgl@sss.pgh.pa.us         284                 :         229455 :     RestrictInfo *tidclause = NULL; /* best simple CTID qual so far */
                                285                 :         229455 :     List       *orlist = NIL;   /* best OR'ed CTID qual so far */
                                286                 :                :     ListCell   *l;
                                287                 :                : 
  601 rhaas@postgresql.org      288                 :         229455 :     *isCurrentOf = false;
                                289                 :                : 
 2632 tgl@sss.pgh.pa.us         290   [ +  +  +  +  :         451393 :     foreach(l, rlist)
                                              +  + ]
                                291                 :                :     {
                                292                 :         222140 :         RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                                293                 :                : 
                                294         [ +  + ]:         222140 :         if (restriction_is_or_clause(rinfo))
                                295                 :                :         {
  677                           296                 :           3380 :             List       *rlst = NIL;
                                297                 :                :             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                 :                :              */
 2632                           303   [ +  -  +  +  :           3406 :             foreach(j, ((BoolExpr *) rinfo->orclause)->args)
                                              +  + ]
                                304                 :                :             {
                                305                 :           3393 :                 Node       *orarg = (Node *) lfirst(j);
                                306                 :                :                 List       *sublist;
                                307                 :                : 
                                308                 :                :                 /* OR arguments should be ANDs or sub-RestrictInfos */
 2602                           309         [ +  + ]:           3393 :                 if (is_andclause(orarg))
                                310                 :                :                 {
 2632                           311                 :            420 :                     List       *andargs = ((BoolExpr *) orarg)->args;
                                312                 :                :                     bool        sublistIsCurrentOf;
                                313                 :                : 
                                314                 :                :                     /* Recurse in case there are sub-ORs */
  601 rhaas@postgresql.org      315                 :            420 :                     sublist = TidQualFromRestrictInfoList(root, andargs, rel,
                                316                 :                :                                                           &sublistIsCurrentOf);
                                317         [ -  + ]:            420 :                     if (sublistIsCurrentOf)
  601 rhaas@postgresql.org      318         [ #  # ]:UBC           0 :                         elog(ERROR, "IS CURRENT OF within OR clause");
                                319                 :                :                 }
                                320                 :                :                 else
                                321                 :                :                 {
 1257 drowley@postgresql.o      322                 :CBC        2973 :                     RestrictInfo *ri = castNode(RestrictInfo, orarg);
                                323                 :                : 
                                324         [ -  + ]:           2973 :                     Assert(!restriction_is_or_clause(ri));
  677 tgl@sss.pgh.pa.us         325         [ +  + ]:           2973 :                     if (RestrictInfoIsTidQual(root, ri, rel))
                                326                 :             14 :                         sublist = list_make1(ri);
                                327                 :                :                     else
                                328                 :           2959 :                         sublist = NIL;
                                329                 :                :                 }
                                330                 :                : 
                                331                 :                :                 /*
                                332                 :                :                  * If nothing found in this arm, we can't do anything with
                                333                 :                :                  * this OR clause.
                                334                 :                :                  */
 2632                           335         [ +  + ]:           3393 :                 if (sublist == NIL)
                                336                 :                :                 {
                                337                 :           3367 :                     rlst = NIL; /* forget anything we had */
                                338                 :           3367 :                     break;      /* out of loop over OR args */
                                339                 :                :                 }
                                340                 :                : 
                                341                 :                :                 /*
                                342                 :                :                  * OK, continue constructing implicitly-OR'ed result list.
                                343                 :                :                  */
                                344                 :             26 :                 rlst = list_concat(rlst, sublist);
                                345                 :                :             }
                                346                 :                : 
  677                           347         [ +  + ]:           3380 :             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   [ -  +  -  - ]:             13 :                 if (orlist == NIL || list_length(rlst) < list_length(orlist))
                                354                 :             13 :                     orlist = rlst;
                                355                 :                :             }
                                356                 :                :         }
                                357                 :                :         else
                                358                 :                :         {
                                359                 :                :             /* Not an OR clause, so handle base cases */
                                360         [ +  + ]:         218760 :             if (RestrictInfoIsTidQual(root, rinfo, rel))
                                361                 :                :             {
                                362                 :                :                 /* We can stop immediately if it's a CurrentOfExpr */
                                363         [ +  + ]:            380 :                 if (IsCurrentOfClause(rinfo, rel))
                                364                 :                :                 {
  601 rhaas@postgresql.org      365                 :            202 :                     *isCurrentOf = true;
  677 tgl@sss.pgh.pa.us         366                 :            202 :                     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         [ +  - ]:            178 :                 if (tidclause == NULL)
                                375                 :            178 :                     tidclause = rinfo;
                                376                 :                :             }
                                377                 :                :         }
                                378                 :                :     }
                                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         [ +  + ]:         229253 :     if (tidclause)
                                385                 :            172 :         return list_make1(tidclause);
                                386                 :         229081 :     return orlist;
                                387                 :                : }
                                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 *
 1842 drowley@postgresql.o      398                 :         228805 : TidRangeQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
                                399                 :                : {
                                400                 :         228805 :     List       *rlst = NIL;
                                401                 :                :     ListCell   *l;
                                402                 :                : 
                                403         [ -  + ]:         228805 :     if ((rel->amflags & AMFLAG_HAS_TID_RANGE) == 0)
 1842 drowley@postgresql.o      404                 :UBC           0 :         return NIL;
                                405                 :                : 
 1842 drowley@postgresql.o      406   [ +  +  +  +  :CBC      449706 :     foreach(l, rlist)
                                              +  + ]
                                407                 :                :     {
                                408                 :         220901 :         RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                                409                 :                : 
                                410         [ +  + ]:         220901 :         if (IsTidRangeClause(rinfo, rel))
                                411                 :           1030 :             rlst = lappend(rlst, rinfo);
                                412                 :                :     }
                                413                 :                : 
                                414                 :         228805 :     return rlst;
                                415                 :                : }
                                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
 2632 tgl@sss.pgh.pa.us         426                 :         309235 : BuildParameterizedTidPaths(PlannerInfo *root, RelOptInfo *rel, List *clauses)
                                427                 :                : {
                                428                 :                :     ListCell   *l;
                                429                 :                : 
                                430   [ +  +  +  +  :         384788 :     foreach(l, clauses)
                                              +  + ]
                                431                 :                :     {
                                432                 :          75553 :         RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                                433                 :                :         List       *tidquals;
                                434                 :                :         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                 :                :          */
 2631                           450         [ +  + ]:          75553 :         if (rinfo->pseudoconstant ||
                                451         [ +  - ]:          67287 :             !restriction_is_securely_promotable(rinfo, rel) ||
                                452         [ +  + ]:          67287 :             !IsTidEqualClause(rinfo, rel))
 3343                           453                 :          75481 :             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                 :                :          */
 2632                           460         [ +  + ]:             78 :         if (!join_clause_is_movable_to(rinfo, rel))
                                461                 :              6 :             continue;
                                462                 :                : 
                                463                 :                :         /* OK, make list of clauses for this path */
                                464                 :             72 :         tidquals = list_make1(rinfo);
                                465                 :                : 
                                466                 :                :         /* Compute required outer rels for this path */
                                467                 :             72 :         required_outer = bms_union(rinfo->required_relids, rel->lateral_relids);
                                468                 :             72 :         required_outer = bms_del_member(required_outer, rel->relid);
                                469                 :                : 
                                470                 :             72 :         add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
                                471                 :                :                                                    required_outer));
                                472                 :                :     }
                                473                 :         309235 : }
                                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                 :          84952 : ec_member_matches_ctid(PlannerInfo *root, RelOptInfo *rel,
                                482                 :                :                        EquivalenceClass *ec, EquivalenceMember *em,
                                483                 :                :                        void *arg)
                                484                 :                : {
                                485   [ +  -  +  +  :         168009 :     if (em->em_expr && IsA(em->em_expr, Var) &&
                                              +  + ]
                                486                 :          83057 :         IsCTIDVar((Var *) em->em_expr, rel))
                                487                 :             66 :         return true;
                                488                 :          84886 :     return false;
                                489                 :                : }
                                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
 7588                           497                 :         229035 : create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
                                498                 :                : {
                                499                 :                :     List       *tidquals;
                                500                 :                :     List       *tidrangequals;
                                501                 :                :     bool        isCurrentOf;
   46 rhaas@postgresql.org      502                 :GNC      229035 :     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                 :                :      */
  601 rhaas@postgresql.org      511                 :CBC      229035 :     tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel,
                                512                 :                :                                            &isCurrentOf);
                                513                 :                : 
   46 rhaas@postgresql.org      514   [ +  +  +  +  :GNC      229035 :     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                 :                :          */
 2632 tgl@sss.pgh.pa.us         520                 :CBC         374 :         Relids      required_outer = rel->lateral_relids;
                                521                 :                : 
 4949                           522                 :            374 :         add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
                                523                 :                :                                                    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                 :                :          */
  601 rhaas@postgresql.org      531         [ +  + ]:            374 :         if (isCurrentOf)
                                532                 :            202 :             return true;
                                533                 :                :     }
                                534                 :                : 
                                535                 :                :     /* Skip the rest if TID scans are disabled. */
   46 rhaas@postgresql.org      536         [ +  + ]:GNC      228833 :     if (!enabled)
  601 rhaas@postgresql.org      537                 :GBC          28 :         return false;
                                538                 :                : 
                                539                 :                :     /*
                                540                 :                :      * If there are range quals in the baserestrict list, generate a
                                541                 :                :      * TidRangePath.
                                542                 :                :      */
 1842 drowley@postgresql.o      543                 :CBC      228805 :     tidrangequals = TidRangeQualFromRestrictInfoList(rel->baserestrictinfo,
                                544                 :                :                                                      rel);
                                545                 :                : 
                                546         [ +  + ]:         228805 :     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                 :           1005 :         Relids      required_outer = rel->lateral_relids;
                                553                 :                : 
                                554                 :           1005 :         add_path(rel, (Path *) create_tidrangescan_path(root, rel,
                                555                 :                :                                                         tidrangequals,
                                556                 :                :                                                         required_outer,
                                557                 :                :                                                         0));
                                558                 :                : 
                                559                 :                :         /* If appropriate, consider parallel tid range scan. */
  108 drowley@postgresql.o      560   [ +  +  +  - ]:GNC        1005 :         if (rel->consider_parallel && required_outer == NULL)
                                561                 :                :         {
                                562                 :                :             int         parallel_workers;
                                563                 :                : 
                                564                 :            114 :             parallel_workers = compute_parallel_worker(rel, rel->pages, -1,
                                565                 :                :                                                        max_parallel_workers_per_gather);
                                566                 :                : 
                                567         [ +  + ]:            114 :             if (parallel_workers > 0)
                                568                 :             24 :                 add_partial_path(rel, (Path *) create_tidrangescan_path(root,
                                569                 :                :                                                                         rel,
                                570                 :                :                                                                         tidrangequals,
                                571                 :                :                                                                         required_outer,
                                572                 :                :                                                                         parallel_workers));
                                573                 :                :         }
                                574                 :                :     }
                                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                 :                :      */
 2632 tgl@sss.pgh.pa.us         581         [ +  + ]:CBC      228805 :     if (rel->has_eclass_joins)
                                582                 :                :     {
                                583                 :                :         List       *clauses;
                                584                 :                : 
                                585                 :                :         /* Generate clauses, skipping any that join to lateral_referencers */
                                586                 :          80430 :         clauses = generate_implied_equalities_for_column(root,
                                587                 :                :                                                          rel,
                                588                 :                :                                                          ec_member_matches_ctid,
                                589                 :                :                                                          NULL,
                                590                 :                :                                                          rel->lateral_referencers);
                                591                 :                : 
                                592                 :                :         /* Generate a path for each usable join clause */
                                593                 :          80430 :         BuildParameterizedTidPaths(root, rel, clauses);
                                594                 :                :     }
                                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                 :         228805 :     BuildParameterizedTidPaths(root, rel, rel->joininfo);
                                602                 :                : 
  601 rhaas@postgresql.org      603                 :         228805 :     return false;
                                604                 :                : }
        

Generated by: LCOV version 2.4-beta