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 "catalog/namespace.h"
26 : : #include "catalog/pg_type.h"
27 : : #include "common/hashfn.h"
28 : : #include "libpq/pqformat.h"
29 : : #include "miscadmin.h"
30 : : #include "parser/parsetree.h"
31 : : #include "utils/acl.h"
32 : : #include "utils/fmgrprotos.h"
33 : : #include "utils/lsyscache.h"
34 : : #include "utils/rel.h"
35 : : #include "utils/snapmgr.h"
36 : : #include "utils/varlena.h"
37 : :
38 : :
39 : : #define LDELIM '('
40 : : #define RDELIM ')'
41 : : #define DELIM ','
42 : : #define NTIDARGS 2
43 : :
44 : : static ItemPointer currtid_for_view(Relation viewrel, const ItemPointerData *tid);
45 : :
46 : : /* ----------------------------------------------------------------
47 : : * tidin
48 : : * ----------------------------------------------------------------
49 : : */
50 : : Datum
9406 tgl@sss.pgh.pa.us 51 :CBC 6260 : tidin(PG_FUNCTION_ARGS)
52 : : {
53 : 6260 : char *str = PG_GETARG_CSTRING(0);
1238 54 : 6260 : Node *escontext = fcinfo->context;
55 : : char *p,
56 : : *coord[NTIDARGS];
57 : : int i;
58 : : ItemPointer result;
59 : : BlockNumber blockNumber;
60 : : OffsetNumber offsetNumber;
61 : : char *badp;
62 : : unsigned long cvt;
63 : :
10467 bruce@momjian.us 64 [ + - + + : 31156 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
1524 tgl@sss.pgh.pa.us 65 [ + + + + : 24896 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
10467 bruce@momjian.us 66 : 12512 : coord[i++] = p + 1;
67 : :
9703 inoue@tpf.co.jp 68 [ + + ]: 6260 : if (i < NTIDARGS)
1238 tgl@sss.pgh.pa.us 69 [ + + ]: 8 : ereturn(escontext, (Datum) 0,
70 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
71 : : errmsg("invalid input syntax for type %s: \"%s\"",
72 : : "tid", str)));
73 : :
8694 bruce@momjian.us 74 : 6252 : errno = 0;
1524 tgl@sss.pgh.pa.us 75 : 6252 : cvt = strtoul(coord[0], &badp, 10);
8694 bruce@momjian.us 76 [ + - - + ]: 6252 : if (errno || *badp != DELIM)
1238 tgl@sss.pgh.pa.us 77 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
78 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
79 : : errmsg("invalid input syntax for type %s: \"%s\"",
80 : : "tid", str)));
1524 tgl@sss.pgh.pa.us 81 :CBC 6252 : blockNumber = (BlockNumber) cvt;
82 : :
83 : : /*
84 : : * Cope with possibility that unsigned long is wider than BlockNumber, in
85 : : * which case strtoul will not raise an error for some values that are out
86 : : * of the range of BlockNumber. (See similar code in uint32in_subr().)
87 : : */
88 : : #if SIZEOF_LONG > 4
89 [ + + ]: 6252 : if (cvt != (unsigned long) blockNumber &&
90 [ + + ]: 12 : cvt != (unsigned long) ((int32) blockNumber))
1238 91 [ + - ]: 4 : ereturn(escontext, (Datum) 0,
92 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
93 : : errmsg("invalid input syntax for type %s: \"%s\"",
94 : : "tid", str)));
95 : : #endif
96 : :
1524 97 : 6248 : cvt = strtoul(coord[1], &badp, 10);
8694 bruce@momjian.us 98 [ + - + - : 6248 : if (errno || *badp != RDELIM ||
+ + ]
99 : : cvt > USHRT_MAX)
1238 tgl@sss.pgh.pa.us 100 [ + + ]: 12 : ereturn(escontext, (Datum) 0,
101 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
102 : : errmsg("invalid input syntax for type %s: \"%s\"",
103 : : "tid", str)));
1524 104 : 6236 : offsetNumber = (OffsetNumber) cvt;
105 : :
146 michael@paquier.xyz 106 :GNC 6236 : result = (ItemPointer) palloc_object(ItemPointerData);
107 : :
10467 bruce@momjian.us 108 :CBC 6236 : ItemPointerSet(result, blockNumber, offsetNumber);
109 : :
9406 tgl@sss.pgh.pa.us 110 : 6236 : PG_RETURN_ITEMPOINTER(result);
111 : : }
112 : :
113 : : /* ----------------------------------------------------------------
114 : : * tidout
115 : : * ----------------------------------------------------------------
116 : : */
117 : : Datum
118 : 24805 : tidout(PG_FUNCTION_ARGS)
119 : : {
9175 bruce@momjian.us 120 : 24805 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
121 : : BlockNumber blockNumber;
122 : : OffsetNumber offsetNumber;
123 : : char buf[32];
124 : :
3325 alvherre@alvh.no-ip. 125 : 24805 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
126 : 24805 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
127 : :
128 : : /* Perhaps someday we should output this as a record. */
8651 bruce@momjian.us 129 : 24805 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
130 : :
9406 tgl@sss.pgh.pa.us 131 : 24805 : PG_RETURN_CSTRING(pstrdup(buf));
132 : : }
133 : :
134 : : /*
135 : : * tidrecv - converts external binary format to tid
136 : : */
137 : : Datum
8394 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 : :
146 michael@paquier.xyz 148 :UNC 0 : result = (ItemPointer) palloc_object(ItemPointerData);
149 : :
8394 tgl@sss.pgh.pa.us 150 :UBC 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);
3128 andres@anarazel.de 165 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
166 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
8394 tgl@sss.pgh.pa.us 167 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
168 : : }
169 : :
170 : : /*****************************************************************************
171 : : * PUBLIC ROUTINES *
172 : : *****************************************************************************/
173 : :
174 : : Datum
9406 tgl@sss.pgh.pa.us 175 :CBC 12275691 : tideq(PG_FUNCTION_ARGS)
176 : : {
9175 bruce@momjian.us 177 : 12275691 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
178 : 12275691 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
179 : :
7153 180 : 12275691 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
181 : : }
182 : :
183 : : Datum
9406 tgl@sss.pgh.pa.us 184 : 143 : tidne(PG_FUNCTION_ARGS)
185 : : {
9175 bruce@momjian.us 186 : 143 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
187 : 143 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
188 : :
7153 189 : 143 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
190 : : }
191 : :
192 : : Datum
7228 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 : :
7153 bruce@momjian.us 198 : 55452 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
199 : : }
200 : :
201 : : Datum
7228 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 : :
7153 bruce@momjian.us 207 : 2532 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
208 : : }
209 : :
210 : : Datum
7228 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 : :
7153 bruce@momjian.us 216 : 3556 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
217 : : }
218 : :
219 : : Datum
7228 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 : :
7153 bruce@momjian.us 225 : 2488 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
226 : : }
227 : :
228 : : Datum
7228 tgl@sss.pgh.pa.us 229 : 1964801 : bttidcmp(PG_FUNCTION_ARGS)
230 : : {
231 : 1964801 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
232 : 1964801 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
233 : :
7193 234 : 1964801 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
235 : : }
236 : :
237 : : Datum
7228 238 : 800 : tidlarger(PG_FUNCTION_ARGS)
239 : : {
240 : 800 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
241 : 800 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
242 : :
7153 bruce@momjian.us 243 [ - + ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
244 : : }
245 : :
246 : : Datum
7228 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 : :
7153 bruce@momjian.us 252 [ + - ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
253 : : }
254 : :
255 : : Datum
2683 tgl@sss.pgh.pa.us 256 : 84048 : hashtid(PG_FUNCTION_ARGS)
257 : : {
258 : 84048 : 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 : 84048 : return hash_any((unsigned char *) key,
267 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
268 : : }
269 : :
270 : : Datum
2683 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
30 andres@anarazel.de 288 :GNC 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
187 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 : :
146 michael@paquier.xyz 331 : 40 : result = (ItemPointer) palloc_object(ItemPointerData);
332 : :
1987 michael@paquier.xyz 333 :CBC 40 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
334 : : ACL_SELECT);
335 [ - + ]: 40 : if (aclresult != ACLCHECK_OK)
1987 michael@paquier.xyz 336 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
337 : 0 : RelationGetRelationName(rel));
338 : :
1987 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))
+ + + - +
+ ]
670 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 : :
1987 349 : 20 : ItemPointerCopy(tid, result);
350 : :
351 : 20 : snapshot = RegisterSnapshot(GetLatestSnapshot());
352 : 20 : scan = table_beginscan_tid(rel, snapshot);
353 : 20 : table_tuple_get_latest_tid(scan, result);
354 : 12 : table_endscan(scan);
355 : 12 : UnregisterSnapshot(snapshot);
356 : :
357 : 12 : return result;
358 : : }
359 : :
360 : : /*
361 : : * Handle CTIDs of views.
362 : : * CTID should be defined in the view and it must
363 : : * correspond to the CTID of a base relation.
364 : : */
365 : : static ItemPointer
187 peter@eisentraut.org 366 :GNC 16 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
367 : : {
8749 inoue@tpf.co.jp 368 :CBC 16 : TupleDesc att = RelationGetDescr(viewrel);
369 : : RuleLock *rulelock;
370 : : RewriteRule *rewrite;
371 : : int i,
8644 bruce@momjian.us 372 : 16 : natts = att->natts,
373 : 16 : tididx = -1;
374 : :
375 [ + + ]: 20 : for (i = 0; i < natts; i++)
376 : : {
3180 andres@anarazel.de 377 : 16 : Form_pg_attribute attr = TupleDescAttr(att, i);
378 : :
379 [ + + ]: 16 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
380 : : {
381 [ + + ]: 12 : if (attr->atttypid != TIDOID)
670 michael@paquier.xyz 382 [ + - ]: 4 : ereport(ERROR,
383 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
384 : : errmsg("ctid isn't of type TID"));
8749 inoue@tpf.co.jp 385 : 8 : tididx = i;
8303 tgl@sss.pgh.pa.us 386 : 8 : break;
387 : : }
388 : : }
8749 inoue@tpf.co.jp 389 [ + + ]: 12 : if (tididx < 0)
670 michael@paquier.xyz 390 [ + - ]: 4 : ereport(ERROR,
391 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
392 : : errmsg("currtid cannot handle views with no CTID"));
8318 tgl@sss.pgh.pa.us 393 : 8 : rulelock = viewrel->rd_rules;
394 [ - + ]: 8 : if (!rulelock)
670 michael@paquier.xyz 395 [ # # ]:UBC 0 : ereport(ERROR,
396 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
397 : : errmsg("the view has no rules"));
8749 inoue@tpf.co.jp 398 [ + - ]:CBC 8 : for (i = 0; i < rulelock->numLocks; i++)
399 : : {
400 : 8 : rewrite = rulelock->rules[i];
401 [ + - ]: 8 : if (rewrite->event == CMD_SELECT)
402 : : {
403 : : Query *query;
404 : : TargetEntry *tle;
405 : :
8010 neilc@samurai.com 406 [ - + ]: 8 : if (list_length(rewrite->actions) != 1)
670 michael@paquier.xyz 407 [ # # ]:UBC 0 : ereport(ERROR,
408 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
409 : : errmsg("only one select rule is allowed in views"));
8014 neilc@samurai.com 410 :CBC 8 : query = (Query *) linitial(rewrite->actions);
7919 bruce@momjian.us 411 : 8 : tle = get_tle_by_resno(query->targetList, tididx + 1);
8318 tgl@sss.pgh.pa.us 412 [ + - + - : 8 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
413 : : {
8644 bruce@momjian.us 414 : 8 : Var *var = (Var *) tle->expr;
415 : : RangeTblEntry *rte;
416 : :
5320 tgl@sss.pgh.pa.us 417 [ + - ]: 8 : if (!IS_SPECIAL_VARNO(var->varno) &&
8318 418 [ + - ]: 8 : var->varattno == SelfItemPointerAttributeNumber)
419 : : {
8303 420 : 8 : rte = rt_fetch(var->varno, query->rtable);
8749 inoue@tpf.co.jp 421 [ + - ]: 8 : if (rte)
422 : : {
423 : : ItemPointer result;
424 : : Relation rel;
425 : :
1987 michael@paquier.xyz 426 : 8 : rel = table_open(rte->relid, AccessShareLock);
427 : 8 : result = currtid_internal(rel, tid);
428 : 4 : table_close(rel, AccessShareLock);
2164 429 : 4 : return result;
430 : : }
431 : : }
432 : : }
8749 inoue@tpf.co.jp 433 :UBC 0 : break;
434 : : }
435 : : }
8318 tgl@sss.pgh.pa.us 436 [ # # ]: 0 : elog(ERROR, "currtid cannot handle this view");
437 : : return NULL;
438 : : }
439 : :
440 : : /*
441 : : * currtid_byrelname
442 : : * Get the latest tuple version of the tuple pointing at a CTID, for a
443 : : * given relation name.
444 : : */
445 : : Datum
9461 tgl@sss.pgh.pa.us 446 :CBC 36 : currtid_byrelname(PG_FUNCTION_ARGS)
447 : : {
3341 noah@leadboat.com 448 : 36 : text *relname = PG_GETARG_TEXT_PP(0);
9175 bruce@momjian.us 449 : 36 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
450 : : ItemPointer result;
451 : : RangeVar *relrv;
452 : : Relation rel;
453 : :
7648 neilc@samurai.com 454 : 36 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
2661 andres@anarazel.de 455 : 36 : rel = table_openrv(relrv, AccessShareLock);
456 : :
457 : : /* grab the latest tuple version associated to this CTID */
1987 michael@paquier.xyz 458 : 32 : result = currtid_internal(rel, tid);
459 : :
2661 andres@anarazel.de 460 : 12 : table_close(rel, AccessShareLock);
461 : :
9406 tgl@sss.pgh.pa.us 462 : 12 : PG_RETURN_ITEMPOINTER(result);
463 : : }
|