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 : 661 : tidin(PG_FUNCTION_ARGS)
52 : : {
53 : 661 : char *str = PG_GETARG_CSTRING(0);
54 : 661 : Node *escontext = fcinfo->context;
55 : 661 : char *p,
56 : : *coord[NTIDARGS];
57 : 661 : int i;
58 : 661 : ItemPointer result;
59 : 661 : BlockNumber blockNumber;
60 : 661 : OffsetNumber offsetNumber;
61 : 661 : char *badp;
62 : 661 : unsigned long cvt;
63 : :
64 [ + + + + : 3953 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
65 [ + + + + : 4608 : if (*p == DELIM || (*p == LDELIM && i == 0))
- + ]
66 : 1316 : coord[i++] = p + 1;
67 : :
68 [ + + ]: 661 : if (i < NTIDARGS)
69 [ + + ]: 2 : ereturn(escontext, (Datum) 0,
70 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
71 : : errmsg("invalid input syntax for type %s: \"%s\"",
72 : : "tid", str)));
73 : :
74 : 659 : errno = 0;
75 : 659 : cvt = strtoul(coord[0], &badp, 10);
76 [ + + + + ]: 659 : if (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 : 659 : 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 [ + + - + ]: 659 : if (cvt != (unsigned long) blockNumber &&
90 : 2 : cvt != (unsigned long) ((int32) blockNumber))
91 [ + + ]: 2 : 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 : 657 : cvt = strtoul(coord[1], &badp, 10);
98 [ + + + - : 657 : if (errno || *badp != RDELIM ||
+ + ]
99 : 656 : cvt > USHRT_MAX)
100 [ - + ]: 4 : ereturn(escontext, (Datum) 0,
101 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
102 : : errmsg("invalid input syntax for type %s: \"%s\"",
103 : : "tid", str)));
104 : 653 : offsetNumber = (OffsetNumber) cvt;
105 : :
106 : 653 : result = (ItemPointer) palloc_object(ItemPointerData);
107 : :
108 : 653 : ItemPointerSet(result, blockNumber, offsetNumber);
109 : :
110 : 653 : PG_RETURN_ITEMPOINTER(result);
111 : 657 : }
112 : :
113 : : /* ----------------------------------------------------------------
114 : : * tidout
115 : : * ----------------------------------------------------------------
116 : : */
117 : : Datum
118 : 474 : tidout(PG_FUNCTION_ARGS)
119 : : {
120 : 474 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
121 : 474 : BlockNumber blockNumber;
122 : 474 : OffsetNumber offsetNumber;
123 : 474 : char buf[32];
124 : :
125 : 474 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
126 : 474 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
127 : :
128 : : /* Perhaps someday we should output this as a record. */
129 : 474 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
130 : :
131 : 948 : PG_RETURN_CSTRING(pstrdup(buf));
132 : 474 : }
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 : 0 : ItemPointer result;
142 : 0 : BlockNumber blockNumber;
143 : 0 : 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 : 0 : }
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 : 0 : 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 : 0 : }
169 : :
170 : : /*****************************************************************************
171 : : * PUBLIC ROUTINES *
172 : : *****************************************************************************/
173 : :
174 : : Datum
175 : 3068798 : tideq(PG_FUNCTION_ARGS)
176 : : {
177 : 3068798 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
178 : 3068798 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
179 : :
180 : 6137596 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
181 : 3068798 : }
182 : :
183 : : Datum
184 : 29 : tidne(PG_FUNCTION_ARGS)
185 : : {
186 : 29 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
187 : 29 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
188 : :
189 : 58 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
190 : 29 : }
191 : :
192 : : Datum
193 : 13850 : tidlt(PG_FUNCTION_ARGS)
194 : : {
195 : 13850 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
196 : 13850 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
197 : :
198 : 27700 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
199 : 13850 : }
200 : :
201 : : Datum
202 : 633 : tidle(PG_FUNCTION_ARGS)
203 : : {
204 : 633 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
205 : 633 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
206 : :
207 : 1266 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
208 : 633 : }
209 : :
210 : : Datum
211 : 878 : tidgt(PG_FUNCTION_ARGS)
212 : : {
213 : 878 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
214 : 878 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
215 : :
216 : 1756 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
217 : 878 : }
218 : :
219 : : Datum
220 : 622 : tidge(PG_FUNCTION_ARGS)
221 : : {
222 : 622 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
223 : 622 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
224 : :
225 : 1244 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
226 : 622 : }
227 : :
228 : : Datum
229 : 490476 : bttidcmp(PG_FUNCTION_ARGS)
230 : : {
231 : 490476 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
232 : 490476 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
233 : :
234 : 980952 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
235 : 490476 : }
236 : :
237 : : Datum
238 : 200 : tidlarger(PG_FUNCTION_ARGS)
239 : : {
240 : 200 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
241 : 200 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
242 : :
243 [ - + ]: 200 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
244 : 200 : }
245 : :
246 : : Datum
247 : 200 : tidsmaller(PG_FUNCTION_ARGS)
248 : : {
249 : 200 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
250 : 200 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
251 : :
252 [ + - ]: 200 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
253 : 200 : }
254 : :
255 : : Datum
256 : 21008 : hashtid(PG_FUNCTION_ARGS)
257 : : {
258 : 21008 : 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 : 42016 : return hash_any((unsigned char *) key,
267 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
268 : 21008 : }
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 : 0 : seed);
280 : 0 : }
281 : :
282 : :
283 : : /*
284 : : * Functions to get latest tid of a specified tuple.
285 : : *
286 : : * Maybe these implementations should be moved to another place
287 : : */
288 : :
289 : : /*
290 : : * Utility wrapper for current CTID functions.
291 : : * Returns the latest version of a tuple pointing at "tid" for
292 : : * relation "rel".
293 : : */
294 : : static ItemPointer
295 : 10 : currtid_internal(Relation rel, const ItemPointerData *tid)
296 : : {
297 : 10 : ItemPointer result;
298 : 10 : AclResult aclresult;
299 : 10 : Snapshot snapshot;
300 : 10 : TableScanDesc scan;
301 : :
302 : 10 : result = (ItemPointer) palloc_object(ItemPointerData);
303 : :
304 : 10 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
305 : : ACL_SELECT);
306 [ + - ]: 10 : if (aclresult != ACLCHECK_OK)
307 : 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
308 : 0 : RelationGetRelationName(rel));
309 : :
310 [ + + ]: 10 : if (rel->rd_rel->relkind == RELKIND_VIEW)
311 : 4 : return currtid_for_view(rel, tid);
312 : :
313 [ + + + - : 6 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ + + - +
+ ]
314 [ + - + - ]: 1 : ereport(ERROR,
315 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
316 : : errmsg("cannot look at latest visible tid for relation \"%s.%s\"",
317 : : get_namespace_name(RelationGetNamespace(rel)),
318 : : RelationGetRelationName(rel)));
319 : :
320 : 5 : ItemPointerCopy(tid, result);
321 : :
322 : 5 : snapshot = RegisterSnapshot(GetLatestSnapshot());
323 : 5 : scan = table_beginscan_tid(rel, snapshot);
324 : 5 : table_tuple_get_latest_tid(scan, result);
325 : 5 : table_endscan(scan);
326 : 5 : UnregisterSnapshot(snapshot);
327 : :
328 : 5 : return result;
329 : 9 : }
330 : :
331 : : /*
332 : : * Handle CTIDs of views.
333 : : * CTID should be defined in the view and it must
334 : : * correspond to the CTID of a base relation.
335 : : */
336 : : static ItemPointer
337 : 4 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
338 : : {
339 : 4 : TupleDesc att = RelationGetDescr(viewrel);
340 : 4 : RuleLock *rulelock;
341 : 4 : RewriteRule *rewrite;
342 : 8 : int i,
343 : 4 : natts = att->natts,
344 : 4 : tididx = -1;
345 : :
346 [ + + ]: 5 : for (i = 0; i < natts; i++)
347 : : {
348 : 4 : Form_pg_attribute attr = TupleDescAttr(att, i);
349 : :
350 [ + + ]: 4 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
351 : : {
352 [ + + ]: 3 : if (attr->atttypid != TIDOID)
353 [ + - + - ]: 1 : ereport(ERROR,
354 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
355 : : errmsg("ctid isn't of type TID"));
356 : 2 : tididx = i;
357 : 2 : break;
358 : : }
359 [ - + + ]: 3 : }
360 [ + + ]: 3 : if (tididx < 0)
361 [ + - + - ]: 1 : ereport(ERROR,
362 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
363 : : errmsg("currtid cannot handle views with no CTID"));
364 : 2 : rulelock = viewrel->rd_rules;
365 [ + - ]: 2 : if (!rulelock)
366 [ # # # # ]: 0 : ereport(ERROR,
367 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
368 : : errmsg("the view has no rules"));
369 [ + - ]: 2 : for (i = 0; i < rulelock->numLocks; i++)
370 : : {
371 : 2 : rewrite = rulelock->rules[i];
372 [ - + ]: 2 : if (rewrite->event == CMD_SELECT)
373 : : {
374 : 2 : Query *query;
375 : 2 : TargetEntry *tle;
376 : :
377 [ + - ]: 2 : if (list_length(rewrite->actions) != 1)
378 [ # # # # ]: 0 : ereport(ERROR,
379 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
380 : : errmsg("only one select rule is allowed in views"));
381 : 2 : query = (Query *) linitial(rewrite->actions);
382 : 2 : tle = get_tle_by_resno(query->targetList, tididx + 1);
383 [ + - + - : 2 : if (tle && tle->expr && IsA(tle->expr, Var))
- + ]
384 : : {
385 : 2 : Var *var = (Var *) tle->expr;
386 : 2 : RangeTblEntry *rte;
387 : :
388 [ + - - + ]: 2 : if (!IS_SPECIAL_VARNO(var->varno) &&
389 : 2 : var->varattno == SelfItemPointerAttributeNumber)
390 : : {
391 : 2 : rte = rt_fetch(var->varno, query->rtable);
392 [ + - ]: 2 : if (rte)
393 : : {
394 : 2 : ItemPointer result;
395 : 2 : Relation rel;
396 : :
397 : 2 : rel = table_open(rte->relid, AccessShareLock);
398 : 2 : result = currtid_internal(rel, tid);
399 : 2 : table_close(rel, AccessShareLock);
400 : 2 : return result;
401 : 2 : }
402 : 0 : }
403 [ + - ]: 2 : }
404 : 0 : break;
405 : 2 : }
406 : 0 : }
407 [ # # # # ]: 0 : elog(ERROR, "currtid cannot handle this view");
408 : 0 : return NULL;
409 : 2 : }
410 : :
411 : : /*
412 : : * currtid_byrelname
413 : : * Get the latest tuple version of the tuple pointing at a CTID, for a
414 : : * given relation name.
415 : : */
416 : : Datum
417 : 9 : currtid_byrelname(PG_FUNCTION_ARGS)
418 : : {
419 : 9 : text *relname = PG_GETARG_TEXT_PP(0);
420 : 9 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
421 : 9 : ItemPointer result;
422 : 9 : RangeVar *relrv;
423 : 9 : Relation rel;
424 : :
425 : 9 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
426 : 9 : rel = table_openrv(relrv, AccessShareLock);
427 : :
428 : : /* grab the latest tuple version associated to this CTID */
429 : 9 : result = currtid_internal(rel, tid);
430 : :
431 : 9 : table_close(rel, AccessShareLock);
432 : :
433 : 18 : PG_RETURN_ITEMPOINTER(result);
434 : 9 : }
|