1 /*-------------------------------------------------------------------------
2  *
3  * tid.c
4  *	  Functions for the built-in type tuple id
5  *
6  * Portions Copyright (c) 1996-2018, 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 = ItemPointerGetBlockNumberNoCheck(itemPtr);
113 	offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr);
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 	StringInfoData buf;
150 
151 	pq_begintypsend(&buf);
152 	pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr));
153 	pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr));
154 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
155 }
156 
157 /*****************************************************************************
158  *	 PUBLIC ROUTINES														 *
159  *****************************************************************************/
160 
161 Datum
162 tideq(PG_FUNCTION_ARGS)
163 {
164 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
165 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
166 
167 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
168 }
169 
170 Datum
171 tidne(PG_FUNCTION_ARGS)
172 {
173 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
174 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
175 
176 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
177 }
178 
179 Datum
180 tidlt(PG_FUNCTION_ARGS)
181 {
182 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
183 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
184 
185 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
186 }
187 
188 Datum
189 tidle(PG_FUNCTION_ARGS)
190 {
191 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
192 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
193 
194 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
195 }
196 
197 Datum
198 tidgt(PG_FUNCTION_ARGS)
199 {
200 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
201 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
202 
203 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
204 }
205 
206 Datum
207 tidge(PG_FUNCTION_ARGS)
208 {
209 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
210 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
211 
212 	PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
213 }
214 
215 Datum
216 bttidcmp(PG_FUNCTION_ARGS)
217 {
218 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
219 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
220 
221 	PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
222 }
223 
224 Datum
225 tidlarger(PG_FUNCTION_ARGS)
226 {
227 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
228 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
229 
230 	PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
231 }
232 
233 Datum
234 tidsmaller(PG_FUNCTION_ARGS)
235 {
236 	ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
237 	ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
238 
239 	PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
240 }
241 
242 
243 /*
244  *	Functions to get latest tid of a specified tuple.
245  *
246  *	Maybe these implementations should be moved to another place
247  */
248 
249 static ItemPointerData Current_last_tid = {{0, 0}, 0};
250 
251 void
252 setLastTid(const ItemPointer tid)
253 {
254 	Current_last_tid = *tid;
255 }
256 
257 /*
258  *	Handle CTIDs of views.
259  *		CTID should be defined in the view and it must
260  *		correspond to the CTID of a base relation.
261  */
262 static Datum
263 currtid_for_view(Relation viewrel, ItemPointer tid)
264 {
265 	TupleDesc	att = RelationGetDescr(viewrel);
266 	RuleLock   *rulelock;
267 	RewriteRule *rewrite;
268 	int			i,
269 				natts = att->natts,
270 				tididx = -1;
271 
272 	for (i = 0; i < natts; i++)
273 	{
274 		Form_pg_attribute attr = TupleDescAttr(att, i);
275 
276 		if (strcmp(NameStr(attr->attname), "ctid") == 0)
277 		{
278 			if (attr->atttypid != TIDOID)
279 				elog(ERROR, "ctid isn't of type TID");
280 			tididx = i;
281 			break;
282 		}
283 	}
284 	if (tididx < 0)
285 		elog(ERROR, "currtid cannot handle views with no CTID");
286 	rulelock = viewrel->rd_rules;
287 	if (!rulelock)
288 		elog(ERROR, "the view has no rules");
289 	for (i = 0; i < rulelock->numLocks; i++)
290 	{
291 		rewrite = rulelock->rules[i];
292 		if (rewrite->event == CMD_SELECT)
293 		{
294 			Query	   *query;
295 			TargetEntry *tle;
296 
297 			if (list_length(rewrite->actions) != 1)
298 				elog(ERROR, "only one select rule is allowed in views");
299 			query = (Query *) linitial(rewrite->actions);
300 			tle = get_tle_by_resno(query->targetList, tididx + 1);
301 			if (tle && tle->expr && IsA(tle->expr, Var))
302 			{
303 				Var		   *var = (Var *) tle->expr;
304 				RangeTblEntry *rte;
305 
306 				if (!IS_SPECIAL_VARNO(var->varno) &&
307 					var->varattno == SelfItemPointerAttributeNumber)
308 				{
309 					rte = rt_fetch(var->varno, query->rtable);
310 					if (rte)
311 					{
312 						Datum		result;
313 
314 						result = DirectFunctionCall2(currtid_byreloid,
315 													 ObjectIdGetDatum(rte->relid),
316 													 PointerGetDatum(tid));
317 						heap_close(viewrel, AccessShareLock);
318 						return result;
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, get_relkind_objtype(rel->rd_rel->relkind),
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_PP(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, get_relkind_objtype(rel->rd_rel->relkind),
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 }
402