Re: obtaining row locking information - Mailing list pgsql-hackers
| From | Tatsuo Ishii |
|---|---|
| Subject | Re: obtaining row locking information |
| Date | |
| Msg-id | 20050812.231011.78703789.t-ishii@sra.co.jp Whole thread Raw |
| In response to | Re: obtaining row locking information (Alvaro Herrera <alvherre@alvh.no-ip.org>) |
| Responses |
Re: obtaining row locking information
|
| List | pgsql-hackers |
> On Fri, Aug 12, 2005 at 02:08:29PM +0900, Tatsuo Ishii wrote:
> > > On Fri, Aug 12, 2005 at 12:27:25PM +0900, Tatsuo Ishii wrote:
> > >
> > > > However even one of transactions, for example 647 commits, still it
> > > > shows as if 647 is a member of muitixid 3.
> > > >
> > > > test=# select * from pgrowlocks('t1');
> > > > locked_row | lock_type | locker | multi | xids
> > > > ------------+-----------+--------+-------+-----------
> > > > (0,1) | Shared | 3 | t | {646,647}
> > > > (1 row)
> > > >
> > > > Am I missing something?
> > >
> > > By design, a MultiXactId does not change its membership, that is, no
> > > members are added nor deleted. When this has to happen (for example a
> > > row is locked by another backend), a new MultiXactId is generated. The
> > > caller is expected to check whether the member transactions are still
> > > running.
> >
> > But it seems when members are deleted, new multixid is not
> > generated. i.e. I see "locker" column does not change. Is this an
> > expected behavior?
>
> Yes. Members are never deleted. This is for two reasons: first, the
> transaction could theoretically hold millions of MultiXactId, and we
> can't expect it to remember them all; so we don't have a way to find out
> which ones it should clean up when it finishes (a process which would be
> slow and cumbersome anyway). Second, because the implementation does
> not really allow for shrinking (nor enlarging) an array. Once created,
> the array is immutable.
>
> If you locked a tuple with transactions B and C; then transaction B
> committed; then transaction D locked the tuple again, you would see a
> new MultiXactId comprising transactions C and D.
Ok, here is the new version of the function which now checks if the
transactions are still running.
BTW, I think it would be helpfull if the function returns the process
id which runs the transaction. I couldn't find any existing function
which converts an xid to a process id so far, and think inventing
someting like BackendPidGetProc(int pid) would be the way I should
go. Any suggestion?
--
Tatsuo Ishii
/** $PostgreSQL$** Copyright (c) 2005 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
theabove* copyright notice and this paragraph and the following two* paragraphs appear in all copies.** IN NO EVENT
SHALLTHE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING*
LOSTPROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS* DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS
BEENADVISED* OF THE POSSIBILITY OF SUCH DAMAGE.** THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT*
LIMITEDTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED
HEREUNDERIS ON AN "AS* IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,* SUPPORT, UPDATES,
ENHANCEMENTS,OR MODIFICATIONS.*/
#include "postgres.h"
#include "funcapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/transam.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
PG_FUNCTION_INFO_V1(pgrowlocks);
extern Datum pgrowlocks(PG_FUNCTION_ARGS);
/* ----------* pgrowlocks:* returns tids of rows being locked** C FUNCTION definition* pgrowlocks(text) returns set of
pgrowlocks_type*see pgrowlocks.sql for pgrowlocks_type* ----------*/
#define DUMMY_TUPLE "public.pgrowlocks_type"
#define NCHARS 32
/** define this if makeRangeVarFromNameList() has two arguments. As far* as I know, this only happens in 8.0.x.*/
#undef MAKERANGEVARFROMNAMELIST_HAS_TWO_ARGS
typedef struct {HeapScanDesc scan;int ncolumns;
} MyData;
Datum
pgrowlocks(PG_FUNCTION_ARGS)
{FuncCallContext *funcctx;HeapScanDesc scan;HeapTuple tuple;TupleDesc tupdesc;AttInMetadata *attinmeta;Datum
result;MyData *mydata;Relation rel;
if (SRF_IS_FIRSTCALL()){ text *relname; RangeVar *relrv; MemoryContext oldcontext;
funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE); attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta= attinmeta;
relname = PG_GETARG_TEXT_P(0);
#ifdef MAKERANGEVARFROMNAMELIST_HAS_TWO_ARGS relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
"pgrowlocks"));
#else relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
#endif rel = heap_openrv(relrv, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); mydata =
palloc(sizeof(*mydata)); mydata->scan = scan; mydata->ncolumns = tupdesc->natts; funcctx->user_fctx = mydata;
MemoryContextSwitchTo(oldcontext);}
funcctx = SRF_PERCALL_SETUP();attinmeta = funcctx->attinmeta;mydata = (MyData *)funcctx->user_fctx;scan =
mydata->scan;
/* scan the relation */while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL){ /* must hold a buffer
lockto call HeapTupleSatisfiesUpdate */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
if (HeapTupleSatisfiesUpdate(tuple->t_data, GetCurrentCommandId(), scan->rs_cbuf) == HeapTupleBeingUpdated)
{
char **values; int i;
values = (char **) palloc(mydata->ncolumns * sizeof(char *));
i = 0; values[i++] = (char *)DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self));
#ifdef HEAP_XMAX_SHARED_LOCK if (tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK) values[i++] =
pstrdup("Shared"); else values[i++] = pstrdup("Exclusive");
#else values[i++] = pstrdup("Exclusive");
#endif values[i] = palloc(NCHARS*sizeof(char)); snprintf(values[i++], NCHARS, "%d",
HeapTupleHeaderGetXmax(tuple->t_data));
#ifdef HEAP_XMAX_SHARED_LOCK if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) {
TransactionId*xids; int nxids; int j; int isValidXid = 0; /* any valid xid ever
exists?*/
values[i++] = pstrdup("true"); nxids =
GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple->t_data),&xids); if (nxids == -1) {
elog(ERROR, "GetMultiXactIdMembers returns error"); }
values[i] = palloc(NCHARS*nxids); strcpy(values[i], "{");
for (j=0;j<nxids;j++) { char buf[NCHARS];
if (TransactionIdIsInProgress(xids[j])) { if (isValidXid)
{ strcat(values[i], ","); } snprintf(buf, NCHARS,
"%d",xids[j]); strcat(values[i], buf); isValidXid = 1; }
}
strcat(values[i], "}"); i++; } else { values[i++] =
pstrdup("false"); values[i++] = pstrdup("{}"); }
#else values[i++] = pstrdup("false"); values[i++] = pstrdup("{}");
#endif
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
/* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */ result = HeapTupleGetDatum(tuple);
/* Clean up */ for (i = 0; i < mydata->ncolumns; i++) pfree(values[i]);
pfree(values);
SRF_RETURN_NEXT(funcctx, result); } else { LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}}
heap_endscan(scan);heap_close(scan->rs_rd, AccessShareLock);
SRF_RETURN_DONE(funcctx);
}
pgsql-hackers by date: