PostgreSQL Source Code  git master
tid.c
Go to the documentation of this file.
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 
46 
47 /* ----------------------------------------------------------------
48  * tidin
49  * ----------------------------------------------------------------
50  */
51 Datum
53 {
54  char *str = PG_GETARG_CSTRING(0);
55  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  for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
66  if (*p == DELIM || (*p == LDELIM && i == 0))
67  coord[i++] = p + 1;
68 
69  if (i < NTIDARGS)
70  ereturn(escontext, (Datum) 0,
71  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
72  errmsg("invalid input syntax for type %s: \"%s\"",
73  "tid", str)));
74 
75  errno = 0;
76  cvt = strtoul(coord[0], &badp, 10);
77  if (errno || *badp != DELIM)
78  ereturn(escontext, (Datum) 0,
79  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80  errmsg("invalid input syntax for type %s: \"%s\"",
81  "tid", str)));
82  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  if (cvt != (unsigned long) blockNumber &&
91  cvt != (unsigned long) ((int32) blockNumber))
92  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  cvt = strtoul(coord[1], &badp, 10);
99  if (errno || *badp != RDELIM ||
100  cvt > USHRT_MAX)
101  ereturn(escontext, (Datum) 0,
102  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
103  errmsg("invalid input syntax for type %s: \"%s\"",
104  "tid", str)));
105  offsetNumber = (OffsetNumber) cvt;
106 
107  result = (ItemPointer) palloc(sizeof(ItemPointerData));
108 
109  ItemPointerSet(result, blockNumber, offsetNumber);
110 
111  PG_RETURN_ITEMPOINTER(result);
112 }
113 
114 /* ----------------------------------------------------------------
115  * tidout
116  * ----------------------------------------------------------------
117  */
118 Datum
120 {
121  ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
122  BlockNumber blockNumber;
123  OffsetNumber offsetNumber;
124  char buf[32];
125 
126  blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
127  offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
128 
129  /* Perhaps someday we should output this as a record. */
130  snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
131 
133 }
134 
135 /*
136  * tidrecv - converts external binary format to tid
137  */
138 Datum
140 {
142  ItemPointer result;
143  BlockNumber blockNumber;
144  OffsetNumber offsetNumber;
145 
146  blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
147  offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
148 
149  result = (ItemPointer) palloc(sizeof(ItemPointerData));
150 
151  ItemPointerSet(result, blockNumber, offsetNumber);
152 
153  PG_RETURN_ITEMPOINTER(result);
154 }
155 
156 /*
157  * tidsend - converts tid to binary format
158  */
159 Datum
161 {
162  ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
164 
169 }
170 
171 /*****************************************************************************
172  * PUBLIC ROUTINES *
173  *****************************************************************************/
174 
175 Datum
177 {
180 
181  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
182 }
183 
184 Datum
186 {
189 
190  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
191 }
192 
193 Datum
195 {
198 
199  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
200 }
201 
202 Datum
204 {
207 
208  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
209 }
210 
211 Datum
213 {
216 
217  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
218 }
219 
220 Datum
222 {
225 
226  PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
227 }
228 
229 Datum
231 {
234 
235  PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
236 }
237 
238 Datum
240 {
243 
244  PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
245 }
246 
247 Datum
249 {
252 
253  PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
254 }
255 
256 Datum
258 {
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  return hash_any((unsigned char *) key,
268  sizeof(BlockIdData) + sizeof(OffsetNumber));
269 }
270 
271 Datum
273 {
275  uint64 seed = PG_GETARG_INT64(1);
276 
277  /* As above */
278  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
297 {
298  ItemPointer result;
299  AclResult aclresult;
300  Snapshot snapshot;
301  TableScanDesc scan;
302 
303  result = (ItemPointer) palloc(sizeof(ItemPointerData));
304 
305  aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
306  ACL_SELECT);
307  if (aclresult != ACLCHECK_OK)
308  aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
310 
311  if (rel->rd_rel->relkind == RELKIND_VIEW)
312  return currtid_for_view(rel, tid);
313 
314  if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
315  elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
318 
319  ItemPointerCopy(tid, result);
320 
321  snapshot = RegisterSnapshot(GetLatestSnapshot());
322  scan = table_beginscan_tid(rel, snapshot);
323  table_tuple_get_latest_tid(scan, result);
324  table_endscan(scan);
325  UnregisterSnapshot(snapshot);
326 
327  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
337 {
338  TupleDesc att = RelationGetDescr(viewrel);
339  RuleLock *rulelock;
340  RewriteRule *rewrite;
341  int i,
342  natts = att->natts,
343  tididx = -1;
344 
345  for (i = 0; i < natts; i++)
346  {
347  Form_pg_attribute attr = TupleDescAttr(att, i);
348 
349  if (strcmp(NameStr(attr->attname), "ctid") == 0)
350  {
351  if (attr->atttypid != TIDOID)
352  elog(ERROR, "ctid isn't of type TID");
353  tididx = i;
354  break;
355  }
356  }
357  if (tididx < 0)
358  elog(ERROR, "currtid cannot handle views with no CTID");
359  rulelock = viewrel->rd_rules;
360  if (!rulelock)
361  elog(ERROR, "the view has no rules");
362  for (i = 0; i < rulelock->numLocks; i++)
363  {
364  rewrite = rulelock->rules[i];
365  if (rewrite->event == CMD_SELECT)
366  {
367  Query *query;
368  TargetEntry *tle;
369 
370  if (list_length(rewrite->actions) != 1)
371  elog(ERROR, "only one select rule is allowed in views");
372  query = (Query *) linitial(rewrite->actions);
373  tle = get_tle_by_resno(query->targetList, tididx + 1);
374  if (tle && tle->expr && IsA(tle->expr, Var))
375  {
376  Var *var = (Var *) tle->expr;
377  RangeTblEntry *rte;
378 
379  if (!IS_SPECIAL_VARNO(var->varno) &&
381  {
382  rte = rt_fetch(var->varno, query->rtable);
383  if (rte)
384  {
385  ItemPointer result;
386  Relation rel;
387 
388  rel = table_open(rte->relid, AccessShareLock);
389  result = currtid_internal(rel, tid);
391  return result;
392  }
393  }
394  }
395  break;
396  }
397  }
398  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
409 {
412  ItemPointer result;
413  RangeVar *relrv;
414  Relation rel;
415 
417  rel = table_openrv(relrv, AccessShareLock);
418 
419  /* grab the latest tuple version associated to this CTID */
420  result = currtid_internal(rel, tid);
421 
423 
424  PG_RETURN_ITEMPOINTER(result);
425 }
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2688
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4079
uint32 BlockNumber
Definition: block.h:31
#define NameStr(name)
Definition: c.h:733
signed int int32
Definition: c.h:481
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereturn(context, dummy_value,...)
Definition: elog.h:276
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.h:37
static Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.h:31
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
#define PG_RETURN_ITEMPOINTER(x)
Definition: itemptr.h:243
static OffsetNumber ItemPointerGetOffsetNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:114
#define PG_GETARG_ITEMPOINTER(n)
Definition: itemptr.h:242
static BlockNumber ItemPointerGetBlockNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:93
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
#define AccessShareLock
Definition: lockdefs.h:36
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3322
char * pstrdup(const char *in)
Definition: mcxt.c:1683
void * palloc(Size size)
Definition: mcxt.c:1304
Oid GetUserId(void)
Definition: miscinit.c:514
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3539
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
@ CMD_SELECT
Definition: nodes.h:255
ObjectType get_relkind_objtype(char relkind)
uint16 OffsetNumber
Definition: off.h:24
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
#define ACL_SELECT
Definition: parsenodes.h:77
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
NameData relname
Definition: pg_class.h:38
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial(l)
Definition: pg_list.h:178
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
uintptr_t Datum
Definition: postgres.h:64
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:415
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:326
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:346
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:144
static void pq_sendint16(StringInfo buf, uint16 i)
Definition: pqformat.h:136
#define IS_SPECIAL_VARNO(varno)
Definition: primnodes.h:227
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define RelationGetNamespace(relation)
Definition: rel.h:546
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:291
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:836
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:794
StringInfoData * StringInfo
Definition: stringinfo.h:54
Definition: nodes.h:129
List * rtable
Definition: parsenodes.h:168
List * targetList
Definition: parsenodes.h:190
RuleLock * rd_rules
Definition: rel.h:115
Form_pg_class rd_rel
Definition: rel.h:111
CmdType event
Definition: prs2lock.h:27
List * actions
Definition: prs2lock.h:29
RewriteRule ** rules
Definition: prs2lock.h:43
int numLocks
Definition: prs2lock.h:42
Expr * expr
Definition: primnodes.h:1943
Definition: primnodes.h:234
AttrNumber varattno
Definition: primnodes.h:246
int varno
Definition: primnodes.h:241
Definition: c.h:674
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
void table_tuple_get_latest_tid(TableScanDesc scan, ItemPointer tid)
Definition: tableam.c:235
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1009
static TableScanDesc table_beginscan_tid(Relation rel, Snapshot snapshot)
Definition: tableam.h:985
Datum tidge(PG_FUNCTION_ARGS)
Definition: tid.c:221
#define DELIM
Definition: tid.c:42
Datum bttidcmp(PG_FUNCTION_ARGS)
Definition: tid.c:230
Datum currtid_byrelname(PG_FUNCTION_ARGS)
Definition: tid.c:408
static ItemPointer currtid_internal(Relation rel, ItemPointer tid)
Definition: tid.c:296
Datum hashtid(PG_FUNCTION_ARGS)
Definition: tid.c:257
Datum tidlarger(PG_FUNCTION_ARGS)
Definition: tid.c:239
#define NTIDARGS
Definition: tid.c:43
Datum hashtidextended(PG_FUNCTION_ARGS)
Definition: tid.c:272
Datum tideq(PG_FUNCTION_ARGS)
Definition: tid.c:176
Datum tidgt(PG_FUNCTION_ARGS)
Definition: tid.c:212
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:52
Datum tidle(PG_FUNCTION_ARGS)
Definition: tid.c:203
#define RDELIM
Definition: tid.c:41
Datum tidsmaller(PG_FUNCTION_ARGS)
Definition: tid.c:248
Datum tidne(PG_FUNCTION_ARGS)
Definition: tid.c:185
static ItemPointer currtid_for_view(Relation viewrel, ItemPointer tid)
Definition: tid.c:336
Datum tidrecv(PG_FUNCTION_ARGS)
Definition: tid.c:139
Datum tidout(PG_FUNCTION_ARGS)
Definition: tid.c:119
Datum tidsend(PG_FUNCTION_ARGS)
Definition: tid.c:160
Datum tidlt(PG_FUNCTION_ARGS)
Definition: tid.c:194
#define LDELIM
Definition: tid.c:40
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
List * textToQualifiedNameList(text *textval)
Definition: varlena.c:3398