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