> Hi,
> 
> With a help from Bruce, I wrote a small function which returns row
> locking information(see attached file if you are interested). Here is
> a sample result:
> 
> test=# select * from pgrowlocks('t1');
>  locked_row | lock_type | locker | multi 
> ------------+-----------+--------+-------
>       (0,1) | Shared    |      1 | t
>       (0,3) | Exclusive |    575 | f
> (2 rows)
> 
> I think it will be more usefull if actual xids are shown in the case
> "locker" is a multixid. It seems GetMultiXactIdMembers() does the
> job. Unfortunately that is a static funtcion, however. Is there any
> chance GetMultiXactIdMembers() becomes public funtion?

I enhanced pgrowlocks() to use GetMultiXactIdMembers() so that it
displays each xid belonging to particular multi xid (see attached
source code).

test=# select * from pgrowlocks('t1');
 locked_row | lock_type | locker | multi |   xids    
------------+-----------+--------+-------+-----------
      (0,1) | Shared    |      3 | t     | {646,647}
(1 row)

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?
--
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 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 "funcapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/transam.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.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 lock to 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;

                                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];
                                        snprintf(buf, NCHARS, "%d", xids[j]);
                                        strcat(values[i], buf);
                                        if ((j+1) != nxids)
                                        {
                                                strcat(values[i], ",");
                                        }
                                }
                                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);
}
---------------------------(end of broadcast)---------------------------
TIP 6: explain analyze is your friend

Reply via email to