Line data Source code
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/fmgrprotos.h"
41 : #include "utils/rel.h"
42 : #include "utils/snapmgr.h"
43 : #include "utils/varlena.h"
44 :
45 0 : PG_MODULE_MAGIC_EXT(
46 : .name = "pgrowlocks",
47 : .version = PG_VERSION
48 : );
49 :
50 0 : PG_FUNCTION_INFO_V1(pgrowlocks);
51 :
52 : /* ----------
53 : * pgrowlocks:
54 : * returns tids of rows being locked
55 : * ----------
56 : */
57 :
58 : #define NCHARS 32
59 :
60 : #define Atnum_tid 0
61 : #define Atnum_xmax 1
62 : #define Atnum_ismulti 2
63 : #define Atnum_xids 3
64 : #define Atnum_modes 4
65 : #define Atnum_pids 5
66 :
67 : Datum
68 0 : pgrowlocks(PG_FUNCTION_ARGS)
69 : {
70 0 : text *relname = PG_GETARG_TEXT_PP(0);
71 0 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
72 0 : AttInMetadata *attinmeta;
73 0 : Relation rel;
74 0 : RangeVar *relrv;
75 0 : TableScanDesc scan;
76 0 : HeapScanDesc hscan;
77 0 : HeapTuple tuple;
78 0 : AclResult aclresult;
79 0 : char **values;
80 :
81 0 : InitMaterializedSRF(fcinfo, 0);
82 :
83 : /* Access the table */
84 0 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
85 0 : rel = relation_openrv(relrv, AccessShareLock);
86 :
87 0 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
88 0 : ereport(ERROR,
89 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
90 : errmsg("\"%s\" is a partitioned table",
91 : RelationGetRelationName(rel)),
92 : errdetail("Partitioned tables do not contain rows.")));
93 0 : else if (rel->rd_rel->relkind != RELKIND_RELATION)
94 0 : ereport(ERROR,
95 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
96 : errmsg("\"%s\" is not a table",
97 : RelationGetRelationName(rel))));
98 0 : else if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
99 0 : ereport(ERROR,
100 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
101 : errmsg("only heap AM is supported")));
102 :
103 : /*
104 : * check permissions: must have SELECT on table or be in
105 : * pg_stat_scan_tables
106 : */
107 0 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
108 : ACL_SELECT);
109 0 : if (aclresult != ACLCHECK_OK)
110 0 : aclresult = has_privs_of_role(GetUserId(), ROLE_PG_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
111 :
112 0 : if (aclresult != ACLCHECK_OK)
113 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
114 0 : RelationGetRelationName(rel));
115 :
116 : /* Scan the relation */
117 0 : scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
118 0 : hscan = (HeapScanDesc) scan;
119 :
120 0 : attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
121 :
122 0 : values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
123 :
124 0 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
125 : {
126 0 : TM_Result htsu;
127 0 : TransactionId xmax;
128 0 : uint16 infomask;
129 :
130 : /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
131 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
132 :
133 0 : htsu = HeapTupleSatisfiesUpdate(tuple,
134 0 : GetCurrentCommandId(false),
135 0 : hscan->rs_cbuf);
136 0 : xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
137 0 : infomask = tuple->t_data->t_infomask;
138 :
139 : /*
140 : * A tuple is locked if HTSU returns BeingModified.
141 : */
142 0 : if (htsu == TM_BeingModified)
143 : {
144 0 : values[Atnum_tid] = DatumGetCString(DirectFunctionCall1(tidout,
145 : PointerGetDatum(&tuple->t_self)));
146 :
147 0 : values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
148 0 : snprintf(values[Atnum_xmax], NCHARS, "%u", xmax);
149 0 : if (infomask & HEAP_XMAX_IS_MULTI)
150 : {
151 0 : MultiXactMember *members;
152 0 : int nmembers;
153 0 : bool first = true;
154 0 : bool allow_old;
155 :
156 0 : values[Atnum_ismulti] = pstrdup("true");
157 :
158 0 : allow_old = HEAP_LOCKED_UPGRADED(infomask);
159 0 : nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
160 : false);
161 0 : if (nmembers == -1)
162 : {
163 0 : values[Atnum_xids] = "{0}";
164 0 : values[Atnum_modes] = "{transient upgrade status}";
165 0 : values[Atnum_pids] = "{0}";
166 0 : }
167 : else
168 : {
169 0 : int j;
170 :
171 0 : values[Atnum_xids] = palloc(NCHARS * nmembers);
172 0 : values[Atnum_modes] = palloc(NCHARS * nmembers);
173 0 : values[Atnum_pids] = palloc(NCHARS * nmembers);
174 :
175 0 : strcpy(values[Atnum_xids], "{");
176 0 : strcpy(values[Atnum_modes], "{");
177 0 : strcpy(values[Atnum_pids], "{");
178 :
179 0 : for (j = 0; j < nmembers; j++)
180 : {
181 0 : char buf[NCHARS];
182 :
183 0 : if (!first)
184 : {
185 0 : strcat(values[Atnum_xids], ",");
186 0 : strcat(values[Atnum_modes], ",");
187 0 : strcat(values[Atnum_pids], ",");
188 0 : }
189 0 : snprintf(buf, NCHARS, "%u", members[j].xid);
190 0 : strcat(values[Atnum_xids], buf);
191 0 : switch (members[j].status)
192 : {
193 : case MultiXactStatusUpdate:
194 0 : snprintf(buf, NCHARS, "Update");
195 0 : break;
196 : case MultiXactStatusNoKeyUpdate:
197 0 : snprintf(buf, NCHARS, "No Key Update");
198 0 : break;
199 : case MultiXactStatusForUpdate:
200 0 : snprintf(buf, NCHARS, "For Update");
201 0 : break;
202 : case MultiXactStatusForNoKeyUpdate:
203 0 : snprintf(buf, NCHARS, "For No Key Update");
204 0 : break;
205 : case MultiXactStatusForShare:
206 0 : snprintf(buf, NCHARS, "For Share");
207 0 : break;
208 : case MultiXactStatusForKeyShare:
209 0 : snprintf(buf, NCHARS, "For Key Share");
210 0 : break;
211 : }
212 0 : strcat(values[Atnum_modes], buf);
213 0 : snprintf(buf, NCHARS, "%d",
214 0 : BackendXidGetPid(members[j].xid));
215 0 : strcat(values[Atnum_pids], buf);
216 :
217 0 : first = false;
218 0 : }
219 :
220 0 : strcat(values[Atnum_xids], "}");
221 0 : strcat(values[Atnum_modes], "}");
222 0 : strcat(values[Atnum_pids], "}");
223 0 : }
224 0 : }
225 : else
226 : {
227 0 : values[Atnum_ismulti] = pstrdup("false");
228 :
229 0 : values[Atnum_xids] = palloc(NCHARS * sizeof(char));
230 0 : snprintf(values[Atnum_xids], NCHARS, "{%u}", xmax);
231 :
232 0 : values[Atnum_modes] = palloc(NCHARS);
233 0 : if (infomask & HEAP_XMAX_LOCK_ONLY)
234 : {
235 0 : if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
236 0 : snprintf(values[Atnum_modes], NCHARS, "{For Share}");
237 0 : else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
238 0 : snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
239 0 : else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
240 : {
241 0 : if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
242 0 : snprintf(values[Atnum_modes], NCHARS, "{For Update}");
243 : else
244 0 : snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
245 0 : }
246 : else
247 : /* neither keyshare nor exclusive bit it set */
248 0 : snprintf(values[Atnum_modes], NCHARS,
249 : "{transient upgrade status}");
250 0 : }
251 : else
252 : {
253 0 : if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
254 0 : snprintf(values[Atnum_modes], NCHARS, "{Update}");
255 : else
256 0 : snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
257 : }
258 :
259 0 : values[Atnum_pids] = palloc(NCHARS * sizeof(char));
260 0 : snprintf(values[Atnum_pids], NCHARS, "{%d}",
261 0 : BackendXidGetPid(xmax));
262 : }
263 :
264 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
265 :
266 : /* build a tuple */
267 0 : tuple = BuildTupleFromCStrings(attinmeta, values);
268 0 : tuplestore_puttuple(rsinfo->setResult, tuple);
269 0 : }
270 : else
271 : {
272 0 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
273 : }
274 0 : }
275 :
276 0 : table_endscan(scan);
277 0 : table_close(rel, AccessShareLock);
278 0 : return (Datum) 0;
279 0 : }
|