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 : /* ----------------------------------------------------------------
48 : * tidin
49 : * ----------------------------------------------------------------
50 : */
51 : Datum
52 4864 : tidin(PG_FUNCTION_ARGS)
53 : {
54 4864 : char *str = PG_GETARG_CSTRING(0);
55 4864 : 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 :
65 24688 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
66 19824 : if (*p == DELIM || (*p == LDELIM && i == 0))
67 9716 : coord[i++] = p + 1;
68 :
69 4864 : if (i < NTIDARGS)
70 12 : ereturn(escontext, (Datum) 0,
71 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
72 : errmsg("invalid input syntax for type %s: \"%s\"",
73 : "tid", str)));
74 :
75 4852 : errno = 0;
76 4852 : cvt = strtoul(coord[0], &badp, 10);
77 4852 : if (errno || *badp != DELIM)
78 0 : ereturn(escontext, (Datum) 0,
79 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
80 : errmsg("invalid input syntax for type %s: \"%s\"",
81 : "tid", str)));
82 4852 : 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 oidin().)
88 : */
89 : #if SIZEOF_LONG > 4
90 4852 : if (cvt != (unsigned long) blockNumber &&
91 12 : cvt != (unsigned long) ((int32) blockNumber))
92 6 : 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 :
98 4846 : cvt = strtoul(coord[1], &badp, 10);
99 4846 : if (errno || *badp != RDELIM ||
100 : cvt > USHRT_MAX)
101 18 : ereturn(escontext, (Datum) 0,
102 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
103 : errmsg("invalid input syntax for type %s: \"%s\"",
104 : "tid", str)));
105 4828 : offsetNumber = (OffsetNumber) cvt;
106 :
107 4828 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
108 :
109 4828 : ItemPointerSet(result, blockNumber, offsetNumber);
110 :
111 4828 : PG_RETURN_ITEMPOINTER(result);
112 : }
113 :
114 : /* ----------------------------------------------------------------
115 : * tidout
116 : * ----------------------------------------------------------------
117 : */
118 : Datum
119 5752 : tidout(PG_FUNCTION_ARGS)
120 : {
121 5752 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
122 : BlockNumber blockNumber;
123 : OffsetNumber offsetNumber;
124 : char buf[32];
125 :
126 5752 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
127 5752 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
128 :
129 : /* Perhaps someday we should output this as a record. */
130 5752 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
131 :
132 5752 : PG_RETURN_CSTRING(pstrdup(buf));
133 : }
134 :
135 : /*
136 : * tidrecv - converts external binary format to tid
137 : */
138 : Datum
139 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);
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 : * PUBLIC ROUTINES *
173 : *****************************************************************************/
174 :
175 : Datum
176 18381172 : tideq(PG_FUNCTION_ARGS)
177 : {
178 18381172 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
179 18381172 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
180 :
181 18381172 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
182 : }
183 :
184 : Datum
185 222 : tidne(PG_FUNCTION_ARGS)
186 : {
187 222 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
188 222 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
189 :
190 222 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
191 : }
192 :
193 : Datum
194 14608 : tidlt(PG_FUNCTION_ARGS)
195 : {
196 14608 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
197 14608 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
198 :
199 14608 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
200 : }
201 :
202 : Datum
203 3798 : tidle(PG_FUNCTION_ARGS)
204 : {
205 3798 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
206 3798 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
207 :
208 3798 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
209 : }
210 :
211 : Datum
212 5356 : tidgt(PG_FUNCTION_ARGS)
213 : {
214 5356 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
215 5356 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
216 :
217 5356 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
218 : }
219 :
220 : Datum
221 3732 : tidge(PG_FUNCTION_ARGS)
222 : {
223 3732 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
224 3732 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
225 :
226 3732 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
227 : }
228 :
229 : Datum
230 2945732 : bttidcmp(PG_FUNCTION_ARGS)
231 : {
232 2945732 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
233 2945732 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
234 :
235 2945732 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
236 : }
237 :
238 : Datum
239 6 : tidlarger(PG_FUNCTION_ARGS)
240 : {
241 6 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
242 6 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
243 :
244 6 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
245 : }
246 :
247 : Datum
248 6 : tidsmaller(PG_FUNCTION_ARGS)
249 : {
250 6 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
251 6 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
252 :
253 6 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
254 : }
255 :
256 : Datum
257 126060 : hashtid(PG_FUNCTION_ARGS)
258 : {
259 126060 : 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 126060 : return hash_any((unsigned char *) key,
268 : sizeof(BlockIdData) + sizeof(OffsetNumber));
269 : }
270 :
271 : Datum
272 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
296 60 : currtid_internal(Relation rel, ItemPointer tid)
297 : {
298 : ItemPointer result;
299 : AclResult aclresult;
300 : Snapshot snapshot;
301 : TableScanDesc scan;
302 :
303 60 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
304 :
305 60 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
306 : ACL_SELECT);
307 60 : if (aclresult != ACLCHECK_OK)
308 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
309 0 : RelationGetRelationName(rel));
310 :
311 60 : if (rel->rd_rel->relkind == RELKIND_VIEW)
312 24 : return currtid_for_view(rel, tid);
313 :
314 36 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
315 6 : elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
316 : get_namespace_name(RelationGetNamespace(rel)),
317 : RelationGetRelationName(rel));
318 :
319 30 : ItemPointerCopy(tid, result);
320 :
321 30 : snapshot = RegisterSnapshot(GetLatestSnapshot());
322 30 : scan = table_beginscan_tid(rel, snapshot);
323 30 : table_tuple_get_latest_tid(scan, result);
324 18 : table_endscan(scan);
325 18 : UnregisterSnapshot(snapshot);
326 :
327 18 : return result;
328 : }
329 :
330 : /*
331 : * Handle CTIDs of views.
332 : * CTID should be defined in the view and it must
333 : * correspond to the CTID of a base relation.
334 : */
335 : static ItemPointer
336 24 : currtid_for_view(Relation viewrel, ItemPointer tid)
337 : {
338 24 : TupleDesc att = RelationGetDescr(viewrel);
339 : RuleLock *rulelock;
340 : RewriteRule *rewrite;
341 : int i,
342 24 : natts = att->natts,
343 24 : tididx = -1;
344 :
345 30 : for (i = 0; i < natts; i++)
346 : {
347 24 : Form_pg_attribute attr = TupleDescAttr(att, i);
348 :
349 24 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
350 : {
351 18 : if (attr->atttypid != TIDOID)
352 6 : elog(ERROR, "ctid isn't of type TID");
353 12 : tididx = i;
354 12 : break;
355 : }
356 : }
357 18 : if (tididx < 0)
358 6 : elog(ERROR, "currtid cannot handle views with no CTID");
359 12 : rulelock = viewrel->rd_rules;
360 12 : if (!rulelock)
361 0 : elog(ERROR, "the view has no rules");
362 12 : for (i = 0; i < rulelock->numLocks; i++)
363 : {
364 12 : rewrite = rulelock->rules[i];
365 12 : if (rewrite->event == CMD_SELECT)
366 : {
367 : Query *query;
368 : TargetEntry *tle;
369 :
370 12 : if (list_length(rewrite->actions) != 1)
371 0 : elog(ERROR, "only one select rule is allowed in views");
372 12 : query = (Query *) linitial(rewrite->actions);
373 12 : tle = get_tle_by_resno(query->targetList, tididx + 1);
374 12 : if (tle && tle->expr && IsA(tle->expr, Var))
375 : {
376 12 : Var *var = (Var *) tle->expr;
377 : RangeTblEntry *rte;
378 :
379 12 : if (!IS_SPECIAL_VARNO(var->varno) &&
380 12 : var->varattno == SelfItemPointerAttributeNumber)
381 : {
382 12 : rte = rt_fetch(var->varno, query->rtable);
383 12 : if (rte)
384 : {
385 : ItemPointer result;
386 : Relation rel;
387 :
388 12 : rel = table_open(rte->relid, AccessShareLock);
389 12 : result = currtid_internal(rel, tid);
390 6 : table_close(rel, AccessShareLock);
391 6 : return result;
392 : }
393 : }
394 : }
395 0 : break;
396 : }
397 : }
398 0 : elog(ERROR, "currtid cannot handle this view");
399 : return NULL;
400 : }
401 :
402 : /*
403 : * currtid_byrelname
404 : * Get the latest tuple version of the tuple pointing at a CTID, for a
405 : * given relation name.
406 : */
407 : Datum
408 54 : currtid_byrelname(PG_FUNCTION_ARGS)
409 : {
410 54 : text *relname = PG_GETARG_TEXT_PP(0);
411 54 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
412 : ItemPointer result;
413 : RangeVar *relrv;
414 : Relation rel;
415 :
416 54 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
417 54 : rel = table_openrv(relrv, AccessShareLock);
418 :
419 : /* grab the latest tuple version associated to this CTID */
420 48 : result = currtid_internal(rel, tid);
421 :
422 18 : table_close(rel, AccessShareLock);
423 :
424 18 : PG_RETURN_ITEMPOINTER(result);
425 : }
|