Age Owner Branch data TLA 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 : :
164 tgl@sss.pgh.pa.us 45 :CBC 2 : PG_MODULE_MAGIC_EXT(
46 : : .name = "pgrowlocks",
47 : : .version = PG_VERSION
48 : : );
49 : :
7076 ishii@postgresql.org 50 : 2 : 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 : 12 : pgrowlocks(PG_FUNCTION_ARGS)
69 : : {
2000 tgl@sss.pgh.pa.us 70 : 12 : text *relname = PG_GETARG_TEXT_PP(0);
71 : 12 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
72 : : AttInMetadata *attinmeta;
73 : : Relation rel;
74 : : RangeVar *relrv;
75 : : TableScanDesc scan;
76 : : HeapScanDesc hscan;
77 : : HeapTuple tuple;
78 : : AclResult aclresult;
79 : : char **values;
80 : :
1054 michael@paquier.xyz 81 : 12 : InitMaterializedSRF(fcinfo, 0);
82 : :
83 : : /* Access the table */
2000 tgl@sss.pgh.pa.us 84 : 12 : relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
85 : 12 : rel = relation_openrv(relrv, AccessShareLock);
86 : :
87 [ - + ]: 12 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2000 tgl@sss.pgh.pa.us 88 [ # # ]:UBC 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.")));
2000 tgl@sss.pgh.pa.us 93 [ - + ]:CBC 12 : else if (rel->rd_rel->relkind != RELKIND_RELATION)
2000 tgl@sss.pgh.pa.us 94 [ # # ]:UBC 0 : ereport(ERROR,
95 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
96 : : errmsg("\"%s\" is not a table",
97 : : RelationGetRelationName(rel))));
676 drowley@postgresql.o 98 [ - + ]:CBC 12 : else if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
676 drowley@postgresql.o 99 [ # # ]:UBC 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 : : */
2000 tgl@sss.pgh.pa.us 107 :CBC 12 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
108 : : ACL_SELECT);
109 [ - + ]: 12 : if (aclresult != ACLCHECK_OK)
1258 mail@joeconway.com 110 :UBC 0 : aclresult = has_privs_of_role(GetUserId(), ROLE_PG_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
111 : :
2000 tgl@sss.pgh.pa.us 112 [ - + ]:CBC 12 : if (aclresult != ACLCHECK_OK)
2000 tgl@sss.pgh.pa.us 113 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
114 : 0 : RelationGetRelationName(rel));
115 : :
116 : : /* Scan the relation */
2000 tgl@sss.pgh.pa.us 117 :CBC 12 : scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
118 : 12 : hscan = (HeapScanDesc) scan;
119 : :
1278 michael@paquier.xyz 120 : 12 : attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
121 : :
122 : 12 : values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
123 : :
7076 ishii@postgresql.org 124 [ + + ]: 36 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
125 : : {
126 : : TM_Result htsu;
127 : : TransactionId xmax;
128 : : uint16 infomask;
129 : :
130 : : /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
2371 andres@anarazel.de 131 : 24 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
132 : :
4429 rhaas@postgresql.org 133 : 24 : htsu = HeapTupleSatisfiesUpdate(tuple,
134 : : GetCurrentCommandId(false),
135 : : hscan->rs_cbuf);
4609 alvherre@alvh.no-ip. 136 : 24 : xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
137 : 24 : infomask = tuple->t_data->t_infomask;
138 : :
139 : : /*
140 : : * A tuple is locked if HTSU returns BeingModified.
141 : : */
2359 andres@anarazel.de 142 [ + + ]: 24 : if (htsu == TM_BeingModified)
143 : : {
29 peter@eisentraut.org 144 :GNC 22 : values[Atnum_tid] = DatumGetCString(DirectFunctionCall1(tidout,
145 : : PointerGetDatum(&tuple->t_self)));
146 : :
4609 alvherre@alvh.no-ip. 147 :CBC 22 : values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
1368 peter@eisentraut.org 148 : 22 : snprintf(values[Atnum_xmax], NCHARS, "%u", xmax);
4609 alvherre@alvh.no-ip. 149 [ + + ]: 22 : if (infomask & HEAP_XMAX_IS_MULTI)
150 : : {
151 : : MultiXactMember *members;
152 : : int nmembers;
153 : 8 : bool first = true;
154 : : bool allow_old;
155 : :
156 : 8 : values[Atnum_ismulti] = pstrdup("true");
157 : :
3361 158 : 8 : allow_old = HEAP_LOCKED_UPGRADED(infomask);
4057 159 : 8 : nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
160 : : false);
4609 161 [ - + ]: 8 : if (nmembers == -1)
162 : : {
4609 alvherre@alvh.no-ip. 163 :UBC 0 : values[Atnum_xids] = "{0}";
164 : 0 : values[Atnum_modes] = "{transient upgrade status}";
165 : 0 : values[Atnum_pids] = "{0}";
166 : : }
167 : : else
168 : : {
169 : : int j;
170 : :
4609 alvherre@alvh.no-ip. 171 :CBC 8 : values[Atnum_xids] = palloc(NCHARS * nmembers);
172 : 8 : values[Atnum_modes] = palloc(NCHARS * nmembers);
173 : 8 : values[Atnum_pids] = palloc(NCHARS * nmembers);
174 : :
175 : 8 : strcpy(values[Atnum_xids], "{");
176 : 8 : strcpy(values[Atnum_modes], "{");
177 : 8 : strcpy(values[Atnum_pids], "{");
178 : :
179 [ + + ]: 24 : for (j = 0; j < nmembers; j++)
180 : : {
181 : : char buf[NCHARS];
182 : :
183 [ + + ]: 16 : if (!first)
184 : : {
185 : 8 : strcat(values[Atnum_xids], ",");
186 : 8 : strcat(values[Atnum_modes], ",");
187 : 8 : strcat(values[Atnum_pids], ",");
188 : : }
1368 peter@eisentraut.org 189 : 16 : snprintf(buf, NCHARS, "%u", members[j].xid);
4609 alvherre@alvh.no-ip. 190 : 16 : strcat(values[Atnum_xids], buf);
191 [ + + + + : 16 : switch (members[j].status)
+ + - ]
192 : : {
193 : 1 : case MultiXactStatusUpdate:
194 : 1 : snprintf(buf, NCHARS, "Update");
195 : 1 : break;
196 : 1 : case MultiXactStatusNoKeyUpdate:
197 : 1 : snprintf(buf, NCHARS, "No Key Update");
198 : 1 : break;
199 : 2 : case MultiXactStatusForUpdate:
200 : 2 : snprintf(buf, NCHARS, "For Update");
201 : 2 : break;
202 : 2 : case MultiXactStatusForNoKeyUpdate:
203 : 2 : snprintf(buf, NCHARS, "For No Key Update");
204 : 2 : break;
205 : 2 : case MultiXactStatusForShare:
711 bruce@momjian.us 206 : 2 : snprintf(buf, NCHARS, "For Share");
4609 alvherre@alvh.no-ip. 207 : 2 : break;
208 : 8 : case MultiXactStatusForKeyShare:
711 bruce@momjian.us 209 : 8 : snprintf(buf, NCHARS, "For Key Share");
4609 alvherre@alvh.no-ip. 210 : 8 : break;
211 : : }
212 : 16 : strcat(values[Atnum_modes], buf);
213 : 16 : snprintf(buf, NCHARS, "%d",
214 : 16 : BackendXidGetPid(members[j].xid));
215 : 16 : strcat(values[Atnum_pids], buf);
216 : :
217 : 16 : first = false;
218 : : }
219 : :
220 : 8 : strcat(values[Atnum_xids], "}");
221 : 8 : strcat(values[Atnum_modes], "}");
222 : 8 : strcat(values[Atnum_pids], "}");
223 : : }
224 : : }
225 : : else
226 : : {
227 : 14 : values[Atnum_ismulti] = pstrdup("false");
228 : :
229 : 14 : values[Atnum_xids] = palloc(NCHARS * sizeof(char));
1368 peter@eisentraut.org 230 : 14 : snprintf(values[Atnum_xids], NCHARS, "{%u}", xmax);
231 : :
4609 alvherre@alvh.no-ip. 232 : 14 : values[Atnum_modes] = palloc(NCHARS);
233 [ + + ]: 14 : if (infomask & HEAP_XMAX_LOCK_ONLY)
234 : : {
235 [ + + ]: 12 : if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
236 : 2 : snprintf(values[Atnum_modes], NCHARS, "{For Share}");
237 [ + + ]: 10 : else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
238 : 6 : snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
239 [ + - ]: 4 : else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
240 : : {
4601 241 [ + + ]: 4 : if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
242 : 2 : snprintf(values[Atnum_modes], NCHARS, "{For Update}");
243 : : else
244 : 2 : snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
245 : : }
246 : : else
247 : : /* neither keyshare nor exclusive bit it set */
4609 alvherre@alvh.no-ip. 248 :UBC 0 : snprintf(values[Atnum_modes], NCHARS,
249 : : "{transient upgrade status}");
250 : : }
251 : : else
252 : : {
4609 alvherre@alvh.no-ip. 253 [ + + ]:CBC 2 : if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
254 : 1 : snprintf(values[Atnum_modes], NCHARS, "{Update}");
255 : : else
4601 256 : 1 : snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
257 : : }
258 : :
4609 259 : 14 : values[Atnum_pids] = palloc(NCHARS * sizeof(char));
260 : 14 : snprintf(values[Atnum_pids], NCHARS, "{%d}",
261 : : BackendXidGetPid(xmax));
262 : : }
263 : :
2371 andres@anarazel.de 264 : 22 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
265 : :
266 : : /* build a tuple */
7076 ishii@postgresql.org 267 : 22 : tuple = BuildTupleFromCStrings(attinmeta, values);
1278 michael@paquier.xyz 268 : 22 : tuplestore_puttuple(rsinfo->setResult, tuple);
269 : : }
270 : : else
271 : : {
2371 andres@anarazel.de 272 : 2 : LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
273 : : }
274 : : }
275 : :
276 : 12 : table_endscan(scan);
2000 tgl@sss.pgh.pa.us 277 : 12 : table_close(rel, AccessShareLock);
278 : 12 : return (Datum) 0;
279 : : }
|