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