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-2023, 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/heapam.h"
24 : #include "access/sysattr.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/builtins.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 ECB : /* ----------------------------------------------------------------
48 : * tidin
49 : * ----------------------------------------------------------------
50 : */
51 : Datum
52 GIC 2432 : tidin(PG_FUNCTION_ARGS)
53 : {
54 2432 : char *str = PG_GETARG_CSTRING(0);
55 GNC 2432 : Node *escontext = fcinfo->context;
56 : char *p,
57 : *coord[NTIDARGS];
58 : int i;
59 : ItemPointer result;
60 : BlockNumber blockNumber;
61 ECB : OffsetNumber offsetNumber;
62 : char *badp;
63 : unsigned long cvt;
64 :
65 CBC 12344 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
66 9912 : if (*p == DELIM || (*p == LDELIM && i == 0))
67 GIC 4858 : coord[i++] = p + 1;
68 :
69 2432 : if (i < NTIDARGS)
70 GNC 6 : ereturn(escontext, (Datum) 0,
71 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
72 : errmsg("invalid input syntax for type %s: \"%s\"",
73 : "tid", str)));
74 EUB :
75 GIC 2426 : errno = 0;
76 2426 : cvt = strtoul(coord[0], &badp, 10);
77 2426 : if (errno || *badp != DELIM)
78 UNC 0 : ereturn(escontext, (Datum) 0,
79 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80 : errmsg("invalid input syntax for type %s: \"%s\"",
81 : "tid", str)));
82 GIC 2426 : blockNumber = (BlockNumber) cvt;
83 :
84 : /*
85 : * Cope with possibility that unsigned long is wider than BlockNumber, in
86 ECB : * which case strtoul will not raise an error for some values that are out
87 : * of the range of BlockNumber. (See similar code in oidin().)
88 : */
89 : #if SIZEOF_LONG > 4
90 GIC 2426 : if (cvt != (unsigned long) blockNumber &&
91 6 : cvt != (unsigned long) ((int32) blockNumber))
92 GNC 3 : ereturn(escontext, (Datum) 0,
93 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
94 ECB : errmsg("invalid input syntax for type %s: \"%s\"",
95 : "tid", str)));
96 : #endif
97 :
98 GIC 2423 : cvt = strtoul(coord[1], &badp, 10);
99 2423 : if (errno || *badp != RDELIM ||
100 : cvt > USHRT_MAX)
101 GNC 9 : ereturn(escontext, (Datum) 0,
102 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
103 ECB : errmsg("invalid input syntax for type %s: \"%s\"",
104 : "tid", str)));
105 CBC 2414 : offsetNumber = (OffsetNumber) cvt;
106 :
107 2414 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
108 :
109 GIC 2414 : ItemPointerSet(result, blockNumber, offsetNumber);
110 :
111 2414 : PG_RETURN_ITEMPOINTER(result);
112 : }
113 :
114 : /* ----------------------------------------------------------------
115 ECB : * tidout
116 : * ----------------------------------------------------------------
117 : */
118 : Datum
119 GIC 2816 : tidout(PG_FUNCTION_ARGS)
120 : {
121 2816 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
122 ECB : BlockNumber blockNumber;
123 : OffsetNumber offsetNumber;
124 : char buf[32];
125 :
126 CBC 2816 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
127 GIC 2816 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
128 ECB :
129 : /* Perhaps someday we should output this as a record. */
130 GIC 2816 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
131 :
132 2816 : PG_RETURN_CSTRING(pstrdup(buf));
133 : }
134 :
135 EUB : /*
136 : * tidrecv - converts external binary format to tid
137 : */
138 : Datum
139 UIC 0 : tidrecv(PG_FUNCTION_ARGS)
140 : {
141 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
142 EUB : ItemPointer result;
143 : BlockNumber blockNumber;
144 : OffsetNumber offsetNumber;
145 :
146 UIC 0 : blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
147 UBC 0 : offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
148 :
149 0 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
150 :
151 UIC 0 : ItemPointerSet(result, blockNumber, offsetNumber);
152 :
153 0 : PG_RETURN_ITEMPOINTER(result);
154 : }
155 :
156 EUB : /*
157 : * tidsend - converts tid to binary format
158 : */
159 : Datum
160 UIC 0 : tidsend(PG_FUNCTION_ARGS)
161 EUB : {
162 UBC 0 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
163 EUB : StringInfoData buf;
164 :
165 UIC 0 : pq_begintypsend(&buf);
166 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
167 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
168 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
169 : }
170 :
171 : /*****************************************************************************
172 ECB : * PUBLIC ROUTINES *
173 : *****************************************************************************/
174 :
175 : Datum
176 GIC 9206383 : tideq(PG_FUNCTION_ARGS)
177 ECB : {
178 GIC 9206383 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
179 9206383 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
180 :
181 CBC 9206383 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
182 : }
183 ECB :
184 : Datum
185 GIC 111 : tidne(PG_FUNCTION_ARGS)
186 ECB : {
187 GIC 111 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
188 111 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
189 :
190 CBC 111 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
191 : }
192 ECB :
193 : Datum
194 GIC 41550 : tidlt(PG_FUNCTION_ARGS)
195 ECB : {
196 GIC 41550 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
197 41550 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
198 :
199 CBC 41550 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
200 : }
201 ECB :
202 : Datum
203 GIC 1899 : tidle(PG_FUNCTION_ARGS)
204 ECB : {
205 GIC 1899 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
206 1899 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
207 :
208 CBC 1899 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
209 : }
210 ECB :
211 : Datum
212 GIC 2680 : tidgt(PG_FUNCTION_ARGS)
213 ECB : {
214 GIC 2680 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
215 2680 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
216 :
217 CBC 2680 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
218 : }
219 ECB :
220 : Datum
221 GIC 1866 : tidge(PG_FUNCTION_ARGS)
222 ECB : {
223 GIC 1866 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
224 1866 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
225 :
226 CBC 1866 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
227 : }
228 ECB :
229 : Datum
230 GIC 1471428 : bttidcmp(PG_FUNCTION_ARGS)
231 ECB : {
232 GIC 1471428 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
233 1471428 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
234 :
235 CBC 1471428 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
236 : }
237 ECB :
238 : Datum
239 GIC 3 : tidlarger(PG_FUNCTION_ARGS)
240 ECB : {
241 GIC 3 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
242 3 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
243 :
244 CBC 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
245 : }
246 ECB :
247 : Datum
248 GIC 3 : tidsmaller(PG_FUNCTION_ARGS)
249 ECB : {
250 GIC 3 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
251 3 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
252 :
253 CBC 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
254 : }
255 ECB :
256 : Datum
257 GIC 63030 : hashtid(PG_FUNCTION_ARGS)
258 : {
259 63030 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
260 :
261 : /*
262 : * While you'll probably have a lot of trouble with a compiler that
263 ECB : * 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 GIC 63030 : return hash_any((unsigned char *) key,
268 EUB : sizeof(BlockIdData) + sizeof(OffsetNumber));
269 : }
270 :
271 : Datum
272 UIC 0 : hashtidextended(PG_FUNCTION_ARGS)
273 : {
274 UBC 0 : ItemPointer key = PG_GETARG_ITEMPOINTER(0);
275 UIC 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 ECB : * Returns the latest version of a tuple pointing at "tid" for
293 : * relation "rel".
294 : */
295 : static ItemPointer
296 GIC 30 : currtid_internal(Relation rel, ItemPointer tid)
297 : {
298 : ItemPointer result;
299 ECB : AclResult aclresult;
300 : Snapshot snapshot;
301 : TableScanDesc scan;
302 :
303 CBC 30 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
304 EUB :
305 GBC 30 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
306 : ACL_SELECT);
307 CBC 30 : if (aclresult != ACLCHECK_OK)
308 LBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
309 UIC 0 : RelationGetRelationName(rel));
310 ECB :
311 CBC 30 : if (rel->rd_rel->relkind == RELKIND_VIEW)
312 GIC 12 : return currtid_for_view(rel, tid);
313 :
314 18 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
315 CBC 3 : elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
316 : get_namespace_name(RelationGetNamespace(rel)),
317 ECB : RelationGetRelationName(rel));
318 :
319 CBC 15 : ItemPointerCopy(tid, result);
320 ECB :
321 CBC 15 : snapshot = RegisterSnapshot(GetLatestSnapshot());
322 GIC 15 : scan = table_beginscan_tid(rel, snapshot);
323 CBC 15 : table_tuple_get_latest_tid(scan, result);
324 GIC 9 : table_endscan(scan);
325 9 : UnregisterSnapshot(snapshot);
326 :
327 9 : return result;
328 : }
329 :
330 : /*
331 : * Handle CTIDs of views.
332 ECB : * CTID should be defined in the view and it must
333 : * correspond to the CTID of a base relation.
334 : */
335 : static ItemPointer
336 GIC 12 : currtid_for_view(Relation viewrel, ItemPointer tid)
337 : {
338 CBC 12 : TupleDesc att = RelationGetDescr(viewrel);
339 ECB : RuleLock *rulelock;
340 : RewriteRule *rewrite;
341 : int i,
342 GIC 12 : natts = att->natts,
343 CBC 12 : tididx = -1;
344 :
345 15 : for (i = 0; i < natts; i++)
346 : {
347 12 : Form_pg_attribute attr = TupleDescAttr(att, i);
348 ECB :
349 CBC 12 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
350 ECB : {
351 GIC 9 : if (attr->atttypid != TIDOID)
352 3 : elog(ERROR, "ctid isn't of type TID");
353 CBC 6 : tididx = i;
354 6 : break;
355 ECB : }
356 : }
357 GBC 9 : if (tididx < 0)
358 CBC 3 : elog(ERROR, "currtid cannot handle views with no CTID");
359 GIC 6 : rulelock = viewrel->rd_rules;
360 CBC 6 : if (!rulelock)
361 LBC 0 : elog(ERROR, "the view has no rules");
362 GIC 6 : for (i = 0; i < rulelock->numLocks; i++)
363 : {
364 6 : rewrite = rulelock->rules[i];
365 6 : if (rewrite->event == CMD_SELECT)
366 ECB : {
367 EUB : Query *query;
368 ECB : TargetEntry *tle;
369 :
370 CBC 6 : if (list_length(rewrite->actions) != 1)
371 UIC 0 : elog(ERROR, "only one select rule is allowed in views");
372 CBC 6 : query = (Query *) linitial(rewrite->actions);
373 GIC 6 : tle = get_tle_by_resno(query->targetList, tididx + 1);
374 6 : if (tle && tle->expr && IsA(tle->expr, Var))
375 ECB : {
376 CBC 6 : Var *var = (Var *) tle->expr;
377 : RangeTblEntry *rte;
378 ECB :
379 CBC 6 : if (!IS_SPECIAL_VARNO(var->varno) &&
380 GIC 6 : var->varattno == SelfItemPointerAttributeNumber)
381 : {
382 6 : rte = rt_fetch(var->varno, query->rtable);
383 6 : if (rte)
384 ECB : {
385 : ItemPointer result;
386 : Relation rel;
387 :
388 GIC 6 : rel = table_open(rte->relid, AccessShareLock);
389 6 : result = currtid_internal(rel, tid);
390 3 : table_close(rel, AccessShareLock);
391 GBC 3 : return result;
392 : }
393 : }
394 EUB : }
395 UIC 0 : break;
396 : }
397 : }
398 0 : elog(ERROR, "currtid cannot handle this view");
399 : return NULL;
400 : }
401 :
402 : /*
403 : * currtid_byrelname
404 ECB : * Get the latest tuple version of the tuple pointing at a CTID, for a
405 : * given relation name.
406 : */
407 : Datum
408 GIC 27 : currtid_byrelname(PG_FUNCTION_ARGS)
409 : {
410 27 : text *relname = PG_GETARG_TEXT_PP(0);
411 27 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
412 ECB : ItemPointer result;
413 : RangeVar *relrv;
414 : Relation rel;
415 :
416 CBC 27 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
417 GIC 27 : rel = table_openrv(relrv, AccessShareLock);
418 ECB :
419 : /* grab the latest tuple version associated to this CTID */
420 CBC 24 : result = currtid_internal(rel, tid);
421 :
422 GIC 9 : table_close(rel, AccessShareLock);
423 :
424 9 : PG_RETURN_ITEMPOINTER(result);
425 : }
|