summaryrefslogtreecommitdiffstats
path: root/contrib/pgrowlocks
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
commit6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch)
tree657d8194422a5daccecfd42d654b8a245ef7b4c8 /contrib/pgrowlocks
parentInitial commit. (diff)
downloadpostgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.tar.xz
postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.zip
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/pgrowlocks')
-rw-r--r--contrib/pgrowlocks/Makefile21
-rw-r--r--contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql17
-rw-r--r--contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql6
-rw-r--r--contrib/pgrowlocks/pgrowlocks--1.2.sql15
-rw-r--r--contrib/pgrowlocks/pgrowlocks.c302
-rw-r--r--contrib/pgrowlocks/pgrowlocks.control5
6 files changed, 366 insertions, 0 deletions
diff --git a/contrib/pgrowlocks/Makefile b/contrib/pgrowlocks/Makefile
new file mode 100644
index 0000000..294c05d
--- /dev/null
+++ b/contrib/pgrowlocks/Makefile
@@ -0,0 +1,21 @@
+# contrib/pgrowlocks/Makefile
+
+MODULE_big = pgrowlocks
+OBJS = \
+ $(WIN32RES) \
+ pgrowlocks.o
+
+EXTENSION = pgrowlocks
+DATA = pgrowlocks--1.2.sql pgrowlocks--1.1--1.2.sql pgrowlocks--1.0--1.1.sql
+PGFILEDESC = "pgrowlocks - display row locking information"
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/pgrowlocks
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql b/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql
new file mode 100644
index 0000000..3d5ca34
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql
@@ -0,0 +1,17 @@
+/* contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgrowlocks UPDATE TO '1.1'" to load this file. \quit
+
+ALTER EXTENSION pgrowlocks DROP FUNCTION pgrowlocks(text);
+DROP FUNCTION pgrowlocks(text);
+CREATE FUNCTION pgrowlocks(IN relname text,
+ OUT locked_row TID, -- row TID
+ OUT locker XID, -- locking XID
+ OUT multi bool, -- multi XID?
+ OUT xids xid[], -- multi XIDs
+ OUT modes text[], -- multi XID statuses
+ OUT pids INTEGER[]) -- locker's process id
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgrowlocks'
+LANGUAGE C STRICT;
diff --git a/contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql b/contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql
new file mode 100644
index 0000000..94ebf54
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgrowlocks UPDATE TO '1.2'" to load this file. \quit
+
+ALTER FUNCTION pgrowlocks(text) PARALLEL SAFE;
diff --git a/contrib/pgrowlocks/pgrowlocks--1.2.sql b/contrib/pgrowlocks/pgrowlocks--1.2.sql
new file mode 100644
index 0000000..ff76b8b
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks--1.2.sql
@@ -0,0 +1,15 @@
+/* contrib/pgrowlocks/pgrowlocks--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgrowlocks" to load this file. \quit
+
+CREATE FUNCTION pgrowlocks(IN relname text,
+ OUT locked_row TID, -- row TID
+ OUT locker XID, -- locking XID
+ OUT multi bool, -- multi XID?
+ OUT xids xid[], -- multi XIDs
+ OUT modes text[], -- multi XID statuses
+ OUT pids INTEGER[]) -- locker's process id
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgrowlocks'
+LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
new file mode 100644
index 0000000..7143988
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -0,0 +1,302 @@
+/*
+ * contrib/pgrowlocks/pgrowlocks.c
+ *
+ * Copyright (c) 2005-2006 Tatsuo Ishii
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose, without fee, and without a
+ * written agreement is hereby granted, provided that the above
+ * copyright notice and this paragraph and the following two
+ * paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
+ * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+ * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
+ * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
+ * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/tableam.h"
+#include "access/xact.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_am_d.h"
+#include "catalog/pg_authid.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/snapmgr.h"
+#include "utils/varlena.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(pgrowlocks);
+
+/* ----------
+ * pgrowlocks:
+ * returns tids of rows being locked
+ * ----------
+ */
+
+#define NCHARS 32
+
+#define Atnum_tid 0
+#define Atnum_xmax 1
+#define Atnum_ismulti 2
+#define Atnum_xids 3
+#define Atnum_modes 4
+#define Atnum_pids 5
+
+Datum
+pgrowlocks(PG_FUNCTION_ARGS)
+{
+ text *relname = PG_GETARG_TEXT_PP(0);
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ bool randomAccess;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ AttInMetadata *attinmeta;
+ Relation rel;
+ RangeVar *relrv;
+ TableScanDesc scan;
+ HeapScanDesc hscan;
+ HeapTuple tuple;
+ MemoryContext oldcontext;
+ AclResult aclresult;
+ char **values;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("materialize mode required, but it is not allowed in this context")));
+
+ /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
+ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
+
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
+ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Access the table */
+ relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+ rel = relation_openrv(relrv, AccessShareLock);
+
+ if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only heap AM is supported")));
+
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a partitioned table",
+ RelationGetRelationName(rel)),
+ errdetail("Partitioned tables do not contain rows.")));
+ else if (rel->rd_rel->relkind != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ RelationGetRelationName(rel))));
+
+ /*
+ * check permissions: must have SELECT on table or be in
+ * pg_stat_scan_tables
+ */
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+ ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
+
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
+ RelationGetRelationName(rel));
+
+ /* Scan the relation */
+ scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ hscan = (HeapScanDesc) scan;
+
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+
+ values = (char **) palloc(tupdesc->natts * sizeof(char *));
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ TM_Result htsu;
+ TransactionId xmax;
+ uint16 infomask;
+
+ /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
+ LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ htsu = HeapTupleSatisfiesUpdate(tuple,
+ GetCurrentCommandId(false),
+ hscan->rs_cbuf);
+ xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
+ infomask = tuple->t_data->t_infomask;
+
+ /*
+ * A tuple is locked if HTSU returns BeingModified.
+ */
+ if (htsu == TM_BeingModified)
+ {
+ values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
+ PointerGetDatum(&tuple->t_self));
+
+ values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
+ snprintf(values[Atnum_xmax], NCHARS, "%d", xmax);
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactMember *members;
+ int nmembers;
+ bool first = true;
+ bool allow_old;
+
+ values[Atnum_ismulti] = pstrdup("true");
+
+ allow_old = HEAP_LOCKED_UPGRADED(infomask);
+ nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
+ false);
+ if (nmembers == -1)
+ {
+ values[Atnum_xids] = "{0}";
+ values[Atnum_modes] = "{transient upgrade status}";
+ values[Atnum_pids] = "{0}";
+ }
+ else
+ {
+ int j;
+
+ values[Atnum_xids] = palloc(NCHARS * nmembers);
+ values[Atnum_modes] = palloc(NCHARS * nmembers);
+ values[Atnum_pids] = palloc(NCHARS * nmembers);
+
+ strcpy(values[Atnum_xids], "{");
+ strcpy(values[Atnum_modes], "{");
+ strcpy(values[Atnum_pids], "{");
+
+ for (j = 0; j < nmembers; j++)
+ {
+ char buf[NCHARS];
+
+ if (!first)
+ {
+ strcat(values[Atnum_xids], ",");
+ strcat(values[Atnum_modes], ",");
+ strcat(values[Atnum_pids], ",");
+ }
+ snprintf(buf, NCHARS, "%d", members[j].xid);
+ strcat(values[Atnum_xids], buf);
+ switch (members[j].status)
+ {
+ case MultiXactStatusUpdate:
+ snprintf(buf, NCHARS, "Update");
+ break;
+ case MultiXactStatusNoKeyUpdate:
+ snprintf(buf, NCHARS, "No Key Update");
+ break;
+ case MultiXactStatusForUpdate:
+ snprintf(buf, NCHARS, "For Update");
+ break;
+ case MultiXactStatusForNoKeyUpdate:
+ snprintf(buf, NCHARS, "For No Key Update");
+ break;
+ case MultiXactStatusForShare:
+ snprintf(buf, NCHARS, "Share");
+ break;
+ case MultiXactStatusForKeyShare:
+ snprintf(buf, NCHARS, "Key Share");
+ break;
+ }
+ strcat(values[Atnum_modes], buf);
+ snprintf(buf, NCHARS, "%d",
+ BackendXidGetPid(members[j].xid));
+ strcat(values[Atnum_pids], buf);
+
+ first = false;
+ }
+
+ strcat(values[Atnum_xids], "}");
+ strcat(values[Atnum_modes], "}");
+ strcat(values[Atnum_pids], "}");
+ }
+ }
+ else
+ {
+ values[Atnum_ismulti] = pstrdup("false");
+
+ values[Atnum_xids] = palloc(NCHARS * sizeof(char));
+ snprintf(values[Atnum_xids], NCHARS, "{%d}", xmax);
+
+ values[Atnum_modes] = palloc(NCHARS);
+ if (infomask & HEAP_XMAX_LOCK_ONLY)
+ {
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
+ snprintf(values[Atnum_modes], NCHARS, "{For Share}");
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
+ snprintf(values[Atnum_modes], NCHARS, "{For Update}");
+ else
+ snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
+ }
+ else
+ /* neither keyshare nor exclusive bit it set */
+ snprintf(values[Atnum_modes], NCHARS,
+ "{transient upgrade status}");
+ }
+ else
+ {
+ if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
+ snprintf(values[Atnum_modes], NCHARS, "{Update}");
+ else
+ snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
+ }
+
+ values[Atnum_pids] = palloc(NCHARS * sizeof(char));
+ snprintf(values[Atnum_pids], NCHARS, "{%d}",
+ BackendXidGetPid(xmax));
+ }
+
+ LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /* build a tuple */
+ tuple = BuildTupleFromCStrings(attinmeta, values);
+ tuplestore_puttuple(tupstore, tuple);
+ }
+ else
+ {
+ LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ }
+ }
+
+ table_endscan(scan);
+ table_close(rel, AccessShareLock);
+ return (Datum) 0;
+}
diff --git a/contrib/pgrowlocks/pgrowlocks.control b/contrib/pgrowlocks/pgrowlocks.control
new file mode 100644
index 0000000..9f92b2f
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks.control
@@ -0,0 +1,5 @@
+# pgrowlocks extension
+comment = 'show row-level locking information'
+default_version = '1.2'
+module_pathname = '$libdir/pgrowlocks'
+relocatable = true