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