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-2025, 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 <math.h>
21 : : #include <limits.h>
22 : :
23 : : #include "access/sysattr.h"
24 : : #include "access/table.h"
25 : : #include "access/tableam.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, ItemPointer tid);
46 : :
47 : : /* ----------------------------------------------------------------
48 : : * tidin
49 : : * ----------------------------------------------------------------
50 : : */
51 : : Datum
9165 tgl@sss.pgh.pa.us 52 :CBC 5493 : tidin(PG_FUNCTION_ARGS)
53 : : {
54 : 5493 : char *str = PG_GETARG_CSTRING(0);
997 55 : 5493 : 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 : :
10226 bruce@momjian.us 65 [ + - + + : 26601 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
1283 tgl@sss.pgh.pa.us 66 [ + + + + : 21108 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
10226 bruce@momjian.us 67 : 10980 : coord[i++] = p + 1;
68 : :
9462 inoue@tpf.co.jp 69 [ + + ]: 5493 : if (i < NTIDARGS)
997 tgl@sss.pgh.pa.us 70 [ + + ]: 6 : ereturn(escontext, (Datum) 0,
71 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
72 : : errmsg("invalid input syntax for type %s: \"%s\"",
73 : : "tid", str)));
74 : :
8453 bruce@momjian.us 75 : 5487 : errno = 0;
1283 tgl@sss.pgh.pa.us 76 : 5487 : cvt = strtoul(coord[0], &badp, 10);
8453 bruce@momjian.us 77 [ + - - + ]: 5487 : if (errno || *badp != DELIM)
997 tgl@sss.pgh.pa.us 78 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
79 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80 : : errmsg("invalid input syntax for type %s: \"%s\"",
81 : : "tid", str)));
1283 tgl@sss.pgh.pa.us 82 :CBC 5487 : 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 [ + + ]: 5487 : if (cvt != (unsigned long) blockNumber &&
91 [ + + ]: 6 : cvt != (unsigned long) ((int32) blockNumber))
997 92 [ + - ]: 3 : 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 : :
1283 98 : 5484 : cvt = strtoul(coord[1], &badp, 10);
8453 bruce@momjian.us 99 [ + - + - : 5484 : if (errno || *badp != RDELIM ||
+ + ]
100 : : cvt > USHRT_MAX)
997 tgl@sss.pgh.pa.us 101 [ + + ]: 9 : ereturn(escontext, (Datum) 0,
102 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
103 : : errmsg("invalid input syntax for type %s: \"%s\"",
104 : : "tid", str)));
1283 105 : 5475 : offsetNumber = (OffsetNumber) cvt;
106 : :
10226 bruce@momjian.us 107 : 5475 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
108 : :
109 : 5475 : ItemPointerSet(result, blockNumber, offsetNumber);
110 : :
9165 tgl@sss.pgh.pa.us 111 : 5475 : PG_RETURN_ITEMPOINTER(result);
112 : : }
113 : :
114 : : /* ----------------------------------------------------------------
115 : : * tidout
116 : : * ----------------------------------------------------------------
117 : : */
118 : : Datum
119 : 22274 : tidout(PG_FUNCTION_ARGS)
120 : : {
8934 bruce@momjian.us 121 : 22274 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
122 : : BlockNumber blockNumber;
123 : : OffsetNumber offsetNumber;
124 : : char buf[32];
125 : :
3084 alvherre@alvh.no-ip. 126 : 22274 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
127 : 22274 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
128 : :
129 : : /* Perhaps someday we should output this as a record. */
8410 bruce@momjian.us 130 : 22274 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
131 : :
9165 tgl@sss.pgh.pa.us 132 : 22274 : PG_RETURN_CSTRING(pstrdup(buf));
133 : : }
134 : :
135 : : /*
136 : : * tidrecv - converts external binary format to tid
137 : : */
138 : : Datum
8153 tgl@sss.pgh.pa.us 139 :UBC 0 : tidrecv(PG_FUNCTION_ARGS)
140 : : {
141 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
142 : : ItemPointer result;
143 : : BlockNumber blockNumber;
144 : : OffsetNumber offsetNumber;
145 : :
146 : 0 : blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
147 : 0 : offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
148 : :
149 : 0 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
150 : :
151 : 0 : ItemPointerSet(result, blockNumber, offsetNumber);
152 : :
153 : 0 : PG_RETURN_ITEMPOINTER(result);
154 : : }
155 : :
156 : : /*
157 : : * tidsend - converts tid to binary format
158 : : */
159 : : Datum
160 : 0 : tidsend(PG_FUNCTION_ARGS)
161 : : {
162 : 0 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
163 : : StringInfoData buf;
164 : :
165 : 0 : pq_begintypsend(&buf);
2887 andres@anarazel.de 166 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
167 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
8153 tgl@sss.pgh.pa.us 168 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
169 : : }
170 : :
171 : : /*****************************************************************************
172 : : * PUBLIC ROUTINES *
173 : : *****************************************************************************/
174 : :
175 : : Datum
9165 tgl@sss.pgh.pa.us 176 :CBC 9206419 : tideq(PG_FUNCTION_ARGS)
177 : : {
8934 bruce@momjian.us 178 : 9206419 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
179 : 9206419 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
180 : :
6912 181 : 9206419 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
182 : : }
183 : :
184 : : Datum
9165 tgl@sss.pgh.pa.us 185 : 114 : tidne(PG_FUNCTION_ARGS)
186 : : {
8934 bruce@momjian.us 187 : 114 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
188 : 114 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
189 : :
6912 190 : 114 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
191 : : }
192 : :
193 : : Datum
6987 tgl@sss.pgh.pa.us 194 : 41589 : tidlt(PG_FUNCTION_ARGS)
195 : : {
196 : 41589 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
197 : 41589 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
198 : :
6912 bruce@momjian.us 199 : 41589 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
200 : : }
201 : :
202 : : Datum
6987 tgl@sss.pgh.pa.us 203 : 1899 : tidle(PG_FUNCTION_ARGS)
204 : : {
205 : 1899 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
206 : 1899 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
207 : :
6912 bruce@momjian.us 208 : 1899 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
209 : : }
210 : :
211 : : Datum
6987 tgl@sss.pgh.pa.us 212 : 2678 : tidgt(PG_FUNCTION_ARGS)
213 : : {
214 : 2678 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
215 : 2678 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
216 : :
6912 bruce@momjian.us 217 : 2678 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
218 : : }
219 : :
220 : : Datum
6987 tgl@sss.pgh.pa.us 221 : 1866 : tidge(PG_FUNCTION_ARGS)
222 : : {
223 : 1866 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
224 : 1866 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
225 : :
6912 bruce@momjian.us 226 : 1866 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
227 : : }
228 : :
229 : : Datum
6987 tgl@sss.pgh.pa.us 230 : 1473610 : bttidcmp(PG_FUNCTION_ARGS)
231 : : {
232 : 1473610 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
233 : 1473610 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
234 : :
6952 235 : 1473610 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
236 : : }
237 : :
238 : : Datum
6987 239 : 3 : tidlarger(PG_FUNCTION_ARGS)
240 : : {
241 : 3 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
242 : 3 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
243 : :
6912 bruce@momjian.us 244 [ - + ]: 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
245 : : }
246 : :
247 : : Datum
6987 tgl@sss.pgh.pa.us 248 : 3 : tidsmaller(PG_FUNCTION_ARGS)
249 : : {
250 : 3 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
251 : 3 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
252 : :
6912 bruce@momjian.us 253 [ + - ]: 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
254 : : }
255 : :
256 : : Datum
2442 tgl@sss.pgh.pa.us 257 : 63040 : hashtid(PG_FUNCTION_ARGS)
258 : : {
259 : 63040 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
260 : :
261 : : /*
262 : : * While you'll probably have a lot of trouble with a compiler that
263 : : * insists on appending pad space to struct ItemPointerData, we can at
264 : : * least make this code work, by not using sizeof(ItemPointerData).
265 : : * Instead rely on knowing the sizes of the component fields.
266 : : */
267 : 63040 : return hash_any((unsigned char *) key,
268 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
269 : : }
270 : :
271 : : Datum
2442 tgl@sss.pgh.pa.us 272 :UBC 0 : hashtidextended(PG_FUNCTION_ARGS)
273 : : {
274 : 0 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
275 : 0 : uint64 seed = PG_GETARG_INT64(1);
276 : :
277 : : /* As above */
278 : 0 : return hash_any_extended((unsigned char *) key,
279 : : sizeof(BlockIdData) + sizeof(OffsetNumber),
280 : : seed);
281 : : }
282 : :
283 : :
284 : : /*
285 : : * Functions to get latest tid of a specified tuple.
286 : : *
287 : : * Maybe these implementations should be moved to another place
288 : : */
289 : :
290 : : /*
291 : : * Utility wrapper for current CTID functions.
292 : : * Returns the latest version of a tuple pointing at "tid" for
293 : : * relation "rel".
294 : : */
295 : : static ItemPointer
1746 michael@paquier.xyz 296 :CBC 30 : currtid_internal(Relation rel, ItemPointer tid)
297 : : {
298 : : ItemPointer result;
299 : : AclResult aclresult;
300 : : Snapshot snapshot;
301 : : TableScanDesc scan;
302 : :
303 : 30 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
304 : :
305 : 30 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
306 : : ACL_SELECT);
307 [ - + ]: 30 : if (aclresult != ACLCHECK_OK)
1746 michael@paquier.xyz 308 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
309 : 0 : RelationGetRelationName(rel));
310 : :
1746 michael@paquier.xyz 311 [ + + ]:CBC 30 : if (rel->rd_rel->relkind == RELKIND_VIEW)
312 : 12 : return currtid_for_view(rel, tid);
313 : :
314 [ + + + - : 18 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ + + - +
+ ]
429 315 [ + - ]: 3 : ereport(ERROR,
316 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
317 : : errmsg("cannot look at latest visible tid for relation \"%s.%s\"",
318 : : get_namespace_name(RelationGetNamespace(rel)),
319 : : RelationGetRelationName(rel)));
320 : :
1746 321 : 15 : ItemPointerCopy(tid, result);
322 : :
323 : 15 : snapshot = RegisterSnapshot(GetLatestSnapshot());
324 : 15 : scan = table_beginscan_tid(rel, snapshot);
325 : 15 : table_tuple_get_latest_tid(scan, result);
326 : 9 : table_endscan(scan);
327 : 9 : UnregisterSnapshot(snapshot);
328 : :
329 : 9 : return result;
330 : : }
331 : :
332 : : /*
333 : : * Handle CTIDs of views.
334 : : * CTID should be defined in the view and it must
335 : : * correspond to the CTID of a base relation.
336 : : */
337 : : static ItemPointer
8403 bruce@momjian.us 338 : 12 : currtid_for_view(Relation viewrel, ItemPointer tid)
339 : : {
8508 inoue@tpf.co.jp 340 : 12 : TupleDesc att = RelationGetDescr(viewrel);
341 : : RuleLock *rulelock;
342 : : RewriteRule *rewrite;
343 : : int i,
8403 bruce@momjian.us 344 : 12 : natts = att->natts,
345 : 12 : tididx = -1;
346 : :
347 [ + + ]: 15 : for (i = 0; i < natts; i++)
348 : : {
2939 andres@anarazel.de 349 : 12 : Form_pg_attribute attr = TupleDescAttr(att, i);
350 : :
351 [ + + ]: 12 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
352 : : {
353 [ + + ]: 9 : if (attr->atttypid != TIDOID)
429 michael@paquier.xyz 354 [ + - ]: 3 : ereport(ERROR,
355 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
356 : : errmsg("ctid isn't of type TID"));
8508 inoue@tpf.co.jp 357 : 6 : tididx = i;
8062 tgl@sss.pgh.pa.us 358 : 6 : break;
359 : : }
360 : : }
8508 inoue@tpf.co.jp 361 [ + + ]: 9 : if (tididx < 0)
429 michael@paquier.xyz 362 [ + - ]: 3 : ereport(ERROR,
363 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
364 : : errmsg("currtid cannot handle views with no CTID"));
8077 tgl@sss.pgh.pa.us 365 : 6 : rulelock = viewrel->rd_rules;
366 [ - + ]: 6 : if (!rulelock)
429 michael@paquier.xyz 367 [ # # ]:UBC 0 : ereport(ERROR,
368 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
369 : : errmsg("the view has no rules"));
8508 inoue@tpf.co.jp 370 [ + - ]:CBC 6 : for (i = 0; i < rulelock->numLocks; i++)
371 : : {
372 : 6 : rewrite = rulelock->rules[i];
373 [ + - ]: 6 : if (rewrite->event == CMD_SELECT)
374 : : {
375 : : Query *query;
376 : : TargetEntry *tle;
377 : :
7769 neilc@samurai.com 378 [ - + ]: 6 : if (list_length(rewrite->actions) != 1)
429 michael@paquier.xyz 379 [ # # ]:UBC 0 : ereport(ERROR,
380 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
381 : : errmsg("only one select rule is allowed in views"));
7773 neilc@samurai.com 382 :CBC 6 : query = (Query *) linitial(rewrite->actions);
7678 bruce@momjian.us 383 : 6 : tle = get_tle_by_resno(query->targetList, tididx + 1);
8077 tgl@sss.pgh.pa.us 384 [ + - + - : 6 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
385 : : {
8403 bruce@momjian.us 386 : 6 : Var *var = (Var *) tle->expr;
387 : : RangeTblEntry *rte;
388 : :
5079 tgl@sss.pgh.pa.us 389 [ + - ]: 6 : if (!IS_SPECIAL_VARNO(var->varno) &&
8077 390 [ + - ]: 6 : var->varattno == SelfItemPointerAttributeNumber)
391 : : {
8062 392 : 6 : rte = rt_fetch(var->varno, query->rtable);
8508 inoue@tpf.co.jp 393 [ + - ]: 6 : if (rte)
394 : : {
395 : : ItemPointer result;
396 : : Relation rel;
397 : :
1746 michael@paquier.xyz 398 : 6 : rel = table_open(rte->relid, AccessShareLock);
399 : 6 : result = currtid_internal(rel, tid);
400 : 3 : table_close(rel, AccessShareLock);
1923 401 : 3 : return result;
402 : : }
403 : : }
404 : : }
8508 inoue@tpf.co.jp 405 :UBC 0 : break;
406 : : }
407 : : }
8077 tgl@sss.pgh.pa.us 408 [ # # ]: 0 : elog(ERROR, "currtid cannot handle this view");
409 : : return NULL;
410 : : }
411 : :
412 : : /*
413 : : * currtid_byrelname
414 : : * Get the latest tuple version of the tuple pointing at a CTID, for a
415 : : * given relation name.
416 : : */
417 : : Datum
9220 tgl@sss.pgh.pa.us 418 :CBC 27 : currtid_byrelname(PG_FUNCTION_ARGS)
419 : : {
3100 noah@leadboat.com 420 : 27 : text *relname = PG_GETARG_TEXT_PP(0);
8934 bruce@momjian.us 421 : 27 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
422 : : ItemPointer result;
423 : : RangeVar *relrv;
424 : : Relation rel;
425 : :
7407 neilc@samurai.com 426 : 27 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
2420 andres@anarazel.de 427 : 27 : rel = table_openrv(relrv, AccessShareLock);
428 : :
429 : : /* grab the latest tuple version associated to this CTID */
1746 michael@paquier.xyz 430 : 24 : result = currtid_internal(rel, tid);
431 : :
2420 andres@anarazel.de 432 : 9 : table_close(rel, AccessShareLock);
433 : :
9165 tgl@sss.pgh.pa.us 434 : 9 : PG_RETURN_ITEMPOINTER(result);
435 : : }
|