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