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