LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tid.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 83.4 % 145 121
Test Date: 2026-02-27 09:14:54 Functions: 83.3 % 18 15
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         5436 : tidin(PG_FUNCTION_ARGS)
      52              : {
      53         5436 :     char       *str = PG_GETARG_CSTRING(0);
      54         5436 :     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        26107 :     for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
      65        20671 :         if (*p == DELIM || (*p == LDELIM && i == 0))
      66        10866 :             coord[i++] = p + 1;
      67              : 
      68         5436 :     if (i < NTIDARGS)
      69            6 :         ereturn(escontext, (Datum) 0,
      70              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      71              :                  errmsg("invalid input syntax for type %s: \"%s\"",
      72              :                         "tid", str)));
      73              : 
      74         5430 :     errno = 0;
      75         5430 :     cvt = strtoul(coord[0], &badp, 10);
      76         5430 :     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         5430 :     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         5430 :     if (cvt != (unsigned long) blockNumber &&
      90            6 :         cvt != (unsigned long) ((int32) blockNumber))
      91            3 :         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         5427 :     cvt = strtoul(coord[1], &badp, 10);
      98         5427 :     if (errno || *badp != RDELIM ||
      99              :         cvt > USHRT_MAX)
     100            9 :         ereturn(escontext, (Datum) 0,
     101              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     102              :                  errmsg("invalid input syntax for type %s: \"%s\"",
     103              :                         "tid", str)));
     104         5418 :     offsetNumber = (OffsetNumber) cvt;
     105              : 
     106         5418 :     result = (ItemPointer) palloc_object(ItemPointerData);
     107              : 
     108         5418 :     ItemPointerSet(result, blockNumber, offsetNumber);
     109              : 
     110         5418 :     PG_RETURN_ITEMPOINTER(result);
     111              : }
     112              : 
     113              : /* ----------------------------------------------------------------
     114              :  *      tidout
     115              :  * ----------------------------------------------------------------
     116              :  */
     117              : Datum
     118        24207 : tidout(PG_FUNCTION_ARGS)
     119              : {
     120        24207 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     121              :     BlockNumber blockNumber;
     122              :     OffsetNumber offsetNumber;
     123              :     char        buf[32];
     124              : 
     125        24207 :     blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
     126        24207 :     offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
     127              : 
     128              :     /* Perhaps someday we should output this as a record. */
     129        24207 :     snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
     130              : 
     131        24207 :     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      9191052 : tideq(PG_FUNCTION_ARGS)
     176              : {
     177      9191052 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     178      9191052 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     179              : 
     180      9191052 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
     181              : }
     182              : 
     183              : Datum
     184          114 : tidne(PG_FUNCTION_ARGS)
     185              : {
     186          114 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     187          114 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     188              : 
     189          114 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
     190              : }
     191              : 
     192              : Datum
     193         7304 : tidlt(PG_FUNCTION_ARGS)
     194              : {
     195         7304 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     196         7304 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     197              : 
     198         7304 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
     199              : }
     200              : 
     201              : Datum
     202         1899 : tidle(PG_FUNCTION_ARGS)
     203              : {
     204         1899 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     205         1899 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     206              : 
     207         1899 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
     208              : }
     209              : 
     210              : Datum
     211         2678 : tidgt(PG_FUNCTION_ARGS)
     212              : {
     213         2678 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     214         2678 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     215              : 
     216         2678 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
     217              : }
     218              : 
     219              : Datum
     220         1866 : tidge(PG_FUNCTION_ARGS)
     221              : {
     222         1866 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     223         1866 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     224              : 
     225         1866 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
     226              : }
     227              : 
     228              : Datum
     229      1472889 : bttidcmp(PG_FUNCTION_ARGS)
     230              : {
     231      1472889 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     232      1472889 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     233              : 
     234      1472889 :     PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
     235              : }
     236              : 
     237              : Datum
     238          600 : tidlarger(PG_FUNCTION_ARGS)
     239              : {
     240          600 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     241          600 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     242              : 
     243          600 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
     244              : }
     245              : 
     246              : Datum
     247          600 : tidsmaller(PG_FUNCTION_ARGS)
     248              : {
     249          600 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     250          600 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     251              : 
     252          600 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
     253              : }
     254              : 
     255              : Datum
     256        63040 : hashtid(PG_FUNCTION_ARGS)
     257              : {
     258        63040 :     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        63040 :     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           30 : currtid_internal(Relation rel, const ItemPointerData *tid)
     296              : {
     297              :     ItemPointer result;
     298              :     AclResult   aclresult;
     299              :     Snapshot    snapshot;
     300              :     TableScanDesc scan;
     301              : 
     302           30 :     result = (ItemPointer) palloc_object(ItemPointerData);
     303              : 
     304           30 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     305              :                                   ACL_SELECT);
     306           30 :     if (aclresult != ACLCHECK_OK)
     307            0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     308            0 :                        RelationGetRelationName(rel));
     309              : 
     310           30 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
     311           12 :         return currtid_for_view(rel, tid);
     312              : 
     313           18 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     314            3 :         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           15 :     ItemPointerCopy(tid, result);
     321              : 
     322           15 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
     323           15 :     scan = table_beginscan_tid(rel, snapshot);
     324           15 :     table_tuple_get_latest_tid(scan, result);
     325            9 :     table_endscan(scan);
     326            9 :     UnregisterSnapshot(snapshot);
     327              : 
     328            9 :     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           12 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
     338              : {
     339           12 :     TupleDesc   att = RelationGetDescr(viewrel);
     340              :     RuleLock   *rulelock;
     341              :     RewriteRule *rewrite;
     342              :     int         i,
     343           12 :                 natts = att->natts,
     344           12 :                 tididx = -1;
     345              : 
     346           15 :     for (i = 0; i < natts; i++)
     347              :     {
     348           12 :         Form_pg_attribute attr = TupleDescAttr(att, i);
     349              : 
     350           12 :         if (strcmp(NameStr(attr->attname), "ctid") == 0)
     351              :         {
     352            9 :             if (attr->atttypid != TIDOID)
     353            3 :                 ereport(ERROR,
     354              :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     355              :                         errmsg("ctid isn't of type TID"));
     356            6 :             tididx = i;
     357            6 :             break;
     358              :         }
     359              :     }
     360            9 :     if (tididx < 0)
     361            3 :         ereport(ERROR,
     362              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     363              :                 errmsg("currtid cannot handle views with no CTID"));
     364            6 :     rulelock = viewrel->rd_rules;
     365            6 :     if (!rulelock)
     366            0 :         ereport(ERROR,
     367              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     368              :                 errmsg("the view has no rules"));
     369            6 :     for (i = 0; i < rulelock->numLocks; i++)
     370              :     {
     371            6 :         rewrite = rulelock->rules[i];
     372            6 :         if (rewrite->event == CMD_SELECT)
     373              :         {
     374              :             Query      *query;
     375              :             TargetEntry *tle;
     376              : 
     377            6 :             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            6 :             query = (Query *) linitial(rewrite->actions);
     382            6 :             tle = get_tle_by_resno(query->targetList, tididx + 1);
     383            6 :             if (tle && tle->expr && IsA(tle->expr, Var))
     384              :             {
     385            6 :                 Var        *var = (Var *) tle->expr;
     386              :                 RangeTblEntry *rte;
     387              : 
     388            6 :                 if (!IS_SPECIAL_VARNO(var->varno) &&
     389            6 :                     var->varattno == SelfItemPointerAttributeNumber)
     390              :                 {
     391            6 :                     rte = rt_fetch(var->varno, query->rtable);
     392            6 :                     if (rte)
     393              :                     {
     394              :                         ItemPointer result;
     395              :                         Relation    rel;
     396              : 
     397            6 :                         rel = table_open(rte->relid, AccessShareLock);
     398            6 :                         result = currtid_internal(rel, tid);
     399            3 :                         table_close(rel, AccessShareLock);
     400            3 :                         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           27 : currtid_byrelname(PG_FUNCTION_ARGS)
     418              : {
     419           27 :     text       *relname = PG_GETARG_TEXT_PP(0);
     420           27 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
     421              :     ItemPointer result;
     422              :     RangeVar   *relrv;
     423              :     Relation    rel;
     424              : 
     425           27 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     426           27 :     rel = table_openrv(relrv, AccessShareLock);
     427              : 
     428              :     /* grab the latest tuple version associated to this CTID */
     429           24 :     result = currtid_internal(rel, tid);
     430              : 
     431            9 :     table_close(rel, AccessShareLock);
     432              : 
     433            9 :     PG_RETURN_ITEMPOINTER(result);
     434              : }
        

Generated by: LCOV version 2.0-1