LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - tid.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 83.4 % 145 121 1 2 17 4 3 78 4 36 17 81 2
Current Date: 2023-04-08 15:15:32 Functions: 83.3 % 18 15 3 15 3 15
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * tid.c
       4                 :  *    Functions for the built-in type tuple id
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, 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 LDELIM          '('
      41                 : #define RDELIM          ')'
      42                 : #define DELIM           ','
      43                 : #define NTIDARGS        2
      44                 : 
      45                 : static ItemPointer currtid_for_view(Relation viewrel, ItemPointer tid);
      46                 : 
      47 ECB             : /* ----------------------------------------------------------------
      48                 :  *      tidin
      49                 :  * ----------------------------------------------------------------
      50                 :  */
      51                 : Datum
      52 GIC        2432 : tidin(PG_FUNCTION_ARGS)
      53                 : {
      54            2432 :     char       *str = PG_GETARG_CSTRING(0);
      55 GNC        2432 :     Node       *escontext = fcinfo->context;
      56                 :     char       *p,
      57                 :                *coord[NTIDARGS];
      58                 :     int         i;
      59                 :     ItemPointer result;
      60                 :     BlockNumber blockNumber;
      61 ECB             :     OffsetNumber offsetNumber;
      62                 :     char       *badp;
      63                 :     unsigned long cvt;
      64                 : 
      65 CBC       12344 :     for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
      66            9912 :         if (*p == DELIM || (*p == LDELIM && i == 0))
      67 GIC        4858 :             coord[i++] = p + 1;
      68                 : 
      69            2432 :     if (i < NTIDARGS)
      70 GNC           6 :         ereturn(escontext, (Datum) 0,
      71 ECB             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      72                 :                  errmsg("invalid input syntax for type %s: \"%s\"",
      73                 :                         "tid", str)));
      74 EUB             : 
      75 GIC        2426 :     errno = 0;
      76            2426 :     cvt = strtoul(coord[0], &badp, 10);
      77            2426 :     if (errno || *badp != DELIM)
      78 UNC           0 :         ereturn(escontext, (Datum) 0,
      79                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      80                 :                  errmsg("invalid input syntax for type %s: \"%s\"",
      81                 :                         "tid", str)));
      82 GIC        2426 :     blockNumber = (BlockNumber) cvt;
      83                 : 
      84                 :     /*
      85                 :      * Cope with possibility that unsigned long is wider than BlockNumber, in
      86 ECB             :      * 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 GIC        2426 :     if (cvt != (unsigned long) blockNumber &&
      91               6 :         cvt != (unsigned long) ((int32) blockNumber))
      92 GNC           3 :         ereturn(escontext, (Datum) 0,
      93                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      94 ECB             :                  errmsg("invalid input syntax for type %s: \"%s\"",
      95                 :                         "tid", str)));
      96                 : #endif
      97                 : 
      98 GIC        2423 :     cvt = strtoul(coord[1], &badp, 10);
      99            2423 :     if (errno || *badp != RDELIM ||
     100                 :         cvt > USHRT_MAX)
     101 GNC           9 :         ereturn(escontext, (Datum) 0,
     102                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     103 ECB             :                  errmsg("invalid input syntax for type %s: \"%s\"",
     104                 :                         "tid", str)));
     105 CBC        2414 :     offsetNumber = (OffsetNumber) cvt;
     106                 : 
     107            2414 :     result = (ItemPointer) palloc(sizeof(ItemPointerData));
     108                 : 
     109 GIC        2414 :     ItemPointerSet(result, blockNumber, offsetNumber);
     110                 : 
     111            2414 :     PG_RETURN_ITEMPOINTER(result);
     112                 : }
     113                 : 
     114                 : /* ----------------------------------------------------------------
     115 ECB             :  *      tidout
     116                 :  * ----------------------------------------------------------------
     117                 :  */
     118                 : Datum
     119 GIC        2816 : tidout(PG_FUNCTION_ARGS)
     120                 : {
     121            2816 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     122 ECB             :     BlockNumber blockNumber;
     123                 :     OffsetNumber offsetNumber;
     124                 :     char        buf[32];
     125                 : 
     126 CBC        2816 :     blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
     127 GIC        2816 :     offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
     128 ECB             : 
     129                 :     /* Perhaps someday we should output this as a record. */
     130 GIC        2816 :     snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
     131                 : 
     132            2816 :     PG_RETURN_CSTRING(pstrdup(buf));
     133                 : }
     134                 : 
     135 EUB             : /*
     136                 :  *      tidrecv         - converts external binary format to tid
     137                 :  */
     138                 : Datum
     139 UIC           0 : tidrecv(PG_FUNCTION_ARGS)
     140                 : {
     141               0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     142 EUB             :     ItemPointer result;
     143                 :     BlockNumber blockNumber;
     144                 :     OffsetNumber offsetNumber;
     145                 : 
     146 UIC           0 :     blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
     147 UBC           0 :     offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
     148                 : 
     149               0 :     result = (ItemPointer) palloc(sizeof(ItemPointerData));
     150                 : 
     151 UIC           0 :     ItemPointerSet(result, blockNumber, offsetNumber);
     152                 : 
     153               0 :     PG_RETURN_ITEMPOINTER(result);
     154                 : }
     155                 : 
     156 EUB             : /*
     157                 :  *      tidsend         - converts tid to binary format
     158                 :  */
     159                 : Datum
     160 UIC           0 : tidsend(PG_FUNCTION_ARGS)
     161 EUB             : {
     162 UBC           0 :     ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
     163 EUB             :     StringInfoData buf;
     164                 : 
     165 UIC           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 ECB             :  *   PUBLIC ROUTINES                                                         *
     173                 :  *****************************************************************************/
     174                 : 
     175                 : Datum
     176 GIC     9206383 : tideq(PG_FUNCTION_ARGS)
     177 ECB             : {
     178 GIC     9206383 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     179         9206383 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     180                 : 
     181 CBC     9206383 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
     182                 : }
     183 ECB             : 
     184                 : Datum
     185 GIC         111 : tidne(PG_FUNCTION_ARGS)
     186 ECB             : {
     187 GIC         111 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     188             111 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     189                 : 
     190 CBC         111 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
     191                 : }
     192 ECB             : 
     193                 : Datum
     194 GIC       41550 : tidlt(PG_FUNCTION_ARGS)
     195 ECB             : {
     196 GIC       41550 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     197           41550 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     198                 : 
     199 CBC       41550 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
     200                 : }
     201 ECB             : 
     202                 : Datum
     203 GIC        1899 : tidle(PG_FUNCTION_ARGS)
     204 ECB             : {
     205 GIC        1899 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     206            1899 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     207                 : 
     208 CBC        1899 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
     209                 : }
     210 ECB             : 
     211                 : Datum
     212 GIC        2680 : tidgt(PG_FUNCTION_ARGS)
     213 ECB             : {
     214 GIC        2680 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     215            2680 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     216                 : 
     217 CBC        2680 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
     218                 : }
     219 ECB             : 
     220                 : Datum
     221 GIC        1866 : tidge(PG_FUNCTION_ARGS)
     222 ECB             : {
     223 GIC        1866 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     224            1866 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     225                 : 
     226 CBC        1866 :     PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
     227                 : }
     228 ECB             : 
     229                 : Datum
     230 GIC     1471428 : bttidcmp(PG_FUNCTION_ARGS)
     231 ECB             : {
     232 GIC     1471428 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     233         1471428 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     234                 : 
     235 CBC     1471428 :     PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
     236                 : }
     237 ECB             : 
     238                 : Datum
     239 GIC           3 : tidlarger(PG_FUNCTION_ARGS)
     240 ECB             : {
     241 GIC           3 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     242               3 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     243                 : 
     244 CBC           3 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
     245                 : }
     246 ECB             : 
     247                 : Datum
     248 GIC           3 : tidsmaller(PG_FUNCTION_ARGS)
     249 ECB             : {
     250 GIC           3 :     ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
     251               3 :     ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
     252                 : 
     253 CBC           3 :     PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
     254                 : }
     255 ECB             : 
     256                 : Datum
     257 GIC       63030 : hashtid(PG_FUNCTION_ARGS)
     258                 : {
     259           63030 :     ItemPointer key = PG_GETARG_ITEMPOINTER(0);
     260                 : 
     261                 :     /*
     262                 :      * While you'll probably have a lot of trouble with a compiler that
     263 ECB             :      * 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 GIC       63030 :     return hash_any((unsigned char *) key,
     268 EUB             :                     sizeof(BlockIdData) + sizeof(OffsetNumber));
     269                 : }
     270                 : 
     271                 : Datum
     272 UIC           0 : hashtidextended(PG_FUNCTION_ARGS)
     273                 : {
     274 UBC           0 :     ItemPointer key = PG_GETARG_ITEMPOINTER(0);
     275 UIC           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 ECB             :  *      Returns the latest version of a tuple pointing at "tid" for
     293                 :  *      relation "rel".
     294                 :  */
     295                 : static ItemPointer
     296 GIC          30 : currtid_internal(Relation rel, ItemPointer tid)
     297                 : {
     298                 :     ItemPointer result;
     299 ECB             :     AclResult   aclresult;
     300                 :     Snapshot    snapshot;
     301                 :     TableScanDesc scan;
     302                 : 
     303 CBC          30 :     result = (ItemPointer) palloc(sizeof(ItemPointerData));
     304 EUB             : 
     305 GBC          30 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     306                 :                                   ACL_SELECT);
     307 CBC          30 :     if (aclresult != ACLCHECK_OK)
     308 LBC           0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     309 UIC           0 :                        RelationGetRelationName(rel));
     310 ECB             : 
     311 CBC          30 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
     312 GIC          12 :         return currtid_for_view(rel, tid);
     313                 : 
     314              18 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     315 CBC           3 :         elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
     316                 :              get_namespace_name(RelationGetNamespace(rel)),
     317 ECB             :              RelationGetRelationName(rel));
     318                 : 
     319 CBC          15 :     ItemPointerCopy(tid, result);
     320 ECB             : 
     321 CBC          15 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
     322 GIC          15 :     scan = table_beginscan_tid(rel, snapshot);
     323 CBC          15 :     table_tuple_get_latest_tid(scan, result);
     324 GIC           9 :     table_endscan(scan);
     325               9 :     UnregisterSnapshot(snapshot);
     326                 : 
     327               9 :     return result;
     328                 : }
     329                 : 
     330                 : /*
     331                 :  *  Handle CTIDs of views.
     332 ECB             :  *      CTID should be defined in the view and it must
     333                 :  *      correspond to the CTID of a base relation.
     334                 :  */
     335                 : static ItemPointer
     336 GIC          12 : currtid_for_view(Relation viewrel, ItemPointer tid)
     337                 : {
     338 CBC          12 :     TupleDesc   att = RelationGetDescr(viewrel);
     339 ECB             :     RuleLock   *rulelock;
     340                 :     RewriteRule *rewrite;
     341                 :     int         i,
     342 GIC          12 :                 natts = att->natts,
     343 CBC          12 :                 tididx = -1;
     344                 : 
     345              15 :     for (i = 0; i < natts; i++)
     346                 :     {
     347              12 :         Form_pg_attribute attr = TupleDescAttr(att, i);
     348 ECB             : 
     349 CBC          12 :         if (strcmp(NameStr(attr->attname), "ctid") == 0)
     350 ECB             :         {
     351 GIC           9 :             if (attr->atttypid != TIDOID)
     352               3 :                 elog(ERROR, "ctid isn't of type TID");
     353 CBC           6 :             tididx = i;
     354               6 :             break;
     355 ECB             :         }
     356                 :     }
     357 GBC           9 :     if (tididx < 0)
     358 CBC           3 :         elog(ERROR, "currtid cannot handle views with no CTID");
     359 GIC           6 :     rulelock = viewrel->rd_rules;
     360 CBC           6 :     if (!rulelock)
     361 LBC           0 :         elog(ERROR, "the view has no rules");
     362 GIC           6 :     for (i = 0; i < rulelock->numLocks; i++)
     363                 :     {
     364               6 :         rewrite = rulelock->rules[i];
     365               6 :         if (rewrite->event == CMD_SELECT)
     366 ECB             :         {
     367 EUB             :             Query      *query;
     368 ECB             :             TargetEntry *tle;
     369                 : 
     370 CBC           6 :             if (list_length(rewrite->actions) != 1)
     371 UIC           0 :                 elog(ERROR, "only one select rule is allowed in views");
     372 CBC           6 :             query = (Query *) linitial(rewrite->actions);
     373 GIC           6 :             tle = get_tle_by_resno(query->targetList, tididx + 1);
     374               6 :             if (tle && tle->expr && IsA(tle->expr, Var))
     375 ECB             :             {
     376 CBC           6 :                 Var        *var = (Var *) tle->expr;
     377                 :                 RangeTblEntry *rte;
     378 ECB             : 
     379 CBC           6 :                 if (!IS_SPECIAL_VARNO(var->varno) &&
     380 GIC           6 :                     var->varattno == SelfItemPointerAttributeNumber)
     381                 :                 {
     382               6 :                     rte = rt_fetch(var->varno, query->rtable);
     383               6 :                     if (rte)
     384 ECB             :                     {
     385                 :                         ItemPointer result;
     386                 :                         Relation    rel;
     387                 : 
     388 GIC           6 :                         rel = table_open(rte->relid, AccessShareLock);
     389               6 :                         result = currtid_internal(rel, tid);
     390               3 :                         table_close(rel, AccessShareLock);
     391 GBC           3 :                         return result;
     392                 :                     }
     393                 :                 }
     394 EUB             :             }
     395 UIC           0 :             break;
     396                 :         }
     397                 :     }
     398               0 :     elog(ERROR, "currtid cannot handle this view");
     399                 :     return NULL;
     400                 : }
     401                 : 
     402                 : /*
     403                 :  * currtid_byrelname
     404 ECB             :  *      Get the latest tuple version of the tuple pointing at a CTID, for a
     405                 :  *      given relation name.
     406                 :  */
     407                 : Datum
     408 GIC          27 : currtid_byrelname(PG_FUNCTION_ARGS)
     409                 : {
     410              27 :     text       *relname = PG_GETARG_TEXT_PP(0);
     411              27 :     ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
     412 ECB             :     ItemPointer result;
     413                 :     RangeVar   *relrv;
     414                 :     Relation    rel;
     415                 : 
     416 CBC          27 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     417 GIC          27 :     rel = table_openrv(relrv, AccessShareLock);
     418 ECB             : 
     419                 :     /* grab the latest tuple version associated to this CTID */
     420 CBC          24 :     result = currtid_internal(rel, tid);
     421                 : 
     422 GIC           9 :     table_close(rel, AccessShareLock);
     423                 : 
     424               9 :     PG_RETURN_ITEMPOINTER(result);
     425                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a