LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tid.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 121 145 83.4 %
Date: 2026-02-01 21:16:50 Functions: 15 18 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tid.c
       4             :  *    Functions for the built-in type tuple id
       5             :  *
       6             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/tid.c
      12             :  *
      13             :  * NOTES
      14             :  *    input routine largely stolen from boxin().
      15             :  *
      16             :  *-------------------------------------------------------------------------
      17             :  */
      18             : #include "postgres.h"
      19             : 
      20             : #include <limits.h>
      21             : 
      22             : #include "access/sysattr.h"
      23             : #include "access/table.h"
      24             : #include "access/tableam.h"
      25             : #include "catalog/namespace.h"
      26             : #include "catalog/pg_type.h"
      27             : #include "common/hashfn.h"
      28             : #include "libpq/pqformat.h"
      29             : #include "miscadmin.h"
      30             : #include "parser/parsetree.h"
      31             : #include "utils/acl.h"
      32             : #include "utils/fmgrprotos.h"
      33             : #include "utils/lsyscache.h"
      34             : #include "utils/rel.h"
      35             : #include "utils/snapmgr.h"
      36             : #include "utils/varlena.h"
      37             : 
      38             : 
      39             : #define LDELIM          '('
      40             : #define RDELIM          ')'
      41             : #define DELIM           ','
      42             : #define NTIDARGS        2
      43             : 
      44             : static ItemPointer currtid_for_view(Relation viewrel, const ItemPointerData *tid);
      45             : 
      46             : /* ----------------------------------------------------------------
      47             :  *      tidin
      48             :  * ----------------------------------------------------------------
      49             :  */
      50             : Datum
      51       11072 : tidin(PG_FUNCTION_ARGS)
      52             : {
      53       11072 :     char       *str = PG_GETARG_CSTRING(0);
      54       11072 :     Node       *escontext = fcinfo->context;
      55             :     char       *p,
      56             :                *coord[NTIDARGS];
      57             :     int         i;
      58             :     ItemPointer result;
      59             :     BlockNumber blockNumber;
      60             :     OffsetNumber offsetNumber;
      61             :     char       *badp;
      62             :     unsigned long cvt;
      63             : 
      64       53592 :     for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
      65       42520 :         if (*p == DELIM || (*p == LDELIM && i == 0))
      66       22132 :             coord[i++] = p + 1;
      67             : 
      68       11072 :     if (i < NTIDARGS)
      69          12 :         ereturn(escontext, (Datum) 0,
      70             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      71             :                  errmsg("invalid input syntax for type %s: \"%s\"",
      72             :                         "tid", str)));
      73             : 
      74       11060 :     errno = 0;
      75       11060 :     cvt = strtoul(coord[0], &badp, 10);
      76       11060 :     if (errno || *badp != DELIM)
      77           0 :         ereturn(escontext, (Datum) 0,
      78             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      79             :                  errmsg("invalid input syntax for type %s: \"%s\"",
      80             :                         "tid", str)));
      81       11060 :     blockNumber = (BlockNumber) cvt;
      82             : 
      83             :     /*
      84             :      * Cope with possibility that unsigned long is wider than BlockNumber, in
      85             :      * which case strtoul will not raise an error for some values that are out
      86             :      * of the range of BlockNumber.  (See similar code in uint32in_subr().)
      87             :      */
      88             : #if SIZEOF_LONG > 4
      89       11060 :     if (cvt != (unsigned long) blockNumber &&
      90          12 :         cvt != (unsigned long) ((int32) blockNumber))
      91           6 :         ereturn(escontext, (Datum) 0,
      92             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      93             :                  errmsg("invalid input syntax for type %s: \"%s\"",
      94             :                         "tid", str)));
      95             : #endif
      96             : 
      97       11054 :     cvt = strtoul(coord[1], &badp, 10);
      98       11054 :     if (errno || *badp != RDELIM ||
      99             :         cvt > USHRT_MAX)
     100          18 :         ereturn(escontext, (Datum) 0,
     101             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     102             :                  errmsg("invalid input syntax for type %s: \"%s\"",
     103             :                         "tid", str)));
     104       11036 :     offsetNumber = (OffsetNumber) cvt;
     105             : 
     106       11036 :     result = (ItemPointer) palloc_object(ItemPointerData);
     107             : 
     108       11036 :     ItemPointerSet(result, blockNumber, offsetNumber);
     109             : 
     110       11036 :     PG_RETURN_ITEMPOINTER(result);
     111             : }
     112             : 
     113             : /* ----------------------------------------------------------------
     114             :  *      tidout
     115             :  * ----------------------------------------------------------------
     116             :  */
     117             : Datum
     118       48214 : tidout(PG_FUNCTION_ARGS)
     119             : {
     120       48214 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     121             :     BlockNumber blockNumber;
     122             :     OffsetNumber offsetNumber;
     123             :     char        buf[32];
     124             : 
     125       48214 :     blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
     126       48214 :     offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
     127             : 
     128             :     /* Perhaps someday we should output this as a record. */
     129       48214 :     snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
     130             : 
     131       48214 :     PG_RETURN_CSTRING(pstrdup(buf));
     132             : }
     133             : 
     134             : /*
     135             :  *      tidrecv         - converts external binary format to tid
     136             :  */
     137             : Datum
     138           0 : tidrecv(PG_FUNCTION_ARGS)
     139             : {
     140           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     141             :     ItemPointer result;
     142             :     BlockNumber blockNumber;
     143             :     OffsetNumber offsetNumber;
     144             : 
     145           0 :     blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
     146           0 :     offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
     147             : 
     148           0 :     result = (ItemPointer) palloc_object(ItemPointerData);
     149             : 
     150           0 :     ItemPointerSet(result, blockNumber, offsetNumber);
     151             : 
     152           0 :     PG_RETURN_ITEMPOINTER(result);
     153             : }
     154             : 
     155             : /*
     156             :  *      tidsend         - converts tid to binary format
     157             :  */
     158             : Datum
     159           0 : tidsend(PG_FUNCTION_ARGS)
     160             : {
     161           0 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     162             :     StringInfoData buf;
     163             : 
     164           0 :     pq_begintypsend(&buf);
     165           0 :     pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
     166           0 :     pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
     167           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     168             : }
     169             : 
     170             : /*****************************************************************************
     171             :  *   PUBLIC ROUTINES                                                         *
     172             :  *****************************************************************************/
     173             : 
     174             : Datum
     175    18382100 : tideq(PG_FUNCTION_ARGS)
     176             : {
     177    18382100 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     178    18382100 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     179             : 
     180    18382100 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
     181             : }
     182             : 
     183             : Datum
     184         228 : tidne(PG_FUNCTION_ARGS)
     185             : {
     186         228 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     187         228 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     188             : 
     189         228 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
     190             : }
     191             : 
     192             : Datum
     193       14594 : tidlt(PG_FUNCTION_ARGS)
     194             : {
     195       14594 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     196       14594 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     197             : 
     198       14594 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
     199             : }
     200             : 
     201             : Datum
     202        3798 : tidle(PG_FUNCTION_ARGS)
     203             : {
     204        3798 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     205        3798 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     206             : 
     207        3798 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
     208             : }
     209             : 
     210             : Datum
     211        5356 : tidgt(PG_FUNCTION_ARGS)
     212             : {
     213        5356 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     214        5356 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     215             : 
     216        5356 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
     217             : }
     218             : 
     219             : Datum
     220        3732 : tidge(PG_FUNCTION_ARGS)
     221             : {
     222        3732 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     223        3732 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     224             : 
     225        3732 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
     226             : }
     227             : 
     228             : Datum
     229     2944344 : bttidcmp(PG_FUNCTION_ARGS)
     230             : {
     231     2944344 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     232     2944344 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     233             : 
     234     2944344 :     PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
     235             : }
     236             : 
     237             : Datum
     238        1200 : tidlarger(PG_FUNCTION_ARGS)
     239             : {
     240        1200 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     241        1200 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     242             : 
     243        1200 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
     244             : }
     245             : 
     246             : Datum
     247        1200 : tidsmaller(PG_FUNCTION_ARGS)
     248             : {
     249        1200 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     250        1200 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     251             : 
     252        1200 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
     253             : }
     254             : 
     255             : Datum
     256      126080 : hashtid(PG_FUNCTION_ARGS)
     257             : {
     258      126080 :     ItemPointer key = PG_GETARG_ITEMPOINTER(0);
     259             : 
     260             :     /*
     261             :      * While you'll probably have a lot of trouble with a compiler that
     262             :      * insists on appending pad space to struct ItemPointerData, we can at
     263             :      * least make this code work, by not using sizeof(ItemPointerData).
     264             :      * Instead rely on knowing the sizes of the component fields.
     265             :      */
     266      126080 :     return hash_any((unsigned char *) key,
     267             :                     sizeof(BlockIdData) + sizeof(OffsetNumber));
     268             : }
     269             : 
     270             : Datum
     271           0 : hashtidextended(PG_FUNCTION_ARGS)
     272             : {
     273           0 :     ItemPointer key = PG_GETARG_ITEMPOINTER(0);
     274           0 :     uint64      seed = PG_GETARG_INT64(1);
     275             : 
     276             :     /* As above */
     277           0 :     return hash_any_extended((unsigned char *) key,
     278             :                              sizeof(BlockIdData) + sizeof(OffsetNumber),
     279             :                              seed);
     280             : }
     281             : 
     282             : 
     283             : /*
     284             :  *  Functions to get latest tid of a specified tuple.
     285             :  *
     286             :  *  Maybe these implementations should be moved to another place
     287             :  */
     288             : 
     289             : /*
     290             :  * Utility wrapper for current CTID functions.
     291             :  *      Returns the latest version of a tuple pointing at "tid" for
     292             :  *      relation "rel".
     293             :  */
     294             : static ItemPointer
     295          60 : currtid_internal(Relation rel, const ItemPointerData *tid)
     296             : {
     297             :     ItemPointer result;
     298             :     AclResult   aclresult;
     299             :     Snapshot    snapshot;
     300             :     TableScanDesc scan;
     301             : 
     302          60 :     result = (ItemPointer) palloc_object(ItemPointerData);
     303             : 
     304          60 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     305             :                                   ACL_SELECT);
     306          60 :     if (aclresult != ACLCHECK_OK)
     307           0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     308           0 :                        RelationGetRelationName(rel));
     309             : 
     310          60 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
     311          24 :         return currtid_for_view(rel, tid);
     312             : 
     313          36 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     314           6 :         ereport(ERROR,
     315             :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     316             :                 errmsg("cannot look at latest visible tid for relation \"%s.%s\"",
     317             :                        get_namespace_name(RelationGetNamespace(rel)),
     318             :                        RelationGetRelationName(rel)));
     319             : 
     320          30 :     ItemPointerCopy(tid, result);
     321             : 
     322          30 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
     323          30 :     scan = table_beginscan_tid(rel, snapshot);
     324          30 :     table_tuple_get_latest_tid(scan, result);
     325          18 :     table_endscan(scan);
     326          18 :     UnregisterSnapshot(snapshot);
     327             : 
     328          18 :     return result;
     329             : }
     330             : 
     331             : /*
     332             :  *  Handle CTIDs of views.
     333             :  *      CTID should be defined in the view and it must
     334             :  *      correspond to the CTID of a base relation.
     335             :  */
     336             : static ItemPointer
     337          24 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
     338             : {
     339          24 :     TupleDesc   att = RelationGetDescr(viewrel);
     340             :     RuleLock   *rulelock;
     341             :     RewriteRule *rewrite;
     342             :     int         i,
     343          24 :                 natts = att->natts,
     344          24 :                 tididx = -1;
     345             : 
     346          30 :     for (i = 0; i < natts; i++)
     347             :     {
     348          24 :         Form_pg_attribute attr = TupleDescAttr(att, i);
     349             : 
     350          24 :         if (strcmp(NameStr(attr->attname), "ctid") == 0)
     351             :         {
     352          18 :             if (attr->atttypid != TIDOID)
     353           6 :                 ereport(ERROR,
     354             :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     355             :                         errmsg("ctid isn't of type TID"));
     356          12 :             tididx = i;
     357          12 :             break;
     358             :         }
     359             :     }
     360          18 :     if (tididx < 0)
     361           6 :         ereport(ERROR,
     362             :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     363             :                 errmsg("currtid cannot handle views with no CTID"));
     364          12 :     rulelock = viewrel->rd_rules;
     365          12 :     if (!rulelock)
     366           0 :         ereport(ERROR,
     367             :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     368             :                 errmsg("the view has no rules"));
     369          12 :     for (i = 0; i < rulelock->numLocks; i++)
     370             :     {
     371          12 :         rewrite = rulelock->rules[i];
     372          12 :         if (rewrite->event == CMD_SELECT)
     373             :         {
     374             :             Query      *query;
     375             :             TargetEntry *tle;
     376             : 
     377          12 :             if (list_length(rewrite->actions) != 1)
     378           0 :                 ereport(ERROR,
     379             :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     380             :                         errmsg("only one select rule is allowed in views"));
     381          12 :             query = (Query *) linitial(rewrite->actions);
     382          12 :             tle = get_tle_by_resno(query->targetList, tididx + 1);
     383          12 :             if (tle && tle->expr && IsA(tle->expr, Var))
     384             :             {
     385          12 :                 Var        *var = (Var *) tle->expr;
     386             :                 RangeTblEntry *rte;
     387             : 
     388          12 :                 if (!IS_SPECIAL_VARNO(var->varno) &&
     389          12 :                     var->varattno == SelfItemPointerAttributeNumber)
     390             :                 {
     391          12 :                     rte = rt_fetch(var->varno, query->rtable);
     392          12 :                     if (rte)
     393             :                     {
     394             :                         ItemPointer result;
     395             :                         Relation    rel;
     396             : 
     397          12 :                         rel = table_open(rte->relid, AccessShareLock);
     398          12 :                         result = currtid_internal(rel, tid);
     399           6 :                         table_close(rel, AccessShareLock);
     400           6 :                         return result;
     401             :                     }
     402             :                 }
     403             :             }
     404           0 :             break;
     405             :         }
     406             :     }
     407           0 :     elog(ERROR, "currtid cannot handle this view");
     408             :     return NULL;
     409             : }
     410             : 
     411             : /*
     412             :  * currtid_byrelname
     413             :  *      Get the latest tuple version of the tuple pointing at a CTID, for a
     414             :  *      given relation name.
     415             :  */
     416             : Datum
     417          54 : currtid_byrelname(PG_FUNCTION_ARGS)
     418             : {
     419          54 :     text       *relname = PG_GETARG_TEXT_PP(0);
     420          54 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
     421             :     ItemPointer result;
     422             :     RangeVar   *relrv;
     423             :     Relation    rel;
     424             : 
     425          54 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     426          54 :     rel = table_openrv(relrv, AccessShareLock);
     427             : 
     428             :     /* grab the latest tuple version associated to this CTID */
     429          48 :     result = currtid_internal(rel, tid);
     430             : 
     431          18 :     table_close(rel, AccessShareLock);
     432             : 
     433          18 :     PG_RETURN_ITEMPOINTER(result);
     434             : }

Generated by: LCOV version 1.16