LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tid.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 84.1 % 151 127
Test Date: 2026-04-07 14:16:30 Functions: 85.0 % 20 17
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         6151 : tidin(PG_FUNCTION_ARGS)
      52              : {
      53         6151 :     char       *str = PG_GETARG_CSTRING(0);
      54         6151 :     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        30432 :     for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
      65        24281 :         if (*p == DELIM || (*p == LDELIM && i == 0))
      66        12294 :             coord[i++] = p + 1;
      67              : 
      68         6151 :     if (i < NTIDARGS)
      69            8 :         ereturn(escontext, (Datum) 0,
      70              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      71              :                  errmsg("invalid input syntax for type %s: \"%s\"",
      72              :                         "tid", str)));
      73              : 
      74         6143 :     errno = 0;
      75         6143 :     cvt = strtoul(coord[0], &badp, 10);
      76         6143 :     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         6143 :     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         6143 :     if (cvt != (unsigned long) blockNumber &&
      90           12 :         cvt != (unsigned long) ((int32) blockNumber))
      91            4 :         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         6139 :     cvt = strtoul(coord[1], &badp, 10);
      98         6139 :     if (errno || *badp != RDELIM ||
      99              :         cvt > USHRT_MAX)
     100           12 :         ereturn(escontext, (Datum) 0,
     101              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     102              :                  errmsg("invalid input syntax for type %s: \"%s\"",
     103              :                         "tid", str)));
     104         6127 :     offsetNumber = (OffsetNumber) cvt;
     105              : 
     106         6127 :     result = (ItemPointer) palloc_object(ItemPointerData);
     107              : 
     108         6127 :     ItemPointerSet(result, blockNumber, offsetNumber);
     109              : 
     110         6127 :     PG_RETURN_ITEMPOINTER(result);
     111              : }
     112              : 
     113              : /* ----------------------------------------------------------------
     114              :  *      tidout
     115              :  * ----------------------------------------------------------------
     116              :  */
     117              : Datum
     118        24705 : tidout(PG_FUNCTION_ARGS)
     119              : {
     120        24705 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     121              :     BlockNumber blockNumber;
     122              :     OffsetNumber offsetNumber;
     123              :     char        buf[32];
     124              : 
     125        24705 :     blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
     126        24705 :     offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
     127              : 
     128              :     /* Perhaps someday we should output this as a record. */
     129        24705 :     snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
     130              : 
     131        24705 :     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     12254593 : tideq(PG_FUNCTION_ARGS)
     176              : {
     177     12254593 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     178     12254593 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     179              : 
     180     12254593 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
     181              : }
     182              : 
     183              : Datum
     184          143 : tidne(PG_FUNCTION_ARGS)
     185              : {
     186          143 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     187          143 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     188              : 
     189          143 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
     190              : }
     191              : 
     192              : Datum
     193         9727 : tidlt(PG_FUNCTION_ARGS)
     194              : {
     195         9727 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     196         9727 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     197              : 
     198         9727 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
     199              : }
     200              : 
     201              : Datum
     202         2532 : tidle(PG_FUNCTION_ARGS)
     203              : {
     204         2532 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     205         2532 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     206              : 
     207         2532 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
     208              : }
     209              : 
     210              : Datum
     211         3556 : tidgt(PG_FUNCTION_ARGS)
     212              : {
     213         3556 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     214         3556 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     215              : 
     216         3556 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
     217              : }
     218              : 
     219              : Datum
     220         2488 : tidge(PG_FUNCTION_ARGS)
     221              : {
     222         2488 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     223         2488 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     224              : 
     225         2488 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
     226              : }
     227              : 
     228              : Datum
     229      1962643 : bttidcmp(PG_FUNCTION_ARGS)
     230              : {
     231      1962643 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     232      1962643 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     233              : 
     234      1962643 :     PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
     235              : }
     236              : 
     237              : Datum
     238          800 : tidlarger(PG_FUNCTION_ARGS)
     239              : {
     240          800 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     241          800 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     242              : 
     243          800 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
     244              : }
     245              : 
     246              : Datum
     247          800 : tidsmaller(PG_FUNCTION_ARGS)
     248              : {
     249          800 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     250          800 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     251              : 
     252          800 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
     253              : }
     254              : 
     255              : Datum
     256        84048 : hashtid(PG_FUNCTION_ARGS)
     257              : {
     258        84048 :     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        84048 :     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              :  * Extract the block number from a TID
     284              :  *
     285              :  * Returns int8 because BlockNumber is uint32, which exceeds the range of int4.
     286              :  */
     287              : Datum
     288           65 : tid_block(PG_FUNCTION_ARGS)
     289              : {
     290           65 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
     291              : 
     292              :     /* need to use NoCheck, as tidin allows InvalidBlockNumber */
     293           65 :     PG_RETURN_INT64((int64) ItemPointerGetBlockNumberNoCheck(tid));
     294              : }
     295              : 
     296              : /*
     297              :  * Extract the offset number from a TID
     298              :  *
     299              :  * Returns int4 because OffsetNumber is uint16, which exceeds the range of
     300              :  * int2.
     301              :  */
     302              : Datum
     303           60 : tid_offset(PG_FUNCTION_ARGS)
     304              : {
     305           60 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
     306              : 
     307              :     /* need to use NoCheck, as tidin allows InvalidOffsetNumber */
     308           60 :     PG_RETURN_INT32((int32) ItemPointerGetOffsetNumberNoCheck(tid));
     309              : }
     310              : 
     311              : 
     312              : /*
     313              :  *  Functions to get latest tid of a specified tuple.
     314              :  *
     315              :  *  Maybe these implementations should be moved to another place
     316              :  */
     317              : 
     318              : /*
     319              :  * Utility wrapper for current CTID functions.
     320              :  *      Returns the latest version of a tuple pointing at "tid" for
     321              :  *      relation "rel".
     322              :  */
     323              : static ItemPointer
     324           40 : currtid_internal(Relation rel, const ItemPointerData *tid)
     325              : {
     326              :     ItemPointer result;
     327              :     AclResult   aclresult;
     328              :     Snapshot    snapshot;
     329              :     TableScanDesc scan;
     330              : 
     331           40 :     result = (ItemPointer) palloc_object(ItemPointerData);
     332              : 
     333           40 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     334              :                                   ACL_SELECT);
     335           40 :     if (aclresult != ACLCHECK_OK)
     336            0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     337            0 :                        RelationGetRelationName(rel));
     338              : 
     339           40 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
     340           16 :         return currtid_for_view(rel, tid);
     341              : 
     342           24 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     343            4 :         ereport(ERROR,
     344              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     345              :                 errmsg("cannot look at latest visible tid for relation \"%s.%s\"",
     346              :                        get_namespace_name(RelationGetNamespace(rel)),
     347              :                        RelationGetRelationName(rel)));
     348              : 
     349           20 :     ItemPointerCopy(tid, result);
     350              : 
     351           20 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
     352           20 :     scan = table_beginscan_tid(rel, snapshot);
     353           20 :     table_tuple_get_latest_tid(scan, result);
     354           12 :     table_endscan(scan);
     355           12 :     UnregisterSnapshot(snapshot);
     356              : 
     357           12 :     return result;
     358              : }
     359              : 
     360              : /*
     361              :  *  Handle CTIDs of views.
     362              :  *      CTID should be defined in the view and it must
     363              :  *      correspond to the CTID of a base relation.
     364              :  */
     365              : static ItemPointer
     366           16 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
     367              : {
     368           16 :     TupleDesc   att = RelationGetDescr(viewrel);
     369              :     RuleLock   *rulelock;
     370              :     RewriteRule *rewrite;
     371              :     int         i,
     372           16 :                 natts = att->natts,
     373           16 :                 tididx = -1;
     374              : 
     375           20 :     for (i = 0; i < natts; i++)
     376              :     {
     377           16 :         Form_pg_attribute attr = TupleDescAttr(att, i);
     378              : 
     379           16 :         if (strcmp(NameStr(attr->attname), "ctid") == 0)
     380              :         {
     381           12 :             if (attr->atttypid != TIDOID)
     382            4 :                 ereport(ERROR,
     383              :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     384              :                         errmsg("ctid isn't of type TID"));
     385            8 :             tididx = i;
     386            8 :             break;
     387              :         }
     388              :     }
     389           12 :     if (tididx < 0)
     390            4 :         ereport(ERROR,
     391              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     392              :                 errmsg("currtid cannot handle views with no CTID"));
     393            8 :     rulelock = viewrel->rd_rules;
     394            8 :     if (!rulelock)
     395            0 :         ereport(ERROR,
     396              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     397              :                 errmsg("the view has no rules"));
     398            8 :     for (i = 0; i < rulelock->numLocks; i++)
     399              :     {
     400            8 :         rewrite = rulelock->rules[i];
     401            8 :         if (rewrite->event == CMD_SELECT)
     402              :         {
     403              :             Query      *query;
     404              :             TargetEntry *tle;
     405              : 
     406            8 :             if (list_length(rewrite->actions) != 1)
     407            0 :                 ereport(ERROR,
     408              :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     409              :                         errmsg("only one select rule is allowed in views"));
     410            8 :             query = (Query *) linitial(rewrite->actions);
     411            8 :             tle = get_tle_by_resno(query->targetList, tididx + 1);
     412            8 :             if (tle && tle->expr && IsA(tle->expr, Var))
     413              :             {
     414            8 :                 Var        *var = (Var *) tle->expr;
     415              :                 RangeTblEntry *rte;
     416              : 
     417            8 :                 if (!IS_SPECIAL_VARNO(var->varno) &&
     418            8 :                     var->varattno == SelfItemPointerAttributeNumber)
     419              :                 {
     420            8 :                     rte = rt_fetch(var->varno, query->rtable);
     421            8 :                     if (rte)
     422              :                     {
     423              :                         ItemPointer result;
     424              :                         Relation    rel;
     425              : 
     426            8 :                         rel = table_open(rte->relid, AccessShareLock);
     427            8 :                         result = currtid_internal(rel, tid);
     428            4 :                         table_close(rel, AccessShareLock);
     429            4 :                         return result;
     430              :                     }
     431              :                 }
     432              :             }
     433            0 :             break;
     434              :         }
     435              :     }
     436            0 :     elog(ERROR, "currtid cannot handle this view");
     437              :     return NULL;
     438              : }
     439              : 
     440              : /*
     441              :  * currtid_byrelname
     442              :  *      Get the latest tuple version of the tuple pointing at a CTID, for a
     443              :  *      given relation name.
     444              :  */
     445              : Datum
     446           36 : currtid_byrelname(PG_FUNCTION_ARGS)
     447              : {
     448           36 :     text       *relname = PG_GETARG_TEXT_PP(0);
     449           36 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
     450              :     ItemPointer result;
     451              :     RangeVar   *relrv;
     452              :     Relation    rel;
     453              : 
     454           36 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     455           36 :     rel = table_openrv(relrv, AccessShareLock);
     456              : 
     457              :     /* grab the latest tuple version associated to this CTID */
     458           32 :     result = currtid_internal(rel, tid);
     459              : 
     460           12 :     table_close(rel, AccessShareLock);
     461              : 
     462           12 :     PG_RETURN_ITEMPOINTER(result);
     463              : }
        

Generated by: LCOV version 2.0-1