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