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?
--
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/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)
                          values[i++] = pstrdup("true");
                        else
                          values[i++] = pstrdup("false");
#else
                        values[i++] = pstrdup("false");
#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 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to