Age Owner Branch data 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-2026, 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 <limits.h>
21 : :
22 : : #include "access/sysattr.h"
23 : : #include "access/table.h"
24 : : #include "access/tableam.h"
25 : : #include "access/xlog.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, const ItemPointerData *tid);
46 : :
47 : : /* ----------------------------------------------------------------
48 : : * tidin
49 : : * ----------------------------------------------------------------
50 : : */
51 : : Datum
9590 tgl@sss.pgh.pa.us 52 :CBC 6276 : tidin(PG_FUNCTION_ARGS)
53 : : {
54 : 6276 : char *str = PG_GETARG_CSTRING(0);
1422 55 : 6276 : 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 : :
10651 bruce@momjian.us 65 [ + - + + : 31192 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
1708 tgl@sss.pgh.pa.us 66 [ + + + + : 24916 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
10651 bruce@momjian.us 67 : 12544 : coord[i++] = p + 1;
68 : :
9887 inoue@tpf.co.jp 69 [ + + ]: 6276 : if (i < NTIDARGS)
1422 tgl@sss.pgh.pa.us 70 [ + + ]: 8 : ereturn(escontext, (Datum) 0,
71 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
72 : : errmsg("invalid input syntax for type %s: \"%s\"",
73 : : "tid", str)));
74 : :
8878 bruce@momjian.us 75 : 6268 : errno = 0;
1708 tgl@sss.pgh.pa.us 76 : 6268 : cvt = strtoul(coord[0], &badp, 10);
91 michael@paquier.xyz 77 [ + + + - :GNC 6268 : if (badp == coord[0] || errno || *badp != DELIM)
- + ]
1422 tgl@sss.pgh.pa.us 78 [ + - ]:GBC 4 : ereturn(escontext, (Datum) 0,
79 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80 : : errmsg("invalid input syntax for type %s: \"%s\"",
81 : : "tid", str)));
1708 tgl@sss.pgh.pa.us 82 :CBC 6264 : 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 uint32in_subr().)
88 : : */
89 : : #if SIZEOF_LONG > 4
90 [ + + ]: 6264 : if (cvt != (unsigned long) blockNumber &&
91 [ + + ]: 12 : cvt != (unsigned long) ((int32) blockNumber))
1422 92 [ + - ]: 4 : 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 : :
1708 98 : 6260 : cvt = strtoul(coord[1], &badp, 10);
91 michael@paquier.xyz 99 [ + + + - :GNC 6260 : if (badp == coord[1] || errno || *badp != RDELIM || cvt > USHRT_MAX)
+ - + + ]
1422 tgl@sss.pgh.pa.us 100 [ + + ]:CBC 16 : ereturn(escontext, (Datum) 0,
101 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
102 : : errmsg("invalid input syntax for type %s: \"%s\"",
103 : : "tid", str)));
1708 104 : 6244 : offsetNumber = (OffsetNumber) cvt;
105 : :
330 michael@paquier.xyz 106 : 6244 : result = (ItemPointer) palloc_object(ItemPointerData);
107 : :
10651 bruce@momjian.us 108 : 6244 : ItemPointerSet(result, blockNumber, offsetNumber);
109 : :
9590 tgl@sss.pgh.pa.us 110 : 6244 : PG_RETURN_ITEMPOINTER(result);
111 : : }
112 : :
113 : : /* ----------------------------------------------------------------
114 : : * tidout
115 : : * ----------------------------------------------------------------
116 : : */
117 : : Datum
118 : 24864 : tidout(PG_FUNCTION_ARGS)
119 : : {
9359 bruce@momjian.us 120 : 24864 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
121 : : BlockNumber blockNumber;
122 : : OffsetNumber offsetNumber;
123 : : char buf[32];
124 : :
3509 alvherre@alvh.no-ip. 125 : 24864 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
126 : 24864 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
127 : :
128 : : /* Perhaps someday we should output this as a record. */
8835 bruce@momjian.us 129 : 24864 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
130 : :
9590 tgl@sss.pgh.pa.us 131 : 24864 : PG_RETURN_CSTRING(pstrdup(buf));
132 : : }
133 : :
134 : : /*
135 : : * tidrecv - converts external binary format to tid
136 : : */
137 : : Datum
8578 tgl@sss.pgh.pa.us 138 :UBC 0 : tidrecv(PG_FUNCTION_ARGS)
139 : : {
140 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
141 : : ItemPointer result;
142 : : BlockNumber blockNumber;
143 : : OffsetNumber offsetNumber;
144 : :
145 : 0 : blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
146 : 0 : offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
147 : :
330 michael@paquier.xyz 148 : 0 : result = (ItemPointer) palloc_object(ItemPointerData);
149 : :
8578 tgl@sss.pgh.pa.us 150 : 0 : ItemPointerSet(result, blockNumber, offsetNumber);
151 : :
152 : 0 : PG_RETURN_ITEMPOINTER(result);
153 : : }
154 : :
155 : : /*
156 : : * tidsend - converts tid to binary format
157 : : */
158 : : Datum
159 : 0 : tidsend(PG_FUNCTION_ARGS)
160 : : {
161 : 0 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
162 : : StringInfoData buf;
163 : :
164 : 0 : pq_begintypsend(&buf);
3312 andres@anarazel.de 165 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
166 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
8578 tgl@sss.pgh.pa.us 167 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
168 : : }
169 : :
170 : : /*****************************************************************************
171 : : * PUBLIC ROUTINES *
172 : : *****************************************************************************/
173 : :
174 : : Datum
9590 tgl@sss.pgh.pa.us 175 :CBC 12239691 : tideq(PG_FUNCTION_ARGS)
176 : : {
9359 bruce@momjian.us 177 : 12239691 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
178 : 12239691 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
179 : :
7337 180 : 12239691 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
181 : : }
182 : :
183 : : Datum
9590 tgl@sss.pgh.pa.us 184 : 143 : tidne(PG_FUNCTION_ARGS)
185 : : {
9359 bruce@momjian.us 186 : 143 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
187 : 143 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
188 : :
7337 189 : 143 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
190 : : }
191 : :
192 : : Datum
7412 tgl@sss.pgh.pa.us 193 : 55452 : tidlt(PG_FUNCTION_ARGS)
194 : : {
195 : 55452 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
196 : 55452 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
197 : :
7337 bruce@momjian.us 198 : 55452 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
199 : : }
200 : :
201 : : Datum
7412 tgl@sss.pgh.pa.us 202 : 2532 : tidle(PG_FUNCTION_ARGS)
203 : : {
204 : 2532 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
205 : 2532 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
206 : :
7337 bruce@momjian.us 207 : 2532 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
208 : : }
209 : :
210 : : Datum
7412 tgl@sss.pgh.pa.us 211 : 3556 : tidgt(PG_FUNCTION_ARGS)
212 : : {
213 : 3556 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
214 : 3556 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
215 : :
7337 bruce@momjian.us 216 : 3556 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
217 : : }
218 : :
219 : : Datum
7412 tgl@sss.pgh.pa.us 220 : 2488 : tidge(PG_FUNCTION_ARGS)
221 : : {
222 : 2488 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
223 : 2488 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
224 : :
7337 bruce@momjian.us 225 : 2488 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
226 : : }
227 : :
228 : : Datum
7412 tgl@sss.pgh.pa.us 229 : 1856802 : bttidcmp(PG_FUNCTION_ARGS)
230 : : {
231 : 1856802 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
232 : 1856802 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
233 : :
7377 234 : 1856802 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
235 : : }
236 : :
237 : : Datum
7412 238 : 800 : tidlarger(PG_FUNCTION_ARGS)
239 : : {
240 : 800 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
241 : 800 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
242 : :
7337 bruce@momjian.us 243 [ - + ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
244 : : }
245 : :
246 : : Datum
7412 tgl@sss.pgh.pa.us 247 : 800 : tidsmaller(PG_FUNCTION_ARGS)
248 : : {
249 : 800 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
250 : 800 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
251 : :
7337 bruce@momjian.us 252 [ + - ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
253 : : }
254 : :
255 : : Datum
2867 tgl@sss.pgh.pa.us 256 : 59627 : hashtid(PG_FUNCTION_ARGS)
257 : : {
258 : 59627 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
259 : :
260 : : /*
261 : : * While you'll probably have a lot of trouble with a compiler that
262 : : * insists on appending pad space to struct ItemPointerData, we can at
263 : : * least make this code work, by not using sizeof(ItemPointerData).
264 : : * Instead rely on knowing the sizes of the component fields.
265 : : */
266 : 59627 : return hash_any((unsigned char *) key,
267 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
268 : : }
269 : :
270 : : Datum
2867 tgl@sss.pgh.pa.us 271 :UBC 0 : hashtidextended(PG_FUNCTION_ARGS)
272 : : {
273 : 0 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
274 : 0 : uint64 seed = PG_GETARG_INT64(1);
275 : :
276 : : /* As above */
277 : 0 : return hash_any_extended((unsigned char *) key,
278 : : sizeof(BlockIdData) + sizeof(OffsetNumber),
279 : : seed);
280 : : }
281 : :
282 : : /*
283 : : * Extract the block number from a TID
284 : : *
285 : : * Returns int8 because BlockNumber is uint32, which exceeds the range of int4.
286 : : */
287 : : Datum
214 andres@anarazel.de 288 :CBC 65 : tid_block(PG_FUNCTION_ARGS)
289 : : {
290 : 65 : ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
291 : :
292 : : /* need to use NoCheck, as tidin allows InvalidBlockNumber */
293 : 65 : PG_RETURN_INT64((int64) ItemPointerGetBlockNumberNoCheck(tid));
294 : : }
295 : :
296 : : /*
297 : : * Extract the offset number from a TID
298 : : *
299 : : * Returns int4 because OffsetNumber is uint16, which exceeds the range of
300 : : * int2.
301 : : */
302 : : Datum
303 : 60 : tid_offset(PG_FUNCTION_ARGS)
304 : : {
305 : 60 : ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
306 : :
307 : : /* need to use NoCheck, as tidin allows InvalidOffsetNumber */
308 : 60 : PG_RETURN_INT32((int32) ItemPointerGetOffsetNumberNoCheck(tid));
309 : : }
310 : :
311 : :
312 : : /*
313 : : * Functions to get latest tid of a specified tuple.
314 : : *
315 : : * Maybe these implementations should be moved to another place
316 : : */
317 : :
318 : : /*
319 : : * Utility wrapper for current CTID functions.
320 : : * Returns the latest version of a tuple pointing at "tid" for
321 : : * relation "rel".
322 : : */
323 : : static ItemPointer
371 peter@eisentraut.org 324 : 40 : currtid_internal(Relation rel, const ItemPointerData *tid)
325 : : {
326 : : ItemPointer result;
327 : : AclResult aclresult;
328 : : Snapshot snapshot;
329 : : TableScanDesc scan;
330 : :
330 michael@paquier.xyz 331 : 40 : result = (ItemPointer) palloc_object(ItemPointerData);
332 : :
2171 333 : 40 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
334 : : ACL_SELECT);
335 [ - + ]: 40 : if (aclresult != ACLCHECK_OK)
2171 michael@paquier.xyz 336 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
337 : 0 : RelationGetRelationName(rel));
338 : :
2171 michael@paquier.xyz 339 [ + + ]:CBC 40 : if (rel->rd_rel->relkind == RELKIND_VIEW)
340 : 16 : return currtid_for_view(rel, tid);
341 : :
342 [ + + + - : 24 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ + + - +
+ ]
854 343 [ + - ]: 4 : ereport(ERROR,
344 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
345 : : errmsg("cannot look at latest visible tid for relation \"%s.%s\"",
346 : : get_namespace_name(RelationGetNamespace(rel)),
347 : : RelationGetRelationName(rel)));
348 : :
349 : : /* Temporary and unlogged relations are inaccessible during recovery. */
15 msawada@postgresql.o 350 [ - + - - ]: 20 : if (!RelationIsPermanent(rel) && RecoveryInProgress())
15 msawada@postgresql.o 351 [ # # ]:UBC 0 : ereport(ERROR,
352 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
353 : : errmsg("cannot access temporary or unlogged relations during recovery")));
354 : :
2171 michael@paquier.xyz 355 :CBC 20 : ItemPointerCopy(tid, result);
356 : :
357 : 20 : snapshot = RegisterSnapshot(GetLatestSnapshot());
358 : 20 : scan = table_beginscan_tid(rel, snapshot);
359 : 20 : table_tuple_get_latest_tid(scan, result);
360 : 12 : table_endscan(scan);
361 : 12 : UnregisterSnapshot(snapshot);
362 : :
363 : 12 : return result;
364 : : }
365 : :
366 : : /*
367 : : * Handle CTIDs of views.
368 : : * CTID should be defined in the view and it must
369 : : * correspond to the CTID of a base relation.
370 : : */
371 : : static ItemPointer
371 peter@eisentraut.org 372 : 16 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
373 : : {
8933 inoue@tpf.co.jp 374 : 16 : TupleDesc att = RelationGetDescr(viewrel);
375 : : RuleLock *rulelock;
376 : : RewriteRule *rewrite;
377 : : int i,
8828 bruce@momjian.us 378 : 16 : natts = att->natts,
379 : 16 : tididx = -1;
380 : :
381 [ + + ]: 20 : for (i = 0; i < natts; i++)
382 : : {
3364 andres@anarazel.de 383 : 16 : Form_pg_attribute attr = TupleDescAttr(att, i);
384 : :
385 [ + + ]: 16 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
386 : : {
387 [ + + ]: 12 : if (attr->atttypid != TIDOID)
854 michael@paquier.xyz 388 [ + - ]: 4 : ereport(ERROR,
389 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390 : : errmsg("ctid isn't of type TID"));
8933 inoue@tpf.co.jp 391 : 8 : tididx = i;
8487 tgl@sss.pgh.pa.us 392 : 8 : break;
393 : : }
394 : : }
8933 inoue@tpf.co.jp 395 [ + + ]: 12 : if (tididx < 0)
854 michael@paquier.xyz 396 [ + - ]: 4 : ereport(ERROR,
397 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
398 : : errmsg("currtid cannot handle views with no CTID"));
8502 tgl@sss.pgh.pa.us 399 : 8 : rulelock = viewrel->rd_rules;
400 [ - + ]: 8 : if (!rulelock)
854 michael@paquier.xyz 401 [ # # ]:UBC 0 : ereport(ERROR,
402 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
403 : : errmsg("the view has no rules"));
8933 inoue@tpf.co.jp 404 [ + - ]:CBC 8 : for (i = 0; i < rulelock->numLocks; i++)
405 : : {
406 : 8 : rewrite = rulelock->rules[i];
407 [ + - ]: 8 : if (rewrite->event == CMD_SELECT)
408 : : {
409 : : Query *query;
410 : : TargetEntry *tle;
411 : :
8194 neilc@samurai.com 412 [ - + ]: 8 : if (list_length(rewrite->actions) != 1)
854 michael@paquier.xyz 413 [ # # ]:UBC 0 : ereport(ERROR,
414 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
415 : : errmsg("only one select rule is allowed in views"));
8198 neilc@samurai.com 416 :CBC 8 : query = (Query *) linitial(rewrite->actions);
8103 bruce@momjian.us 417 : 8 : tle = get_tle_by_resno(query->targetList, tididx + 1);
8502 tgl@sss.pgh.pa.us 418 [ + - + - : 8 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
419 : : {
8828 bruce@momjian.us 420 : 8 : Var *var = (Var *) tle->expr;
421 : : RangeTblEntry *rte;
422 : :
5504 tgl@sss.pgh.pa.us 423 [ + - ]: 8 : if (!IS_SPECIAL_VARNO(var->varno) &&
8502 424 [ + - ]: 8 : var->varattno == SelfItemPointerAttributeNumber)
425 : : {
8487 426 : 8 : rte = rt_fetch(var->varno, query->rtable);
8933 inoue@tpf.co.jp 427 [ + - ]: 8 : if (rte)
428 : : {
429 : : ItemPointer result;
430 : : Relation rel;
431 : :
2171 michael@paquier.xyz 432 : 8 : rel = table_open(rte->relid, AccessShareLock);
433 : 8 : result = currtid_internal(rel, tid);
434 : 4 : table_close(rel, AccessShareLock);
2348 435 : 4 : return result;
436 : : }
437 : : }
438 : : }
8933 inoue@tpf.co.jp 439 :UBC 0 : break;
440 : : }
441 : : }
8502 tgl@sss.pgh.pa.us 442 [ # # ]: 0 : elog(ERROR, "currtid cannot handle this view");
443 : : return NULL;
444 : : }
445 : :
446 : : /*
447 : : * currtid_byrelname
448 : : * Get the latest tuple version of the tuple pointing at a CTID, for a
449 : : * given relation name.
450 : : */
451 : : Datum
9645 tgl@sss.pgh.pa.us 452 :CBC 36 : currtid_byrelname(PG_FUNCTION_ARGS)
453 : : {
3525 noah@leadboat.com 454 : 36 : text *relname = PG_GETARG_TEXT_PP(0);
9359 bruce@momjian.us 455 : 36 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
456 : : ItemPointer result;
457 : : RangeVar *relrv;
458 : : Relation rel;
459 : :
7832 neilc@samurai.com 460 : 36 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
2845 andres@anarazel.de 461 : 36 : rel = table_openrv(relrv, AccessShareLock);
462 : :
463 : : /* grab the latest tuple version associated to this CTID */
2171 michael@paquier.xyz 464 : 32 : result = currtid_internal(rel, tid);
465 : :
2845 andres@anarazel.de 466 : 12 : table_close(rel, AccessShareLock);
467 : :
9590 tgl@sss.pgh.pa.us 468 : 12 : PG_RETURN_ITEMPOINTER(result);
469 : : }
|