1 /*
2  * contrib/pgrowlocks/pgrowlocks.c
3  *
4  * Copyright (c) 2005-2006	Tatsuo Ishii
5  *
6  * Permission to use, copy, modify, and distribute this software and
7  * its documentation for any purpose, without fee, and without a
8  * written agreement is hereby granted, provided that the above
9  * copyright notice and this paragraph and the following two
10  * paragraphs appear in all copies.
11  *
12  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
13  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
16  * OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
21  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
22  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  */
24 
25 #include "postgres.h"
26 
27 #include "access/heapam.h"
28 #include "access/multixact.h"
29 #include "access/relscan.h"
30 #include "access/tableam.h"
31 #include "access/xact.h"
32 #include "catalog/namespace.h"
33 #include "catalog/pg_am_d.h"
34 #include "catalog/pg_authid.h"
35 #include "funcapi.h"
36 #include "miscadmin.h"
37 #include "storage/bufmgr.h"
38 #include "storage/procarray.h"
39 #include "utils/acl.h"
40 #include "utils/builtins.h"
41 #include "utils/rel.h"
42 #include "utils/snapmgr.h"
43 #include "utils/varlena.h"
44 
45 PG_MODULE_MAGIC;
46 
47 PG_FUNCTION_INFO_V1(pgrowlocks);
48 
49 /* ----------
50  * pgrowlocks:
51  * returns tids of rows being locked
52  * ----------
53  */
54 
55 #define NCHARS 32
56 
57 #define		Atnum_tid		0
58 #define		Atnum_xmax		1
59 #define		Atnum_ismulti	2
60 #define		Atnum_xids		3
61 #define		Atnum_modes		4
62 #define		Atnum_pids		5
63 
64 Datum
65 pgrowlocks(PG_FUNCTION_ARGS)
66 {
67 	text	   *relname = PG_GETARG_TEXT_PP(0);
68 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
69 	bool		randomAccess;
70 	TupleDesc	tupdesc;
71 	Tuplestorestate *tupstore;
72 	AttInMetadata *attinmeta;
73 	Relation	rel;
74 	RangeVar   *relrv;
75 	TableScanDesc scan;
76 	HeapScanDesc hscan;
77 	HeapTuple	tuple;
78 	MemoryContext oldcontext;
79 	AclResult	aclresult;
80 	char	  **values;
81 
82 	/* check to see if caller supports us returning a tuplestore */
83 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
84 		ereport(ERROR,
85 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
86 				 errmsg("set-valued function called in context that cannot accept a set")));
87 	if (!(rsinfo->allowedModes & SFRM_Materialize))
88 		ereport(ERROR,
89 				(errcode(ERRCODE_SYNTAX_ERROR),
90 				 errmsg("materialize mode required, but it is not allowed in this context")));
91 
92 	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
93 	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
94 
95 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
96 		elog(ERROR, "return type must be a row type");
97 
98 	randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
99 	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
100 	rsinfo->returnMode = SFRM_Materialize;
101 	rsinfo->setResult = tupstore;
102 	rsinfo->setDesc = tupdesc;
103 
104 	MemoryContextSwitchTo(oldcontext);
105 
106 	/* Access the table */
107 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
108 	rel = relation_openrv(relrv, AccessShareLock);
109 
110 	if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
111 		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
112 						errmsg("only heap AM is supported")));
113 
114 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
115 		ereport(ERROR,
116 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
117 				 errmsg("\"%s\" is a partitioned table",
118 						RelationGetRelationName(rel)),
119 				 errdetail("Partitioned tables do not contain rows.")));
120 	else if (rel->rd_rel->relkind != RELKIND_RELATION)
121 		ereport(ERROR,
122 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
123 				 errmsg("\"%s\" is not a table",
124 						RelationGetRelationName(rel))));
125 
126 	/*
127 	 * check permissions: must have SELECT on table or be in
128 	 * pg_stat_scan_tables
129 	 */
130 	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
131 								  ACL_SELECT);
132 	if (aclresult != ACLCHECK_OK)
133 		aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
134 
135 	if (aclresult != ACLCHECK_OK)
136 		aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
137 					   RelationGetRelationName(rel));
138 
139 	/* Scan the relation */
140 	scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
141 	hscan = (HeapScanDesc) scan;
142 
143 	attinmeta = TupleDescGetAttInMetadata(tupdesc);
144 
145 	values = (char **) palloc(tupdesc->natts * sizeof(char *));
146 
toku_env_run_lock_escalation_for_test(DB_ENV * env)147 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
148 	{
149 		TM_Result	htsu;
150 		TransactionId xmax;
151 		uint16		infomask;
152 
153 		/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
154 		LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
155 
156 		htsu = HeapTupleSatisfiesUpdate(tuple,
157 										GetCurrentCommandId(false),
158 										hscan->rs_cbuf);
159 		xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
160 		infomask = tuple->t_data->t_infomask;
161 
162 		/*
163 		 * A tuple is locked if HTSU returns BeingModified.
164 		 */
165 		if (htsu == TM_BeingModified)
166 		{
167 			values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
168 															 PointerGetDatum(&tuple->t_self));
169 
170 			values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
171 			snprintf(values[Atnum_xmax], NCHARS, "%d", xmax);
172 			if (infomask & HEAP_XMAX_IS_MULTI)
173 			{
174 				MultiXactMember *members;
175 				int			nmembers;
176 				bool		first = true;
177 				bool		allow_old;
178 
179 				values[Atnum_ismulti] = pstrdup("true");
180 
181 				allow_old = HEAP_LOCKED_UPGRADED(infomask);
182 				nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
183 												 false);
184 				if (nmembers == -1)
185 				{
186 					values[Atnum_xids] = "{0}";
187 					values[Atnum_modes] = "{transient upgrade status}";
188 					values[Atnum_pids] = "{0}";
189 				}
190 				else
191 				{
192 					int			j;
193 
194 					values[Atnum_xids] = palloc(NCHARS * nmembers);
195 					values[Atnum_modes] = palloc(NCHARS * nmembers);
196 					values[Atnum_pids] = palloc(NCHARS * nmembers);
197 
198 					strcpy(values[Atnum_xids], "{");
199 					strcpy(values[Atnum_modes], "{");
200 					strcpy(values[Atnum_pids], "{");
201 
202 					for (j = 0; j < nmembers; j++)
203 					{
204 						char		buf[NCHARS];
205 
206 						if (!first)
207 						{
208 							strcat(values[Atnum_xids], ",");
209 							strcat(values[Atnum_modes], ",");
210 							strcat(values[Atnum_pids], ",");
211 						}
212 						snprintf(buf, NCHARS, "%d", members[j].xid);
213 						strcat(values[Atnum_xids], buf);
214 						switch (members[j].status)
215 						{
216 							case MultiXactStatusUpdate:
217 								snprintf(buf, NCHARS, "Update");
218 								break;
219 							case MultiXactStatusNoKeyUpdate:
220 								snprintf(buf, NCHARS, "No Key Update");
221 								break;
222 							case MultiXactStatusForUpdate:
223 								snprintf(buf, NCHARS, "For Update");
224 								break;
225 							case MultiXactStatusForNoKeyUpdate:
226 								snprintf(buf, NCHARS, "For No Key Update");
227 								break;
228 							case MultiXactStatusForShare:
229 								snprintf(buf, NCHARS, "Share");
230 								break;
231 							case MultiXactStatusForKeyShare:
232 								snprintf(buf, NCHARS, "Key Share");
233 								break;
234 						}
235 						strcat(values[Atnum_modes], buf);
236 						snprintf(buf, NCHARS, "%d",
237 								 BackendXidGetPid(members[j].xid));
238 						strcat(values[Atnum_pids], buf);
239 
240 						first = false;
241 					}
242 
243 					strcat(values[Atnum_xids], "}");
244 					strcat(values[Atnum_modes], "}");
245 					strcat(values[Atnum_pids], "}");
246 				}
247 			}
248 			else
249 			{
250 				values[Atnum_ismulti] = pstrdup("false");
251 
252 				values[Atnum_xids] = palloc(NCHARS * sizeof(char));
253 				snprintf(values[Atnum_xids], NCHARS, "{%d}", xmax);
254 
255 				values[Atnum_modes] = palloc(NCHARS);
256 				if (infomask & HEAP_XMAX_LOCK_ONLY)
257 				{
258 					if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
259 						snprintf(values[Atnum_modes], NCHARS, "{For Share}");
260 					else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
261 						snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
262 					else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
263 					{
264 						if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
265 							snprintf(values[Atnum_modes], NCHARS, "{For Update}");
266 						else
267 							snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
268 					}
269 					else
270 						/* neither keyshare nor exclusive bit it set */
271 						snprintf(values[Atnum_modes], NCHARS,
272 								 "{transient upgrade status}");
273 				}
274 				else
275 				{
276 					if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
277 						snprintf(values[Atnum_modes], NCHARS, "{Update}");
278 					else
279 						snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
280 				}
281 
282 				values[Atnum_pids] = palloc(NCHARS * sizeof(char));
283 				snprintf(values[Atnum_pids], NCHARS, "{%d}",
284 						 BackendXidGetPid(xmax));
285 			}
286 
287 			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
288 
289 			/* build a tuple */
290 			tuple = BuildTupleFromCStrings(attinmeta, values);
291 			tuplestore_puttuple(tupstore, tuple);
292 		}
293 		else
294 		{
295 			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
296 		}
297 	}
298 
299 	table_endscan(scan);
300 	table_close(rel, AccessShareLock);
301 	return (Datum) 0;
302 }
303