]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/tid.c
Update copyright for 2014
[postgresql] / src / backend / utils / adt / tid.c
1 /*-------------------------------------------------------------------------
2  *
3  * tid.c
4  *        Functions for the built-in type tuple id
5  *
6  * Portions Copyright (c) 1996-2014, 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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                                                 heap_close(viewrel, AccessShareLock);
317                                                 return DirectFunctionCall2(currtid_byreloid, ObjectIdGetDatum(rte->relid), PointerGetDatum(tid));
318                                         }
319                                 }
320                         }
321                         break;
322                 }
323         }
324         elog(ERROR, "currtid cannot handle this view");
325         return (Datum) 0;
326 }
327
328 Datum
329 currtid_byreloid(PG_FUNCTION_ARGS)
330 {
331         Oid                     reloid = PG_GETARG_OID(0);
332         ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
333         ItemPointer result;
334         Relation        rel;
335         AclResult       aclresult;
336         Snapshot        snapshot;
337
338         result = (ItemPointer) palloc(sizeof(ItemPointerData));
339         if (!reloid)
340         {
341                 *result = Current_last_tid;
342                 PG_RETURN_ITEMPOINTER(result);
343         }
344
345         rel = heap_open(reloid, AccessShareLock);
346
347         aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
348                                                                   ACL_SELECT);
349         if (aclresult != ACLCHECK_OK)
350                 aclcheck_error(aclresult, ACL_KIND_CLASS,
351                                            RelationGetRelationName(rel));
352
353         if (rel->rd_rel->relkind == RELKIND_VIEW)
354                 return currtid_for_view(rel, tid);
355
356         ItemPointerCopy(tid, result);
357
358         snapshot = RegisterSnapshot(GetLatestSnapshot());
359         heap_get_latest_tid(rel, snapshot, result);
360         UnregisterSnapshot(snapshot);
361
362         heap_close(rel, AccessShareLock);
363
364         PG_RETURN_ITEMPOINTER(result);
365 }
366
367 Datum
368 currtid_byrelname(PG_FUNCTION_ARGS)
369 {
370         text       *relname = PG_GETARG_TEXT_P(0);
371         ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
372         ItemPointer result;
373         RangeVar   *relrv;
374         Relation        rel;
375         AclResult       aclresult;
376         Snapshot        snapshot;
377
378         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
379         rel = heap_openrv(relrv, AccessShareLock);
380
381         aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
382                                                                   ACL_SELECT);
383         if (aclresult != ACLCHECK_OK)
384                 aclcheck_error(aclresult, ACL_KIND_CLASS,
385                                            RelationGetRelationName(rel));
386
387         if (rel->rd_rel->relkind == RELKIND_VIEW)
388                 return currtid_for_view(rel, tid);
389
390         result = (ItemPointer) palloc(sizeof(ItemPointerData));
391         ItemPointerCopy(tid, result);
392
393         snapshot = RegisterSnapshot(GetLatestSnapshot());
394         heap_get_latest_tid(rel, snapshot, result);
395         UnregisterSnapshot(snapshot);
396
397         heap_close(rel, AccessShareLock);
398
399         PG_RETURN_ITEMPOINTER(result);
400 }