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