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/multixact.h"
28 #include "access/relscan.h"
29 #include "access/xact.h"
30 #include "catalog/namespace.h"
31 #include "funcapi.h"
32 #include "miscadmin.h"
33 #include "storage/bufmgr.h"
34 #include "storage/procarray.h"
35 #include "utils/acl.h"
36 #include "utils/builtins.h"
37 #include "utils/rel.h"
38 #include "utils/snapmgr.h"
39 #include "utils/tqual.h"
40 
41 PG_MODULE_MAGIC;
42 
43 PG_FUNCTION_INFO_V1(pgrowlocks);
44 
45 /* ----------
46  * pgrowlocks:
47  * returns tids of rows being locked
48  * ----------
49  */
50 
51 #define NCHARS 32
52 
53 #define		Atnum_tid		0
54 #define		Atnum_xmax		1
55 #define		Atnum_ismulti	2
56 #define		Atnum_xids		3
57 #define		Atnum_modes		4
58 #define		Atnum_pids		5
59 
60 Datum
pgrowlocks(PG_FUNCTION_ARGS)61 pgrowlocks(PG_FUNCTION_ARGS)
62 {
63 	text	   *relname = PG_GETARG_TEXT_PP(0);
64 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
65 	bool		randomAccess;
66 	TupleDesc	tupdesc;
67 	Tuplestorestate *tupstore;
68 	AttInMetadata *attinmeta;
69 	Relation	rel;
70 	RangeVar   *relrv;
71 	HeapScanDesc scan;
72 	HeapTuple	tuple;
73 	MemoryContext oldcontext;
74 	AclResult	aclresult;
75 	char	  **values;
76 
77 	/* check to see if caller supports us returning a tuplestore */
78 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
79 		ereport(ERROR,
80 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
81 				 errmsg("set-valued function called in context that cannot accept a set")));
82 	if (!(rsinfo->allowedModes & SFRM_Materialize))
83 		ereport(ERROR,
84 				(errcode(ERRCODE_SYNTAX_ERROR),
85 				 errmsg("materialize mode required, but it is not allowed in this context")));
86 
87 	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
88 	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
89 
90 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
91 		elog(ERROR, "return type must be a row type");
92 
93 	randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
94 	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
95 	rsinfo->returnMode = SFRM_Materialize;
96 	rsinfo->setResult = tupstore;
97 	rsinfo->setDesc = tupdesc;
98 
99 	MemoryContextSwitchTo(oldcontext);
100 
101 	/* Access the table */
102 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
103 	rel = heap_openrv(relrv, AccessShareLock);
104 
105 	/* check permissions: must have SELECT on table */
106 	aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
107 								  ACL_SELECT);
108 	if (aclresult != ACLCHECK_OK)
109 		aclcheck_error(aclresult, ACL_KIND_CLASS,
110 					   RelationGetRelationName(rel));
111 
112 	/* Scan the relation */
113 	scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
114 
115 	attinmeta = TupleDescGetAttInMetadata(tupdesc);
116 
117 	values = (char **) palloc(tupdesc->natts * sizeof(char *));
118 
119 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
120 	{
121 		HTSU_Result htsu;
122 		TransactionId xmax;
123 		uint16		infomask;
124 
125 		/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
126 		LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
127 
128 		htsu = HeapTupleSatisfiesUpdate(tuple,
129 										GetCurrentCommandId(false),
130 										scan->rs_cbuf);
131 		xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
132 		infomask = tuple->t_data->t_infomask;
133 
134 		/*
135 		 * A tuple is locked if HTSU returns BeingUpdated.
136 		 */
137 		if (htsu == HeapTupleBeingUpdated)
138 		{
139 			values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
140 											PointerGetDatum(&tuple->t_self));
141 
142 			values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
143 			snprintf(values[Atnum_xmax], NCHARS, "%d", xmax);
144 			if (infomask & HEAP_XMAX_IS_MULTI)
145 			{
146 				MultiXactMember *members;
147 				int			nmembers;
148 				bool		first = true;
149 				bool		allow_old;
150 
151 				values[Atnum_ismulti] = pstrdup("true");
152 
153 				allow_old = HEAP_LOCKED_UPGRADED(infomask);
154 				nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
155 												 false);
156 				if (nmembers == -1)
157 				{
158 					values[Atnum_xids] = "{0}";
159 					values[Atnum_modes] = "{transient upgrade status}";
160 					values[Atnum_pids] = "{0}";
161 				}
162 				else
163 				{
164 					int			j;
165 
166 					values[Atnum_xids] = palloc(NCHARS * nmembers);
167 					values[Atnum_modes] = palloc(NCHARS * nmembers);
168 					values[Atnum_pids] = palloc(NCHARS * nmembers);
169 
170 					strcpy(values[Atnum_xids], "{");
171 					strcpy(values[Atnum_modes], "{");
172 					strcpy(values[Atnum_pids], "{");
173 
174 					for (j = 0; j < nmembers; j++)
175 					{
176 						char		buf[NCHARS];
177 
178 						if (!first)
179 						{
180 							strcat(values[Atnum_xids], ",");
181 							strcat(values[Atnum_modes], ",");
182 							strcat(values[Atnum_pids], ",");
183 						}
184 						snprintf(buf, NCHARS, "%d", members[j].xid);
185 						strcat(values[Atnum_xids], buf);
186 						switch (members[j].status)
187 						{
188 							case MultiXactStatusUpdate:
189 								snprintf(buf, NCHARS, "Update");
190 								break;
191 							case MultiXactStatusNoKeyUpdate:
192 								snprintf(buf, NCHARS, "No Key Update");
193 								break;
194 							case MultiXactStatusForUpdate:
195 								snprintf(buf, NCHARS, "For Update");
196 								break;
197 							case MultiXactStatusForNoKeyUpdate:
198 								snprintf(buf, NCHARS, "For No Key Update");
199 								break;
200 							case MultiXactStatusForShare:
201 								snprintf(buf, NCHARS, "Share");
202 								break;
203 							case MultiXactStatusForKeyShare:
204 								snprintf(buf, NCHARS, "Key Share");
205 								break;
206 						}
207 						strcat(values[Atnum_modes], buf);
208 						snprintf(buf, NCHARS, "%d",
209 								 BackendXidGetPid(members[j].xid));
210 						strcat(values[Atnum_pids], buf);
211 
212 						first = false;
213 					}
214 
215 					strcat(values[Atnum_xids], "}");
216 					strcat(values[Atnum_modes], "}");
217 					strcat(values[Atnum_pids], "}");
218 				}
219 			}
220 			else
221 			{
222 				values[Atnum_ismulti] = pstrdup("false");
223 
224 				values[Atnum_xids] = palloc(NCHARS * sizeof(char));
225 				snprintf(values[Atnum_xids], NCHARS, "{%d}", xmax);
226 
227 				values[Atnum_modes] = palloc(NCHARS);
228 				if (infomask & HEAP_XMAX_LOCK_ONLY)
229 				{
230 					if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
231 						snprintf(values[Atnum_modes], NCHARS, "{For Share}");
232 					else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
233 						snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
234 					else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
235 					{
236 						if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
237 							snprintf(values[Atnum_modes], NCHARS, "{For Update}");
238 						else
239 							snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
240 					}
241 					else
242 						/* neither keyshare nor exclusive bit it set */
243 						snprintf(values[Atnum_modes], NCHARS,
244 								 "{transient upgrade status}");
245 				}
246 				else
247 				{
248 					if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
249 						snprintf(values[Atnum_modes], NCHARS, "{Update}");
250 					else
251 						snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
252 				}
253 
254 				values[Atnum_pids] = palloc(NCHARS * sizeof(char));
255 				snprintf(values[Atnum_pids], NCHARS, "{%d}",
256 						 BackendXidGetPid(xmax));
257 			}
258 
259 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
260 
261 			/* build a tuple */
262 			tuple = BuildTupleFromCStrings(attinmeta, values);
263 			tuplestore_puttuple(tupstore, tuple);
264 		}
265 		else
266 		{
267 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
268 		}
269 	}
270 
271 	heap_endscan(scan);
272 	heap_close(rel, AccessShareLock);
273 	return (Datum) 0;
274 }
275