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
9355 tgl@sss.pgh.pa.us 51 :CBC 5554 : tidin(PG_FUNCTION_ARGS)
52 : : {
53 : 5554 : char *str = PG_GETARG_CSTRING(0);
1187 54 : 5554 : 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 : :
10416 bruce@momjian.us 64 [ + - + + : 26870 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
1473 tgl@sss.pgh.pa.us 65 [ + + + + : 21316 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
10416 bruce@momjian.us 66 : 11102 : coord[i++] = p + 1;
67 : :
9652 inoue@tpf.co.jp 68 [ + + ]: 5554 : if (i < NTIDARGS)
1187 tgl@sss.pgh.pa.us 69 [ + + ]: 6 : ereturn(escontext, (Datum) 0,
70 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
71 : : errmsg("invalid input syntax for type %s: \"%s\"",
72 : : "tid", str)));
73 : :
8643 bruce@momjian.us 74 : 5548 : errno = 0;
1473 tgl@sss.pgh.pa.us 75 : 5548 : cvt = strtoul(coord[0], &badp, 10);
8643 bruce@momjian.us 76 [ + - - + ]: 5548 : if (errno || *badp != DELIM)
1187 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)));
1473 tgl@sss.pgh.pa.us 81 :CBC 5548 : 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 [ + + ]: 5548 : if (cvt != (unsigned long) blockNumber &&
90 [ + + ]: 6 : cvt != (unsigned long) ((int32) blockNumber))
1187 91 [ + - ]: 3 : 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 : :
1473 97 : 5545 : cvt = strtoul(coord[1], &badp, 10);
8643 bruce@momjian.us 98 [ + - + - : 5545 : if (errno || *badp != RDELIM ||
+ + ]
99 : : cvt > USHRT_MAX)
1187 tgl@sss.pgh.pa.us 100 [ + + ]: 9 : ereturn(escontext, (Datum) 0,
101 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
102 : : errmsg("invalid input syntax for type %s: \"%s\"",
103 : : "tid", str)));
1473 104 : 5536 : offsetNumber = (OffsetNumber) cvt;
105 : :
95 michael@paquier.xyz 106 :GNC 5536 : result = (ItemPointer) palloc_object(ItemPointerData);
107 : :
10416 bruce@momjian.us 108 :CBC 5536 : ItemPointerSet(result, blockNumber, offsetNumber);
109 : :
9355 tgl@sss.pgh.pa.us 110 : 5536 : PG_RETURN_ITEMPOINTER(result);
111 : : }
112 : :
113 : : /* ----------------------------------------------------------------
114 : : * tidout
115 : : * ----------------------------------------------------------------
116 : : */
117 : : Datum
118 : 24316 : tidout(PG_FUNCTION_ARGS)
119 : : {
9124 bruce@momjian.us 120 : 24316 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
121 : : BlockNumber blockNumber;
122 : : OffsetNumber offsetNumber;
123 : : char buf[32];
124 : :
3274 alvherre@alvh.no-ip. 125 : 24316 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
126 : 24316 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
127 : :
128 : : /* Perhaps someday we should output this as a record. */
8600 bruce@momjian.us 129 : 24316 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
130 : :
9355 tgl@sss.pgh.pa.us 131 : 24316 : PG_RETURN_CSTRING(pstrdup(buf));
132 : : }
133 : :
134 : : /*
135 : : * tidrecv - converts external binary format to tid
136 : : */
137 : : Datum
8343 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 : :
95 michael@paquier.xyz 148 :UNC 0 : result = (ItemPointer) palloc_object(ItemPointerData);
149 : :
8343 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);
3077 andres@anarazel.de 165 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
166 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
8343 tgl@sss.pgh.pa.us 167 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
168 : : }
169 : :
170 : : /*****************************************************************************
171 : : * PUBLIC ROUTINES *
172 : : *****************************************************************************/
173 : :
174 : : Datum
9355 tgl@sss.pgh.pa.us 175 :CBC 9206873 : tideq(PG_FUNCTION_ARGS)
176 : : {
9124 bruce@momjian.us 177 : 9206873 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
178 : 9206873 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
179 : :
7102 180 : 9206873 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
181 : : }
182 : :
183 : : Datum
9355 tgl@sss.pgh.pa.us 184 : 114 : tidne(PG_FUNCTION_ARGS)
185 : : {
9124 bruce@momjian.us 186 : 114 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
187 : 114 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
188 : :
7102 189 : 114 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
190 : : }
191 : :
192 : : Datum
7177 tgl@sss.pgh.pa.us 193 : 41589 : tidlt(PG_FUNCTION_ARGS)
194 : : {
195 : 41589 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
196 : 41589 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
197 : :
7102 bruce@momjian.us 198 : 41589 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
199 : : }
200 : :
201 : : Datum
7177 tgl@sss.pgh.pa.us 202 : 1899 : tidle(PG_FUNCTION_ARGS)
203 : : {
204 : 1899 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
205 : 1899 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
206 : :
7102 bruce@momjian.us 207 : 1899 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
208 : : }
209 : :
210 : : Datum
7177 tgl@sss.pgh.pa.us 211 : 2678 : tidgt(PG_FUNCTION_ARGS)
212 : : {
213 : 2678 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
214 : 2678 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
215 : :
7102 bruce@momjian.us 216 : 2678 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
217 : : }
218 : :
219 : : Datum
7177 tgl@sss.pgh.pa.us 220 : 1866 : tidge(PG_FUNCTION_ARGS)
221 : : {
222 : 1866 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
223 : 1866 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
224 : :
7102 bruce@momjian.us 225 : 1866 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
226 : : }
227 : :
228 : : Datum
7177 tgl@sss.pgh.pa.us 229 : 1473604 : bttidcmp(PG_FUNCTION_ARGS)
230 : : {
231 : 1473604 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
232 : 1473604 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
233 : :
7142 234 : 1473604 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
235 : : }
236 : :
237 : : Datum
7177 238 : 600 : tidlarger(PG_FUNCTION_ARGS)
239 : : {
240 : 600 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
241 : 600 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
242 : :
7102 bruce@momjian.us 243 [ - + ]: 600 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
244 : : }
245 : :
246 : : Datum
7177 tgl@sss.pgh.pa.us 247 : 600 : tidsmaller(PG_FUNCTION_ARGS)
248 : : {
249 : 600 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
250 : 600 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
251 : :
7102 bruce@momjian.us 252 [ + - ]: 600 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
253 : : }
254 : :
255 : : Datum
2632 tgl@sss.pgh.pa.us 256 : 63040 : hashtid(PG_FUNCTION_ARGS)
257 : : {
258 : 63040 : 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 : 63040 : return hash_any((unsigned char *) key,
267 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
268 : : }
269 : :
270 : : Datum
2632 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 : : /*
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
136 peter@eisentraut.org 295 :GNC 30 : currtid_internal(Relation rel, const ItemPointerData *tid)
296 : : {
297 : : ItemPointer result;
298 : : AclResult aclresult;
299 : : Snapshot snapshot;
300 : : TableScanDesc scan;
301 : :
95 michael@paquier.xyz 302 : 30 : result = (ItemPointer) palloc_object(ItemPointerData);
303 : :
1936 michael@paquier.xyz 304 :CBC 30 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
305 : : ACL_SELECT);
306 [ - + ]: 30 : if (aclresult != ACLCHECK_OK)
1936 michael@paquier.xyz 307 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
308 : 0 : RelationGetRelationName(rel));
309 : :
1936 michael@paquier.xyz 310 [ + + ]:CBC 30 : if (rel->rd_rel->relkind == RELKIND_VIEW)
311 : 12 : return currtid_for_view(rel, tid);
312 : :
313 [ + + + - : 18 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ + + - +
+ ]
619 314 [ + - ]: 3 : 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 : :
1936 320 : 15 : ItemPointerCopy(tid, result);
321 : :
322 : 15 : snapshot = RegisterSnapshot(GetLatestSnapshot());
323 : 15 : scan = table_beginscan_tid(rel, snapshot);
324 : 15 : table_tuple_get_latest_tid(scan, result);
325 : 9 : table_endscan(scan);
326 : 9 : UnregisterSnapshot(snapshot);
327 : :
328 : 9 : return result;
329 : : }
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
136 peter@eisentraut.org 337 :GNC 12 : currtid_for_view(Relation viewrel, const ItemPointerData *tid)
338 : : {
8698 inoue@tpf.co.jp 339 :CBC 12 : TupleDesc att = RelationGetDescr(viewrel);
340 : : RuleLock *rulelock;
341 : : RewriteRule *rewrite;
342 : : int i,
8593 bruce@momjian.us 343 : 12 : natts = att->natts,
344 : 12 : tididx = -1;
345 : :
346 [ + + ]: 15 : for (i = 0; i < natts; i++)
347 : : {
3129 andres@anarazel.de 348 : 12 : Form_pg_attribute attr = TupleDescAttr(att, i);
349 : :
350 [ + + ]: 12 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
351 : : {
352 [ + + ]: 9 : if (attr->atttypid != TIDOID)
619 michael@paquier.xyz 353 [ + - ]: 3 : ereport(ERROR,
354 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
355 : : errmsg("ctid isn't of type TID"));
8698 inoue@tpf.co.jp 356 : 6 : tididx = i;
8252 tgl@sss.pgh.pa.us 357 : 6 : break;
358 : : }
359 : : }
8698 inoue@tpf.co.jp 360 [ + + ]: 9 : if (tididx < 0)
619 michael@paquier.xyz 361 [ + - ]: 3 : ereport(ERROR,
362 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
363 : : errmsg("currtid cannot handle views with no CTID"));
8267 tgl@sss.pgh.pa.us 364 : 6 : rulelock = viewrel->rd_rules;
365 [ - + ]: 6 : if (!rulelock)
619 michael@paquier.xyz 366 [ # # ]:UBC 0 : ereport(ERROR,
367 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
368 : : errmsg("the view has no rules"));
8698 inoue@tpf.co.jp 369 [ + - ]:CBC 6 : for (i = 0; i < rulelock->numLocks; i++)
370 : : {
371 : 6 : rewrite = rulelock->rules[i];
372 [ + - ]: 6 : if (rewrite->event == CMD_SELECT)
373 : : {
374 : : Query *query;
375 : : TargetEntry *tle;
376 : :
7959 neilc@samurai.com 377 [ - + ]: 6 : if (list_length(rewrite->actions) != 1)
619 michael@paquier.xyz 378 [ # # ]:UBC 0 : ereport(ERROR,
379 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
380 : : errmsg("only one select rule is allowed in views"));
7963 neilc@samurai.com 381 :CBC 6 : query = (Query *) linitial(rewrite->actions);
7868 bruce@momjian.us 382 : 6 : tle = get_tle_by_resno(query->targetList, tididx + 1);
8267 tgl@sss.pgh.pa.us 383 [ + - + - : 6 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
384 : : {
8593 bruce@momjian.us 385 : 6 : Var *var = (Var *) tle->expr;
386 : : RangeTblEntry *rte;
387 : :
5269 tgl@sss.pgh.pa.us 388 [ + - ]: 6 : if (!IS_SPECIAL_VARNO(var->varno) &&
8267 389 [ + - ]: 6 : var->varattno == SelfItemPointerAttributeNumber)
390 : : {
8252 391 : 6 : rte = rt_fetch(var->varno, query->rtable);
8698 inoue@tpf.co.jp 392 [ + - ]: 6 : if (rte)
393 : : {
394 : : ItemPointer result;
395 : : Relation rel;
396 : :
1936 michael@paquier.xyz 397 : 6 : rel = table_open(rte->relid, AccessShareLock);
398 : 6 : result = currtid_internal(rel, tid);
399 : 3 : table_close(rel, AccessShareLock);
2113 400 : 3 : return result;
401 : : }
402 : : }
403 : : }
8698 inoue@tpf.co.jp 404 :UBC 0 : break;
405 : : }
406 : : }
8267 tgl@sss.pgh.pa.us 407 [ # # ]: 0 : elog(ERROR, "currtid cannot handle this view");
408 : : return NULL;
409 : : }
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
9410 tgl@sss.pgh.pa.us 417 :CBC 27 : currtid_byrelname(PG_FUNCTION_ARGS)
418 : : {
3290 noah@leadboat.com 419 : 27 : text *relname = PG_GETARG_TEXT_PP(0);
9124 bruce@momjian.us 420 : 27 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
421 : : ItemPointer result;
422 : : RangeVar *relrv;
423 : : Relation rel;
424 : :
7597 neilc@samurai.com 425 : 27 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
2610 andres@anarazel.de 426 : 27 : rel = table_openrv(relrv, AccessShareLock);
427 : :
428 : : /* grab the latest tuple version associated to this CTID */
1936 michael@paquier.xyz 429 : 24 : result = currtid_internal(rel, tid);
430 : :
2610 andres@anarazel.de 431 : 9 : table_close(rel, AccessShareLock);
432 : :
9355 tgl@sss.pgh.pa.us 433 : 9 : PG_RETURN_ITEMPOINTER(result);
434 : : }
|