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