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