Branch data 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
52 : 6276 : tidin(PG_FUNCTION_ARGS)
53 : : {
54 : 6276 : char *str = PG_GETARG_CSTRING(0);
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 : :
65 [ + - + + : 31191 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
66 [ + + + + : 24915 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
67 : 12544 : coord[i++] = p + 1;
68 : :
69 [ + + ]: 6276 : if (i < NTIDARGS)
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 : :
75 : 6268 : errno = 0;
76 : 6268 : cvt = strtoul(coord[0], &badp, 10);
77 [ + + + - : 6268 : if (badp == coord[0] || errno || *badp != DELIM)
- + ]
78 [ + - ]: 4 : ereturn(escontext, (Datum) 0,
79 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80 : : errmsg("invalid input syntax for type %s: \"%s\"",
81 : : "tid", str)));
82 : 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))
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 : :
98 : 6260 : cvt = strtoul(coord[1], &badp, 10);
99 [ + + + - : 6260 : if (badp == coord[1] || errno || *badp != RDELIM || cvt > USHRT_MAX)
+ - + + ]
100 [ + + ]: 16 : ereturn(escontext, (Datum) 0,
101 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
102 : : errmsg("invalid input syntax for type %s: \"%s\"",
103 : : "tid", str)));
104 : 6244 : offsetNumber = (OffsetNumber) cvt;
105 : :
106 : 6244 : result = (ItemPointer) palloc_object(ItemPointerData);
107 : :
108 : 6244 : ItemPointerSet(result, blockNumber, offsetNumber);
109 : :
110 : 6244 : PG_RETURN_ITEMPOINTER(result);
111 : : }
112 : :
113 : : /* ----------------------------------------------------------------
114 : : * tidout
115 : : * ----------------------------------------------------------------
116 : : */
117 : : Datum
118 : 24864 : tidout(PG_FUNCTION_ARGS)
119 : : {
120 : 24864 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
121 : : BlockNumber blockNumber;
122 : : OffsetNumber offsetNumber;
123 : : char buf[32];
124 : :
125 : 24864 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
126 : 24864 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
127 : :
128 : : /* Perhaps someday we should output this as a record. */
129 : 24864 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
130 : :
131 : 24864 : PG_RETURN_CSTRING(pstrdup(buf));
132 : : }
133 : :
134 : : /*
135 : : * tidrecv - converts external binary format to tid
136 : : */
137 : : Datum
138 : 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 : :
148 : 0 : result = (ItemPointer) palloc_object(ItemPointerData);
149 : :
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);
165 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
166 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
167 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
168 : : }
169 : :
170 : : /*****************************************************************************
171 : : * PUBLIC ROUTINES *
172 : : *****************************************************************************/
173 : :
174 : : Datum
175 : 12218599 : tideq(PG_FUNCTION_ARGS)
176 : : {
177 : 12218599 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
178 : 12218599 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
179 : :
180 : 12218599 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
181 : : }
182 : :
183 : : Datum
184 : 143 : tidne(PG_FUNCTION_ARGS)
185 : : {
186 : 143 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
187 : 143 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
188 : :
189 : 143 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
190 : : }
191 : :
192 : : Datum
193 : 9748 : tidlt(PG_FUNCTION_ARGS)
194 : : {
195 : 9748 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
196 : 9748 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
197 : :
198 : 9748 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
199 : : }
200 : :
201 : : Datum
202 : 2532 : tidle(PG_FUNCTION_ARGS)
203 : : {
204 : 2532 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
205 : 2532 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
206 : :
207 : 2532 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
208 : : }
209 : :
210 : : Datum
211 : 3556 : tidgt(PG_FUNCTION_ARGS)
212 : : {
213 : 3556 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
214 : 3556 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
215 : :
216 : 3556 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
217 : : }
218 : :
219 : : Datum
220 : 2488 : tidge(PG_FUNCTION_ARGS)
221 : : {
222 : 2488 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
223 : 2488 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
224 : :
225 : 2488 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
226 : : }
227 : :
228 : : Datum
229 : 1856799 : bttidcmp(PG_FUNCTION_ARGS)
230 : : {
231 : 1856799 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
232 : 1856799 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
233 : :
234 : 1856799 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
235 : : }
236 : :
237 : : Datum
238 : 800 : tidlarger(PG_FUNCTION_ARGS)
239 : : {
240 : 800 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
241 : 800 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
242 : :
243 [ - + ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
244 : : }
245 : :
246 : : Datum
247 : 800 : tidsmaller(PG_FUNCTION_ARGS)
248 : : {
249 : 800 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
250 : 800 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
251 : :
252 [ + - ]: 800 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
253 : : }
254 : :
255 : : Datum
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
271 : 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
288 : 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
324 : 40 : currtid_internal(Relation rel, const ItemPointerData *tid)
325 : : {
326 : : ItemPointer result;
327 : : AclResult aclresult;
328 : : Snapshot snapshot;
329 : : TableScanDesc scan;
330 : :
331 : 40 : result = (ItemPointer) palloc_object(ItemPointerData);
332 : :
333 : 40 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
334 : : ACL_SELECT);
335 [ - + ]: 40 : if (aclresult != ACLCHECK_OK)
336 : 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
337 : 0 : RelationGetRelationName(rel));
338 : :
339 [ + + ]: 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))
+ + + - +
+ ]
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. */
350 [ - + - - ]: 20 : if (!RelationIsPermanent(rel) && RecoveryInProgress())
351 [ # # ]: 0 : ereport(ERROR,
352 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
353 : : errmsg("cannot access temporary or unlogged relations during recovery")));
354 : :
355 : 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
372 : 16 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
373 : : {
374 : 16 : TupleDesc att = RelationGetDescr(viewrel);
375 : : RuleLock *rulelock;
376 : : RewriteRule *rewrite;
377 : : int i,
378 : 16 : natts = att->natts,
379 : 16 : tididx = -1;
380 : :
381 [ + + ]: 20 : for (i = 0; i < natts; i++)
382 : : {
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)
388 [ + - ]: 4 : ereport(ERROR,
389 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390 : : errmsg("ctid isn't of type TID"));
391 : 8 : tididx = i;
392 : 8 : break;
393 : : }
394 : : }
395 [ + + ]: 12 : if (tididx < 0)
396 [ + - ]: 4 : ereport(ERROR,
397 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
398 : : errmsg("currtid cannot handle views with no CTID"));
399 : 8 : rulelock = viewrel->rd_rules;
400 [ - + ]: 8 : if (!rulelock)
401 [ # # ]: 0 : ereport(ERROR,
402 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
403 : : errmsg("the view has no rules"));
404 [ + - ]: 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 : :
412 [ - + ]: 8 : if (list_length(rewrite->actions) != 1)
413 [ # # ]: 0 : ereport(ERROR,
414 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
415 : : errmsg("only one select rule is allowed in views"));
416 : 8 : query = (Query *) linitial(rewrite->actions);
417 : 8 : tle = get_tle_by_resno(query->targetList, tididx + 1);
418 [ + - + - : 8 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
419 : : {
420 : 8 : Var *var = (Var *) tle->expr;
421 : : RangeTblEntry *rte;
422 : :
423 [ + - ]: 8 : if (!IS_SPECIAL_VARNO(var->varno) &&
424 [ + - ]: 8 : var->varattno == SelfItemPointerAttributeNumber)
425 : : {
426 : 8 : rte = rt_fetch(var->varno, query->rtable);
427 [ + - ]: 8 : if (rte)
428 : : {
429 : : ItemPointer result;
430 : : Relation rel;
431 : :
432 : 8 : rel = table_open(rte->relid, AccessShareLock);
433 : 8 : result = currtid_internal(rel, tid);
434 : 4 : table_close(rel, AccessShareLock);
435 : 4 : return result;
436 : : }
437 : : }
438 : : }
439 : 0 : break;
440 : : }
441 : : }
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
452 : 36 : currtid_byrelname(PG_FUNCTION_ARGS)
453 : : {
454 : 36 : text *relname = PG_GETARG_TEXT_PP(0);
455 : 36 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
456 : : ItemPointer result;
457 : : RangeVar *relrv;
458 : : Relation rel;
459 : :
460 : 36 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
461 : 36 : rel = table_openrv(relrv, AccessShareLock);
462 : :
463 : : /* grab the latest tuple version associated to this CTID */
464 : 32 : result = currtid_internal(rel, tid);
465 : :
466 : 12 : table_close(rel, AccessShareLock);
467 : :
468 : 12 : PG_RETURN_ITEMPOINTER(result);
469 : : }
|