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