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