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

Generated by: LCOV version 1.13