Header And Logo

PostgreSQL
| The world's most advanced open source database.

lmgr.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * lmgr.c
00004  *    POSTGRES lock manager code
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/storage/lmgr/lmgr.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 
00016 #include "postgres.h"
00017 
00018 #include "access/subtrans.h"
00019 #include "access/transam.h"
00020 #include "access/xact.h"
00021 #include "catalog/catalog.h"
00022 #include "miscadmin.h"
00023 #include "storage/lmgr.h"
00024 #include "storage/procarray.h"
00025 #include "utils/inval.h"
00026 
00027 
00028 /*
00029  * RelationInitLockInfo
00030  *      Initializes the lock information in a relation descriptor.
00031  *
00032  *      relcache.c must call this during creation of any reldesc.
00033  */
00034 void
00035 RelationInitLockInfo(Relation relation)
00036 {
00037     Assert(RelationIsValid(relation));
00038     Assert(OidIsValid(RelationGetRelid(relation)));
00039 
00040     relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
00041 
00042     if (relation->rd_rel->relisshared)
00043         relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
00044     else
00045         relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
00046 }
00047 
00048 /*
00049  * SetLocktagRelationOid
00050  *      Set up a locktag for a relation, given only relation OID
00051  */
00052 static inline void
00053 SetLocktagRelationOid(LOCKTAG *tag, Oid relid)
00054 {
00055     Oid         dbid;
00056 
00057     if (IsSharedRelation(relid))
00058         dbid = InvalidOid;
00059     else
00060         dbid = MyDatabaseId;
00061 
00062     SET_LOCKTAG_RELATION(*tag, dbid, relid);
00063 }
00064 
00065 /*
00066  *      LockRelationOid
00067  *
00068  * Lock a relation given only its OID.  This should generally be used
00069  * before attempting to open the relation's relcache entry.
00070  */
00071 void
00072 LockRelationOid(Oid relid, LOCKMODE lockmode)
00073 {
00074     LOCKTAG     tag;
00075     LockAcquireResult res;
00076 
00077     SetLocktagRelationOid(&tag, relid);
00078 
00079     res = LockAcquire(&tag, lockmode, false, false);
00080 
00081     /*
00082      * Now that we have the lock, check for invalidation messages, so that we
00083      * will update or flush any stale relcache entry before we try to use it.
00084      * RangeVarGetRelid() specifically relies on us for this.  We can skip
00085      * this in the not-uncommon case that we already had the same type of lock
00086      * being requested, since then no one else could have modified the
00087      * relcache entry in an undesirable way.  (In the case where our own xact
00088      * modifies the rel, the relcache update happens via
00089      * CommandCounterIncrement, not here.)
00090      */
00091     if (res != LOCKACQUIRE_ALREADY_HELD)
00092         AcceptInvalidationMessages();
00093 }
00094 
00095 /*
00096  *      ConditionalLockRelationOid
00097  *
00098  * As above, but only lock if we can get the lock without blocking.
00099  * Returns TRUE iff the lock was acquired.
00100  *
00101  * NOTE: we do not currently need conditional versions of all the
00102  * LockXXX routines in this file, but they could easily be added if needed.
00103  */
00104 bool
00105 ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
00106 {
00107     LOCKTAG     tag;
00108     LockAcquireResult res;
00109 
00110     SetLocktagRelationOid(&tag, relid);
00111 
00112     res = LockAcquire(&tag, lockmode, false, true);
00113 
00114     if (res == LOCKACQUIRE_NOT_AVAIL)
00115         return false;
00116 
00117     /*
00118      * Now that we have the lock, check for invalidation messages; see notes
00119      * in LockRelationOid.
00120      */
00121     if (res != LOCKACQUIRE_ALREADY_HELD)
00122         AcceptInvalidationMessages();
00123 
00124     return true;
00125 }
00126 
00127 /*
00128  *      UnlockRelationId
00129  *
00130  * Unlock, given a LockRelId.  This is preferred over UnlockRelationOid
00131  * for speed reasons.
00132  */
00133 void
00134 UnlockRelationId(LockRelId *relid, LOCKMODE lockmode)
00135 {
00136     LOCKTAG     tag;
00137 
00138     SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
00139 
00140     LockRelease(&tag, lockmode, false);
00141 }
00142 
00143 /*
00144  *      UnlockRelationOid
00145  *
00146  * Unlock, given only a relation Oid.  Use UnlockRelationId if you can.
00147  */
00148 void
00149 UnlockRelationOid(Oid relid, LOCKMODE lockmode)
00150 {
00151     LOCKTAG     tag;
00152 
00153     SetLocktagRelationOid(&tag, relid);
00154 
00155     LockRelease(&tag, lockmode, false);
00156 }
00157 
00158 /*
00159  *      LockRelation
00160  *
00161  * This is a convenience routine for acquiring an additional lock on an
00162  * already-open relation.  Never try to do "relation_open(foo, NoLock)"
00163  * and then lock with this.
00164  */
00165 void
00166 LockRelation(Relation relation, LOCKMODE lockmode)
00167 {
00168     LOCKTAG     tag;
00169     LockAcquireResult res;
00170 
00171     SET_LOCKTAG_RELATION(tag,
00172                          relation->rd_lockInfo.lockRelId.dbId,
00173                          relation->rd_lockInfo.lockRelId.relId);
00174 
00175     res = LockAcquire(&tag, lockmode, false, false);
00176 
00177     /*
00178      * Now that we have the lock, check for invalidation messages; see notes
00179      * in LockRelationOid.
00180      */
00181     if (res != LOCKACQUIRE_ALREADY_HELD)
00182         AcceptInvalidationMessages();
00183 }
00184 
00185 /*
00186  *      ConditionalLockRelation
00187  *
00188  * This is a convenience routine for acquiring an additional lock on an
00189  * already-open relation.  Never try to do "relation_open(foo, NoLock)"
00190  * and then lock with this.
00191  */
00192 bool
00193 ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
00194 {
00195     LOCKTAG     tag;
00196     LockAcquireResult res;
00197 
00198     SET_LOCKTAG_RELATION(tag,
00199                          relation->rd_lockInfo.lockRelId.dbId,
00200                          relation->rd_lockInfo.lockRelId.relId);
00201 
00202     res = LockAcquire(&tag, lockmode, false, true);
00203 
00204     if (res == LOCKACQUIRE_NOT_AVAIL)
00205         return false;
00206 
00207     /*
00208      * Now that we have the lock, check for invalidation messages; see notes
00209      * in LockRelationOid.
00210      */
00211     if (res != LOCKACQUIRE_ALREADY_HELD)
00212         AcceptInvalidationMessages();
00213 
00214     return true;
00215 }
00216 
00217 /*
00218  *      UnlockRelation
00219  *
00220  * This is a convenience routine for unlocking a relation without also
00221  * closing it.
00222  */
00223 void
00224 UnlockRelation(Relation relation, LOCKMODE lockmode)
00225 {
00226     LOCKTAG     tag;
00227 
00228     SET_LOCKTAG_RELATION(tag,
00229                          relation->rd_lockInfo.lockRelId.dbId,
00230                          relation->rd_lockInfo.lockRelId.relId);
00231 
00232     LockRelease(&tag, lockmode, false);
00233 }
00234 
00235 /*
00236  *      LockHasWaitersRelation
00237  *
00238  * This is a functiion to check if someone else is waiting on a
00239  * lock, we are currently holding.
00240  */
00241 bool
00242 LockHasWaitersRelation(Relation relation, LOCKMODE lockmode)
00243 {
00244     LOCKTAG     tag;
00245 
00246     SET_LOCKTAG_RELATION(tag,
00247                          relation->rd_lockInfo.lockRelId.dbId,
00248                          relation->rd_lockInfo.lockRelId.relId);
00249 
00250     return LockHasWaiters(&tag, lockmode, false);
00251 }
00252 
00253 /*
00254  *      LockRelationIdForSession
00255  *
00256  * This routine grabs a session-level lock on the target relation.  The
00257  * session lock persists across transaction boundaries.  It will be removed
00258  * when UnlockRelationIdForSession() is called, or if an ereport(ERROR) occurs,
00259  * or if the backend exits.
00260  *
00261  * Note that one should also grab a transaction-level lock on the rel
00262  * in any transaction that actually uses the rel, to ensure that the
00263  * relcache entry is up to date.
00264  */
00265 void
00266 LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
00267 {
00268     LOCKTAG     tag;
00269 
00270     SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
00271 
00272     (void) LockAcquire(&tag, lockmode, true, false);
00273 }
00274 
00275 /*
00276  *      UnlockRelationIdForSession
00277  */
00278 void
00279 UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
00280 {
00281     LOCKTAG     tag;
00282 
00283     SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
00284 
00285     LockRelease(&tag, lockmode, true);
00286 }
00287 
00288 /*
00289  *      LockRelationForExtension
00290  *
00291  * This lock tag is used to interlock addition of pages to relations.
00292  * We need such locking because bufmgr/smgr definition of P_NEW is not
00293  * race-condition-proof.
00294  *
00295  * We assume the caller is already holding some type of regular lock on
00296  * the relation, so no AcceptInvalidationMessages call is needed here.
00297  */
00298 void
00299 LockRelationForExtension(Relation relation, LOCKMODE lockmode)
00300 {
00301     LOCKTAG     tag;
00302 
00303     SET_LOCKTAG_RELATION_EXTEND(tag,
00304                                 relation->rd_lockInfo.lockRelId.dbId,
00305                                 relation->rd_lockInfo.lockRelId.relId);
00306 
00307     (void) LockAcquire(&tag, lockmode, false, false);
00308 }
00309 
00310 /*
00311  *      UnlockRelationForExtension
00312  */
00313 void
00314 UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
00315 {
00316     LOCKTAG     tag;
00317 
00318     SET_LOCKTAG_RELATION_EXTEND(tag,
00319                                 relation->rd_lockInfo.lockRelId.dbId,
00320                                 relation->rd_lockInfo.lockRelId.relId);
00321 
00322     LockRelease(&tag, lockmode, false);
00323 }
00324 
00325 /*
00326  *      LockPage
00327  *
00328  * Obtain a page-level lock.  This is currently used by some index access
00329  * methods to lock individual index pages.
00330  */
00331 void
00332 LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
00333 {
00334     LOCKTAG     tag;
00335 
00336     SET_LOCKTAG_PAGE(tag,
00337                      relation->rd_lockInfo.lockRelId.dbId,
00338                      relation->rd_lockInfo.lockRelId.relId,
00339                      blkno);
00340 
00341     (void) LockAcquire(&tag, lockmode, false, false);
00342 }
00343 
00344 /*
00345  *      ConditionalLockPage
00346  *
00347  * As above, but only lock if we can get the lock without blocking.
00348  * Returns TRUE iff the lock was acquired.
00349  */
00350 bool
00351 ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
00352 {
00353     LOCKTAG     tag;
00354 
00355     SET_LOCKTAG_PAGE(tag,
00356                      relation->rd_lockInfo.lockRelId.dbId,
00357                      relation->rd_lockInfo.lockRelId.relId,
00358                      blkno);
00359 
00360     return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
00361 }
00362 
00363 /*
00364  *      UnlockPage
00365  */
00366 void
00367 UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
00368 {
00369     LOCKTAG     tag;
00370 
00371     SET_LOCKTAG_PAGE(tag,
00372                      relation->rd_lockInfo.lockRelId.dbId,
00373                      relation->rd_lockInfo.lockRelId.relId,
00374                      blkno);
00375 
00376     LockRelease(&tag, lockmode, false);
00377 }
00378 
00379 /*
00380  *      LockTuple
00381  *
00382  * Obtain a tuple-level lock.  This is used in a less-than-intuitive fashion
00383  * because we can't afford to keep a separate lock in shared memory for every
00384  * tuple.  See heap_lock_tuple before using this!
00385  */
00386 void
00387 LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
00388 {
00389     LOCKTAG     tag;
00390 
00391     SET_LOCKTAG_TUPLE(tag,
00392                       relation->rd_lockInfo.lockRelId.dbId,
00393                       relation->rd_lockInfo.lockRelId.relId,
00394                       ItemPointerGetBlockNumber(tid),
00395                       ItemPointerGetOffsetNumber(tid));
00396 
00397     (void) LockAcquire(&tag, lockmode, false, false);
00398 }
00399 
00400 /*
00401  *      ConditionalLockTuple
00402  *
00403  * As above, but only lock if we can get the lock without blocking.
00404  * Returns TRUE iff the lock was acquired.
00405  */
00406 bool
00407 ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
00408 {
00409     LOCKTAG     tag;
00410 
00411     SET_LOCKTAG_TUPLE(tag,
00412                       relation->rd_lockInfo.lockRelId.dbId,
00413                       relation->rd_lockInfo.lockRelId.relId,
00414                       ItemPointerGetBlockNumber(tid),
00415                       ItemPointerGetOffsetNumber(tid));
00416 
00417     return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
00418 }
00419 
00420 /*
00421  *      UnlockTuple
00422  */
00423 void
00424 UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
00425 {
00426     LOCKTAG     tag;
00427 
00428     SET_LOCKTAG_TUPLE(tag,
00429                       relation->rd_lockInfo.lockRelId.dbId,
00430                       relation->rd_lockInfo.lockRelId.relId,
00431                       ItemPointerGetBlockNumber(tid),
00432                       ItemPointerGetOffsetNumber(tid));
00433 
00434     LockRelease(&tag, lockmode, false);
00435 }
00436 
00437 /*
00438  *      XactLockTableInsert
00439  *
00440  * Insert a lock showing that the given transaction ID is running ---
00441  * this is done when an XID is acquired by a transaction or subtransaction.
00442  * The lock can then be used to wait for the transaction to finish.
00443  */
00444 void
00445 XactLockTableInsert(TransactionId xid)
00446 {
00447     LOCKTAG     tag;
00448 
00449     SET_LOCKTAG_TRANSACTION(tag, xid);
00450 
00451     (void) LockAcquire(&tag, ExclusiveLock, false, false);
00452 }
00453 
00454 /*
00455  *      XactLockTableDelete
00456  *
00457  * Delete the lock showing that the given transaction ID is running.
00458  * (This is never used for main transaction IDs; those locks are only
00459  * released implicitly at transaction end.  But we do use it for subtrans IDs.)
00460  */
00461 void
00462 XactLockTableDelete(TransactionId xid)
00463 {
00464     LOCKTAG     tag;
00465 
00466     SET_LOCKTAG_TRANSACTION(tag, xid);
00467 
00468     LockRelease(&tag, ExclusiveLock, false);
00469 }
00470 
00471 /*
00472  *      XactLockTableWait
00473  *
00474  * Wait for the specified transaction to commit or abort.
00475  *
00476  * Note that this does the right thing for subtransactions: if we wait on a
00477  * subtransaction, we will exit as soon as it aborts or its top parent commits.
00478  * It takes some extra work to ensure this, because to save on shared memory
00479  * the XID lock of a subtransaction is released when it ends, whether
00480  * successfully or unsuccessfully.  So we have to check if it's "still running"
00481  * and if so wait for its parent.
00482  */
00483 void
00484 XactLockTableWait(TransactionId xid)
00485 {
00486     LOCKTAG     tag;
00487 
00488     for (;;)
00489     {
00490         Assert(TransactionIdIsValid(xid));
00491         Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
00492 
00493         SET_LOCKTAG_TRANSACTION(tag, xid);
00494 
00495         (void) LockAcquire(&tag, ShareLock, false, false);
00496 
00497         LockRelease(&tag, ShareLock, false);
00498 
00499         if (!TransactionIdIsInProgress(xid))
00500             break;
00501         xid = SubTransGetParent(xid);
00502     }
00503 }
00504 
00505 /*
00506  *      ConditionalXactLockTableWait
00507  *
00508  * As above, but only lock if we can get the lock without blocking.
00509  * Returns TRUE if the lock was acquired.
00510  */
00511 bool
00512 ConditionalXactLockTableWait(TransactionId xid)
00513 {
00514     LOCKTAG     tag;
00515 
00516     for (;;)
00517     {
00518         Assert(TransactionIdIsValid(xid));
00519         Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
00520 
00521         SET_LOCKTAG_TRANSACTION(tag, xid);
00522 
00523         if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
00524             return false;
00525 
00526         LockRelease(&tag, ShareLock, false);
00527 
00528         if (!TransactionIdIsInProgress(xid))
00529             break;
00530         xid = SubTransGetParent(xid);
00531     }
00532 
00533     return true;
00534 }
00535 
00536 /*
00537  *      LockDatabaseObject
00538  *
00539  * Obtain a lock on a general object of the current database.  Don't use
00540  * this for shared objects (such as tablespaces).  It's unwise to apply it
00541  * to relations, also, since a lock taken this way will NOT conflict with
00542  * locks taken via LockRelation and friends.
00543  */
00544 void
00545 LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
00546                    LOCKMODE lockmode)
00547 {
00548     LOCKTAG     tag;
00549 
00550     SET_LOCKTAG_OBJECT(tag,
00551                        MyDatabaseId,
00552                        classid,
00553                        objid,
00554                        objsubid);
00555 
00556     (void) LockAcquire(&tag, lockmode, false, false);
00557 
00558     /* Make sure syscaches are up-to-date with any changes we waited for */
00559     AcceptInvalidationMessages();
00560 }
00561 
00562 /*
00563  *      UnlockDatabaseObject
00564  */
00565 void
00566 UnlockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
00567                      LOCKMODE lockmode)
00568 {
00569     LOCKTAG     tag;
00570 
00571     SET_LOCKTAG_OBJECT(tag,
00572                        MyDatabaseId,
00573                        classid,
00574                        objid,
00575                        objsubid);
00576 
00577     LockRelease(&tag, lockmode, false);
00578 }
00579 
00580 /*
00581  *      LockSharedObject
00582  *
00583  * Obtain a lock on a shared-across-databases object.
00584  */
00585 void
00586 LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
00587                  LOCKMODE lockmode)
00588 {
00589     LOCKTAG     tag;
00590 
00591     SET_LOCKTAG_OBJECT(tag,
00592                        InvalidOid,
00593                        classid,
00594                        objid,
00595                        objsubid);
00596 
00597     (void) LockAcquire(&tag, lockmode, false, false);
00598 
00599     /* Make sure syscaches are up-to-date with any changes we waited for */
00600     AcceptInvalidationMessages();
00601 }
00602 
00603 /*
00604  *      UnlockSharedObject
00605  */
00606 void
00607 UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
00608                    LOCKMODE lockmode)
00609 {
00610     LOCKTAG     tag;
00611 
00612     SET_LOCKTAG_OBJECT(tag,
00613                        InvalidOid,
00614                        classid,
00615                        objid,
00616                        objsubid);
00617 
00618     LockRelease(&tag, lockmode, false);
00619 }
00620 
00621 /*
00622  *      LockSharedObjectForSession
00623  *
00624  * Obtain a session-level lock on a shared-across-databases object.
00625  * See LockRelationIdForSession for notes about session-level locks.
00626  */
00627 void
00628 LockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
00629                            LOCKMODE lockmode)
00630 {
00631     LOCKTAG     tag;
00632 
00633     SET_LOCKTAG_OBJECT(tag,
00634                        InvalidOid,
00635                        classid,
00636                        objid,
00637                        objsubid);
00638 
00639     (void) LockAcquire(&tag, lockmode, true, false);
00640 }
00641 
00642 /*
00643  *      UnlockSharedObjectForSession
00644  */
00645 void
00646 UnlockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
00647                              LOCKMODE lockmode)
00648 {
00649     LOCKTAG     tag;
00650 
00651     SET_LOCKTAG_OBJECT(tag,
00652                        InvalidOid,
00653                        classid,
00654                        objid,
00655                        objsubid);
00656 
00657     LockRelease(&tag, lockmode, true);
00658 }
00659 
00660 
00661 /*
00662  * Append a description of a lockable object to buf.
00663  *
00664  * Ideally we would print names for the numeric values, but that requires
00665  * getting locks on system tables, which might cause problems since this is
00666  * typically used to report deadlock situations.
00667  */
00668 void
00669 DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
00670 {
00671     switch ((LockTagType) tag->locktag_type)
00672     {
00673         case LOCKTAG_RELATION:
00674             appendStringInfo(buf,
00675                              _("relation %u of database %u"),
00676                              tag->locktag_field2,
00677                              tag->locktag_field1);
00678             break;
00679         case LOCKTAG_RELATION_EXTEND:
00680             appendStringInfo(buf,
00681                              _("extension of relation %u of database %u"),
00682                              tag->locktag_field2,
00683                              tag->locktag_field1);
00684             break;
00685         case LOCKTAG_PAGE:
00686             appendStringInfo(buf,
00687                              _("page %u of relation %u of database %u"),
00688                              tag->locktag_field3,
00689                              tag->locktag_field2,
00690                              tag->locktag_field1);
00691             break;
00692         case LOCKTAG_TUPLE:
00693             appendStringInfo(buf,
00694                              _("tuple (%u,%u) of relation %u of database %u"),
00695                              tag->locktag_field3,
00696                              tag->locktag_field4,
00697                              tag->locktag_field2,
00698                              tag->locktag_field1);
00699             break;
00700         case LOCKTAG_TRANSACTION:
00701             appendStringInfo(buf,
00702                              _("transaction %u"),
00703                              tag->locktag_field1);
00704             break;
00705         case LOCKTAG_VIRTUALTRANSACTION:
00706             appendStringInfo(buf,
00707                              _("virtual transaction %d/%u"),
00708                              tag->locktag_field1,
00709                              tag->locktag_field2);
00710             break;
00711         case LOCKTAG_OBJECT:
00712             appendStringInfo(buf,
00713                              _("object %u of class %u of database %u"),
00714                              tag->locktag_field3,
00715                              tag->locktag_field2,
00716                              tag->locktag_field1);
00717             break;
00718         case LOCKTAG_USERLOCK:
00719             /* reserved for old contrib code, now on pgfoundry */
00720             appendStringInfo(buf,
00721                              _("user lock [%u,%u,%u]"),
00722                              tag->locktag_field1,
00723                              tag->locktag_field2,
00724                              tag->locktag_field3);
00725             break;
00726         case LOCKTAG_ADVISORY:
00727             appendStringInfo(buf,
00728                              _("advisory lock [%u,%u,%u,%u]"),
00729                              tag->locktag_field1,
00730                              tag->locktag_field2,
00731                              tag->locktag_field3,
00732                              tag->locktag_field4);
00733             break;
00734         default:
00735             appendStringInfo(buf,
00736                              _("unrecognized locktag type %d"),
00737                              (int) tag->locktag_type);
00738             break;
00739     }
00740 }