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

Generated by: LCOV version 1.14