From 293913568e6a7a86fd1479e1cff8e2ecb58d6568 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 15:44:03 +0200 Subject: Adding upstream version 16.2. Signed-off-by: Daniel Baumann --- contrib/pgrowlocks/.gitignore | 6 + contrib/pgrowlocks/Makefile | 24 +++ contrib/pgrowlocks/expected/pgrowlocks.out | 233 +++++++++++++++++++++++ contrib/pgrowlocks/meson.build | 37 ++++ contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql | 17 ++ contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql | 6 + contrib/pgrowlocks/pgrowlocks--1.2.sql | 15 ++ contrib/pgrowlocks/pgrowlocks.c | 276 ++++++++++++++++++++++++++++ contrib/pgrowlocks/pgrowlocks.control | 5 + contrib/pgrowlocks/specs/pgrowlocks.spec | 39 ++++ 10 files changed, 658 insertions(+) create mode 100644 contrib/pgrowlocks/.gitignore create mode 100644 contrib/pgrowlocks/Makefile create mode 100644 contrib/pgrowlocks/expected/pgrowlocks.out create mode 100644 contrib/pgrowlocks/meson.build create mode 100644 contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql create mode 100644 contrib/pgrowlocks/pgrowlocks--1.1--1.2.sql create mode 100644 contrib/pgrowlocks/pgrowlocks--1.2.sql create mode 100644 contrib/pgrowlocks/pgrowlocks.c create mode 100644 contrib/pgrowlocks/pgrowlocks.control create mode 100644 contrib/pgrowlocks/specs/pgrowlocks.spec (limited to 'contrib/pgrowlocks') diff --git a/contrib/pgrowlocks/.gitignore b/contrib/pgrowlocks/.gitignore new file mode 100644 index 0000000..b4903eb --- /dev/null +++ b/contrib/pgrowlocks/.gitignore @@ -0,0 +1,6 @@ +# Generated subdirectories +/log/ +/results/ +/output_iso/ +/tmp_check/ +/tmp_check_iso/ diff --git a/contrib/pgrowlocks/Makefile b/contrib/pgrowlocks/Makefile new file mode 100644 index 0000000..e808064 --- /dev/null +++ b/contrib/pgrowlocks/Makefile @@ -0,0 +1,24 @@ +# 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" + +ISOLATION = pgrowlocks +ISOLATION_OPTS = --load-extension=pgrowlocks + +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/expected/pgrowlocks.out b/contrib/pgrowlocks/expected/pgrowlocks.out new file mode 100644 index 0000000..7254672 --- /dev/null +++ b/contrib/pgrowlocks/expected/pgrowlocks.out @@ -0,0 +1,233 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_begin s1_tuplock1 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_tuplock1: SELECT * FROM multixact_conflict FOR KEY SHARE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+----------------- +(0,1) |f |{"For Key Share"} +(0,2) |f |{"For Key Share"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_tuplock2 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_tuplock2: SELECT * FROM multixact_conflict FOR SHARE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+------------- +(0,1) |f |{"For Share"} +(0,2) |f |{"For Share"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_tuplock3 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_tuplock3: SELECT * FROM multixact_conflict FOR NO KEY UPDATE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+--------------------- +(0,1) |f |{"For No Key Update"} +(0,2) |f |{"For No Key Update"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_tuplock4 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_tuplock4: SELECT * FROM multixact_conflict FOR UPDATE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+-------------- +(0,1) |f |{"For Update"} +(0,2) |f |{"For Update"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_updatea s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_updatea: UPDATE multixact_conflict SET a = 10 WHERE a = 1; +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+-------- +(0,1) |f |{Update} +(1 row) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_updateb s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_updateb: UPDATE multixact_conflict SET b = 11 WHERE b = 4; +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+----------------- +(0,2) |f |{"No Key Update"} +(1 row) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_tuplock1 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_tuplock1: SELECT * FROM multixact_conflict FOR KEY SHARE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+----------------- +(0,1) |f |{"For Key Share"} +(0,2) |f |{"For Key Share"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_tuplock2 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_tuplock2: SELECT * FROM multixact_conflict FOR SHARE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+------------------- +(0,1) |t |{"Key Share",Share} +(0,2) |t |{"Key Share",Share} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_tuplock3 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_tuplock3: SELECT * FROM multixact_conflict FOR NO KEY UPDATE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+--------------------------------- +(0,1) |t |{"Key Share","For No Key Update"} +(0,2) |t |{"Key Share","For No Key Update"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_tuplock4 s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_tuplock4: SELECT * FROM multixact_conflict FOR UPDATE; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+-------------------------- +(0,1) |t |{"Key Share","For Update"} +(0,2) |t |{"Key Share","For Update"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_updatea s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_updatea: UPDATE multixact_conflict SET a = 10 WHERE a = 1; +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+-------------------- +(0,1) |t |{"Key Share",Update} +(0,2) |f |{"For Key Share"} +(2 rows) + +step s1_commit: COMMIT; + +starting permutation: s1_begin s1_lcksvpt s1_updateb s2_rowlocks s1_commit +step s1_begin: BEGIN; +step s1_lcksvpt: SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; +a|b +-+- +1|2 +3|4 +(2 rows) + +step s1_updateb: UPDATE multixact_conflict SET b = 11 WHERE b = 4; +step s2_rowlocks: SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); +locked_row|multi|modes +----------+-----+----------------------------- +(0,1) |f |{"For Key Share"} +(0,2) |t |{"Key Share","No Key Update"} +(2 rows) + +step s1_commit: COMMIT; diff --git a/contrib/pgrowlocks/meson.build b/contrib/pgrowlocks/meson.build new file mode 100644 index 0000000..48bce00 --- /dev/null +++ b/contrib/pgrowlocks/meson.build @@ -0,0 +1,37 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +pgrowlocks_sources = files( + 'pgrowlocks.c', +) + +if host_system == 'windows' + pgrowlocks_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'pgrowlocks', + '--FILEDESC', 'pgrowlocks - display row locking information',]) +endif + +pgrowlocks = shared_module('pgrowlocks', + pgrowlocks_sources, + kwargs: contrib_mod_args, +) +contrib_targets += pgrowlocks + +install_data( + 'pgrowlocks--1.0--1.1.sql', + 'pgrowlocks--1.1--1.2.sql', + 'pgrowlocks--1.2.sql', + 'pgrowlocks.control', + kwargs: contrib_data_args, +) + +tests += { + 'name': 'pgrowlocks', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'isolation': { + 'specs': [ + 'pgrowlocks', + ], + 'regress_args': ['--load-extension=pgrowlocks'], + }, +} 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..fd104e3 --- /dev/null +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -0,0 +1,276 @@ +/* + * 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; + AttInMetadata *attinmeta; + Relation rel; + RangeVar *relrv; + TableScanDesc scan; + HeapScanDesc hscan; + HeapTuple tuple; + AclResult aclresult; + char **values; + + InitMaterializedSRF(fcinfo, 0); + + /* Access the table */ + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + 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)))); + else if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only heap AM is supported"))); + + /* + * 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 = has_privs_of_role(GetUserId(), ROLE_PG_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(rsinfo->setDesc); + + values = (char **) palloc(rsinfo->setDesc->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, "%u", 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, "%u", 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, "{%u}", 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(rsinfo->setResult, 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 diff --git a/contrib/pgrowlocks/specs/pgrowlocks.spec b/contrib/pgrowlocks/specs/pgrowlocks.spec new file mode 100644 index 0000000..4f4013c --- /dev/null +++ b/contrib/pgrowlocks/specs/pgrowlocks.spec @@ -0,0 +1,39 @@ +# Tests for contrib/pgrowlocks + +setup { + CREATE TABLE multixact_conflict (a int PRIMARY KEY, b int); + INSERT INTO multixact_conflict VALUES (1, 2), (3, 4); +} + +teardown { + DROP TABLE multixact_conflict; +} + +session s1 +step s1_begin { BEGIN; } +step s1_tuplock1 { SELECT * FROM multixact_conflict FOR KEY SHARE; } +step s1_tuplock2 { SELECT * FROM multixact_conflict FOR SHARE; } +step s1_tuplock3 { SELECT * FROM multixact_conflict FOR NO KEY UPDATE; } +step s1_tuplock4 { SELECT * FROM multixact_conflict FOR UPDATE; } +step s1_updatea { UPDATE multixact_conflict SET a = 10 WHERE a = 1; } +step s1_updateb { UPDATE multixact_conflict SET b = 11 WHERE b = 4; } +step s1_lcksvpt { SELECT * FROM multixact_conflict FOR KEY SHARE; SAVEPOINT s; } +step s1_commit { COMMIT; } + +session s2 +step s2_rowlocks { SELECT locked_row, multi, modes FROM pgrowlocks('multixact_conflict'); } + +permutation s1_begin s1_tuplock1 s2_rowlocks s1_commit +permutation s1_begin s1_tuplock2 s2_rowlocks s1_commit +permutation s1_begin s1_tuplock3 s2_rowlocks s1_commit +permutation s1_begin s1_tuplock4 s2_rowlocks s1_commit +permutation s1_begin s1_updatea s2_rowlocks s1_commit +permutation s1_begin s1_updateb s2_rowlocks s1_commit + +# test multixact cases using savepoints +permutation s1_begin s1_lcksvpt s1_tuplock1 s2_rowlocks s1_commit +permutation s1_begin s1_lcksvpt s1_tuplock2 s2_rowlocks s1_commit +permutation s1_begin s1_lcksvpt s1_tuplock3 s2_rowlocks s1_commit +permutation s1_begin s1_lcksvpt s1_tuplock4 s2_rowlocks s1_commit +permutation s1_begin s1_lcksvpt s1_updatea s2_rowlocks s1_commit +permutation s1_begin s1_lcksvpt s1_updateb s2_rowlocks s1_commit -- cgit v1.2.3