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-2024, 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
8655 tgl@sss.pgh.pa.us 52 :CBC 2432 : tidin(PG_FUNCTION_ARGS)
53 : : {
54 : 2432 : char *str = PG_GETARG_CSTRING(0);
487 55 : 2432 : 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 : :
9716 bruce@momjian.us 65 [ + - + + : 12344 : for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+ + ]
773 tgl@sss.pgh.pa.us 66 [ + + + + : 9912 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
9716 bruce@momjian.us 67 : 4858 : coord[i++] = p + 1;
68 : :
8952 inoue@tpf.co.jp 69 [ + + ]: 2432 : if (i < NTIDARGS)
487 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 : :
7943 bruce@momjian.us 75 : 2426 : errno = 0;
773 tgl@sss.pgh.pa.us 76 : 2426 : cvt = strtoul(coord[0], &badp, 10);
7943 bruce@momjian.us 77 [ + - - + ]: 2426 : if (errno || *badp != DELIM)
487 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)));
773 tgl@sss.pgh.pa.us 82 :CBC 2426 : 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 [ + + ]: 2426 : if (cvt != (unsigned long) blockNumber &&
91 [ + + ]: 6 : cvt != (unsigned long) ((int32) blockNumber))
487 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 : :
773 98 : 2423 : cvt = strtoul(coord[1], &badp, 10);
7943 bruce@momjian.us 99 [ + - + - : 2423 : if (errno || *badp != RDELIM ||
+ + ]
100 : : cvt > USHRT_MAX)
487 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)));
773 105 : 2414 : offsetNumber = (OffsetNumber) cvt;
106 : :
9716 bruce@momjian.us 107 : 2414 : result = (ItemPointer) palloc(sizeof(ItemPointerData));
108 : :
109 : 2414 : ItemPointerSet(result, blockNumber, offsetNumber);
110 : :
8655 tgl@sss.pgh.pa.us 111 : 2414 : PG_RETURN_ITEMPOINTER(result);
112 : : }
113 : :
114 : : /* ----------------------------------------------------------------
115 : : * tidout
116 : : * ----------------------------------------------------------------
117 : : */
118 : : Datum
119 : 3596 : tidout(PG_FUNCTION_ARGS)
120 : : {
8424 bruce@momjian.us 121 : 3596 : ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
122 : : BlockNumber blockNumber;
123 : : OffsetNumber offsetNumber;
124 : : char buf[32];
125 : :
2574 alvherre@alvh.no-ip. 126 : 3596 : blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr);
127 : 3596 : offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
128 : :
129 : : /* Perhaps someday we should output this as a record. */
7900 bruce@momjian.us 130 : 3596 : snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
131 : :
8655 tgl@sss.pgh.pa.us 132 : 3596 : PG_RETURN_CSTRING(pstrdup(buf));
133 : : }
134 : :
135 : : /*
136 : : * tidrecv - converts external binary format to tid
137 : : */
138 : : Datum
7643 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);
2377 andres@anarazel.de 166 : 0 : pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
167 : 0 : pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
7643 tgl@sss.pgh.pa.us 168 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
169 : : }
170 : :
171 : : /*****************************************************************************
172 : : * PUBLIC ROUTINES *
173 : : *****************************************************************************/
174 : :
175 : : Datum
8655 tgl@sss.pgh.pa.us 176 :CBC 9206407 : tideq(PG_FUNCTION_ARGS)
177 : : {
8424 bruce@momjian.us 178 : 9206407 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
179 : 9206407 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
180 : :
6402 181 : 9206407 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
182 : : }
183 : :
184 : : Datum
8655 tgl@sss.pgh.pa.us 185 : 114 : tidne(PG_FUNCTION_ARGS)
186 : : {
8424 bruce@momjian.us 187 : 114 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
188 : 114 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
189 : :
6402 190 : 114 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
191 : : }
192 : :
193 : : Datum
6477 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 : :
6402 bruce@momjian.us 199 : 41589 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
200 : : }
201 : :
202 : : Datum
6477 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 : :
6402 bruce@momjian.us 208 : 1899 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
209 : : }
210 : :
211 : : Datum
6477 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 : :
6402 bruce@momjian.us 217 : 2678 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
218 : : }
219 : :
220 : : Datum
6477 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 : :
6402 bruce@momjian.us 226 : 1866 : PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
227 : : }
228 : :
229 : : Datum
6477 tgl@sss.pgh.pa.us 230 : 1473585 : bttidcmp(PG_FUNCTION_ARGS)
231 : : {
232 : 1473585 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
233 : 1473585 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
234 : :
6442 235 : 1473585 : PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
236 : : }
237 : :
238 : : Datum
6477 239 : 3 : tidlarger(PG_FUNCTION_ARGS)
240 : : {
241 : 3 : ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
242 : 3 : ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
243 : :
6402 bruce@momjian.us 244 [ - + ]: 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
245 : : }
246 : :
247 : : Datum
6477 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 : :
6402 bruce@momjian.us 253 [ + - ]: 3 : PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
254 : : }
255 : :
256 : : Datum
1932 tgl@sss.pgh.pa.us 257 : 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 : : * 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 : 63030 : return hash_any((unsigned char *) key,
268 : : sizeof(BlockIdData) + sizeof(OffsetNumber));
269 : : }
270 : :
271 : : Datum
1932 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
1236 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)
1236 michael@paquier.xyz 308 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
309 : 0 : RelationGetRelationName(rel));
310 : :
1236 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))
+ + + - +
+ ]
315 [ + - ]: 3 : elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
316 : : get_namespace_name(RelationGetNamespace(rel)),
317 : : RelationGetRelationName(rel));
318 : :
319 : 15 : ItemPointerCopy(tid, result);
320 : :
321 : 15 : snapshot = RegisterSnapshot(GetLatestSnapshot());
322 : 15 : scan = table_beginscan_tid(rel, snapshot);
323 : 15 : table_tuple_get_latest_tid(scan, result);
324 : 9 : table_endscan(scan);
325 : 9 : UnregisterSnapshot(snapshot);
326 : :
327 : 9 : 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
7893 bruce@momjian.us 336 : 12 : currtid_for_view(Relation viewrel, ItemPointer tid)
337 : : {
7998 inoue@tpf.co.jp 338 : 12 : TupleDesc att = RelationGetDescr(viewrel);
339 : : RuleLock *rulelock;
340 : : RewriteRule *rewrite;
341 : : int i,
7893 bruce@momjian.us 342 : 12 : natts = att->natts,
343 : 12 : tididx = -1;
344 : :
345 [ + + ]: 15 : for (i = 0; i < natts; i++)
346 : : {
2429 andres@anarazel.de 347 : 12 : Form_pg_attribute attr = TupleDescAttr(att, i);
348 : :
349 [ + + ]: 12 : if (strcmp(NameStr(attr->attname), "ctid") == 0)
350 : : {
351 [ + + ]: 9 : if (attr->atttypid != TIDOID)
7998 inoue@tpf.co.jp 352 [ + - ]: 3 : elog(ERROR, "ctid isn't of type TID");
353 : 6 : tididx = i;
7552 tgl@sss.pgh.pa.us 354 : 6 : break;
355 : : }
356 : : }
7998 inoue@tpf.co.jp 357 [ + + ]: 9 : if (tididx < 0)
7567 tgl@sss.pgh.pa.us 358 [ + - ]: 3 : elog(ERROR, "currtid cannot handle views with no CTID");
359 : 6 : rulelock = viewrel->rd_rules;
360 [ - + ]: 6 : if (!rulelock)
7998 inoue@tpf.co.jp 361 [ # # ]:UBC 0 : elog(ERROR, "the view has no rules");
7998 inoue@tpf.co.jp 362 [ + - ]:CBC 6 : for (i = 0; i < rulelock->numLocks; i++)
363 : : {
364 : 6 : rewrite = rulelock->rules[i];
365 [ + - ]: 6 : if (rewrite->event == CMD_SELECT)
366 : : {
367 : : Query *query;
368 : : TargetEntry *tle;
369 : :
7259 neilc@samurai.com 370 [ - + ]: 6 : if (list_length(rewrite->actions) != 1)
7998 inoue@tpf.co.jp 371 [ # # ]:UBC 0 : elog(ERROR, "only one select rule is allowed in views");
7263 neilc@samurai.com 372 :CBC 6 : query = (Query *) linitial(rewrite->actions);
7168 bruce@momjian.us 373 : 6 : tle = get_tle_by_resno(query->targetList, tididx + 1);
7567 tgl@sss.pgh.pa.us 374 [ + - + - : 6 : if (tle && tle->expr && IsA(tle->expr, Var))
+ - ]
375 : : {
7893 bruce@momjian.us 376 : 6 : Var *var = (Var *) tle->expr;
377 : : RangeTblEntry *rte;
378 : :
4569 tgl@sss.pgh.pa.us 379 [ + - ]: 6 : if (!IS_SPECIAL_VARNO(var->varno) &&
7567 380 [ + - ]: 6 : var->varattno == SelfItemPointerAttributeNumber)
381 : : {
7552 382 : 6 : rte = rt_fetch(var->varno, query->rtable);
7998 inoue@tpf.co.jp 383 [ + - ]: 6 : if (rte)
384 : : {
385 : : ItemPointer result;
386 : : Relation rel;
387 : :
1236 michael@paquier.xyz 388 : 6 : rel = table_open(rte->relid, AccessShareLock);
389 : 6 : result = currtid_internal(rel, tid);
390 : 3 : table_close(rel, AccessShareLock);
1413 391 : 3 : return result;
392 : : }
393 : : }
394 : : }
7998 inoue@tpf.co.jp 395 :UBC 0 : break;
396 : : }
397 : : }
7567 tgl@sss.pgh.pa.us 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
8710 tgl@sss.pgh.pa.us 408 :CBC 27 : currtid_byrelname(PG_FUNCTION_ARGS)
409 : : {
2590 noah@leadboat.com 410 : 27 : text *relname = PG_GETARG_TEXT_PP(0);
8424 bruce@momjian.us 411 : 27 : ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
412 : : ItemPointer result;
413 : : RangeVar *relrv;
414 : : Relation rel;
415 : :
6897 neilc@samurai.com 416 : 27 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
1910 andres@anarazel.de 417 : 27 : rel = table_openrv(relrv, AccessShareLock);
418 : :
419 : : /* grab the latest tuple version associated to this CTID */
1236 michael@paquier.xyz 420 : 24 : result = currtid_internal(rel, tid);
421 : :
1910 andres@anarazel.de 422 : 9 : table_close(rel, AccessShareLock);
423 : :
8655 tgl@sss.pgh.pa.us 424 : 9 : PG_RETURN_ITEMPOINTER(result);
425 : : }
|