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

Generated by: LCOV version 1.14