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