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