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.
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"
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"
47 PG_FUNCTION_INFO_V1(pgrowlocks
);
51 * returns tids of rows being locked
59 #define Atnum_ismulti 2
65 pgrowlocks(PG_FUNCTION_ARGS
)
67 text
*relname
= PG_GETARG_TEXT_PP(0);
68 ReturnSetInfo
*rsinfo
= (ReturnSetInfo
*) fcinfo
->resultinfo
;
69 AttInMetadata
*attinmeta
;
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
)
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
)
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
)
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(),
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
)
127 /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
128 LockBuffer(hscan
->rs_cbuf
, BUFFER_LOCK_SHARE
);
130 htsu
= HeapTupleSatisfiesUpdate(tuple
,
131 GetCurrentCommandId(false),
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
;
153 values
[Atnum_ismulti
] = pstrdup("true");
155 allow_old
= HEAP_LOCKED_UPGRADED(infomask
);
156 nmembers
= GetMultiXactIdMembers(xmax
, &members
, allow_old
,
160 values
[Atnum_xids
] = "{0}";
161 values
[Atnum_modes
] = "{transient upgrade status}";
162 values
[Atnum_pids
] = "{0}";
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
++)
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");
193 case MultiXactStatusNoKeyUpdate
:
194 snprintf(buf
, NCHARS
, "No Key Update");
196 case MultiXactStatusForUpdate
:
197 snprintf(buf
, NCHARS
, "For Update");
199 case MultiXactStatusForNoKeyUpdate
:
200 snprintf(buf
, NCHARS
, "For No Key Update");
202 case MultiXactStatusForShare
:
203 snprintf(buf
, NCHARS
, "For Share");
205 case MultiXactStatusForKeyShare
:
206 snprintf(buf
, NCHARS
, "For Key Share");
209 strcat(values
[Atnum_modes
], buf
);
210 snprintf(buf
, NCHARS
, "%d",
211 BackendXidGetPid(members
[j
].xid
));
212 strcat(values
[Atnum_pids
], buf
);
217 strcat(values
[Atnum_xids
], "}");
218 strcat(values
[Atnum_modes
], "}");
219 strcat(values
[Atnum_pids
], "}");
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}");
241 snprintf(values
[Atnum_modes
], NCHARS
, "{For No Key Update}");
244 /* neither keyshare nor exclusive bit it set */
245 snprintf(values
[Atnum_modes
], NCHARS
,
246 "{transient upgrade status}");
250 if (tuple
->t_data
->t_infomask2
& HEAP_KEYS_UPDATED
)
251 snprintf(values
[Atnum_modes
], NCHARS
, "{Update}");
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
);
264 tuple
= BuildTupleFromCStrings(attinmeta
, values
);
265 tuplestore_puttuple(rsinfo
->setResult
, tuple
);
269 LockBuffer(hscan
->rs_cbuf
, BUFFER_LOCK_UNLOCK
);
274 table_close(rel
, AccessShareLock
);