transam.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:15k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * transam.c
  4.  *   postgres transaction log/time interface routines
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.27.2.2 1999/08/08 20:24:12 tgl Exp $
  11.  *
  12.  * NOTES
  13.  *   This file contains the high level access-method interface to the
  14.  *   transaction system.
  15.  *
  16.  *-------------------------------------------------------------------------
  17.  */
  18. #include "postgres.h"
  19. #include "access/heapam.h"
  20. #include "catalog/catname.h"
  21. static int RecoveryCheckingEnabled(void);
  22. static void TransRecover(Relation logRelation);
  23. static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
  24. static void TransactionLogUpdate(TransactionId transactionId,
  25.  XidStatus status);
  26. /* ----------------
  27.  *   global variables holding pointers to relations used
  28.  *   by the transaction system.  These are initialized by
  29.  *   InitializeTransactionLog().
  30.  * ----------------
  31.  */
  32. Relation LogRelation = (Relation) NULL;
  33. Relation VariableRelation = (Relation) NULL;
  34. /* ----------------
  35.  * global variables holding cached transaction id's and statuses.
  36.  * ----------------
  37.  */
  38. TransactionId cachedTestXid;
  39. XidStatus cachedTestXidStatus;
  40. /* ----------------
  41.  * transaction system constants
  42.  * ----------------
  43.  */
  44. /* ----------------------------------------------------------------
  45.  * transaction system constants
  46.  *
  47.  * read the comments for GetNewTransactionId in order to
  48.  * understand the initial values for AmiTransactionId and
  49.  * FirstTransactionId. -cim 3/23/90
  50.  * ----------------------------------------------------------------
  51.  */
  52. TransactionId NullTransactionId = (TransactionId) 0;
  53. TransactionId AmiTransactionId = (TransactionId) 512;
  54. TransactionId FirstTransactionId = (TransactionId) 514;
  55. /* ----------------
  56.  * transaction recovery state variables
  57.  *
  58.  * When the transaction system is initialized, we may
  59.  * need to do recovery checking.  This decision is decided
  60.  * by the postmaster or the user by supplying the backend
  61.  * with a special flag.  In general, we want to do recovery
  62.  * checking whenever we are running without a postmaster
  63.  * or when the number of backends running under the postmaster
  64.  * goes from zero to one. -cim 3/21/90
  65.  * ----------------
  66.  */
  67. int RecoveryCheckingEnableState = 0;
  68. /* ------------------
  69.  * spinlock for oid generation
  70.  * -----------------
  71.  */
  72. extern int OidGenLockId;
  73. /* ----------------
  74.  * recovery checking accessors
  75.  * ----------------
  76.  */
  77. static int
  78. RecoveryCheckingEnabled(void)
  79. {
  80. return RecoveryCheckingEnableState;
  81. }
  82. #ifdef NOT_USED
  83. static void
  84. SetRecoveryCheckingEnabled(bool state)
  85. {
  86. RecoveryCheckingEnableState = (state == true);
  87. }
  88. #endif
  89. /* ----------------------------------------------------------------
  90.  * postgres log access method interface
  91.  *
  92.  * TransactionLogTest
  93.  * TransactionLogUpdate
  94.  * ========
  95.  *    these functions do work for the interface
  96.  *    functions - they search/retrieve and append/update
  97.  *    information in the log and time relations.
  98.  * ----------------------------------------------------------------
  99.  */
  100. /* --------------------------------
  101.  * TransactionLogTest
  102.  * --------------------------------
  103.  */
  104. static bool /* true/false: does transaction id have
  105.  * specified status? */
  106. TransactionLogTest(TransactionId transactionId, /* transaction id to test */
  107.    XidStatus status) /* transaction status */
  108. {
  109. BlockNumber blockNumber;
  110. XidStatus xidstatus; /* recorded status of xid */
  111. bool fail = false; /* success/failure */
  112. /* ----------------
  113.  * during initialization consider all transactions
  114.  * as having been committed
  115.  * ----------------
  116.  */
  117. if (!RelationIsValid(LogRelation))
  118. return (bool) (status == XID_COMMIT);
  119. /* ----------------
  120.  *  before going to the buffer manager, check our single
  121.  *  item cache to see if we didn't just check the transaction
  122.  *  status a moment ago.
  123.  * ----------------
  124.  */
  125. if (TransactionIdEquals(transactionId, cachedTestXid))
  126. return (bool)
  127. (status == cachedTestXidStatus);
  128. /* ----------------
  129.  * compute the item pointer corresponding to the
  130.  * page containing our transaction id.  We save the item in
  131.  * our cache to speed up things if we happen to ask for the
  132.  * same xid's status more than once.
  133.  * ----------------
  134.  */
  135. TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
  136. xidstatus = TransBlockNumberGetXidStatus(LogRelation,
  137.  blockNumber,
  138.  transactionId,
  139.  &fail);
  140. if (!fail)
  141. {
  142. /*
  143.  * DO NOT cache status for transactions in unknown state !!!
  144.  */
  145. if (xidstatus == XID_COMMIT || xidstatus == XID_ABORT)
  146. {
  147. TransactionIdStore(transactionId, &cachedTestXid);
  148. cachedTestXidStatus = xidstatus;
  149. }
  150. return (bool) (status == xidstatus);
  151. }
  152. /* ----------------
  153.  *   here the block didn't contain the information we wanted
  154.  * ----------------
  155.  */
  156. elog(ERROR, "TransactionLogTest: failed to get xidstatus");
  157. /*
  158.  * so lint is happy...
  159.  */
  160. return false;
  161. }
  162. /* --------------------------------
  163.  * TransactionLogUpdate
  164.  * --------------------------------
  165.  */
  166. static void
  167. TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
  168.  XidStatus status) /* new trans status */
  169. {
  170. BlockNumber blockNumber;
  171. bool fail = false; /* success/failure */
  172. /* ----------------
  173.  * during initialization we don't record any updates.
  174.  * ----------------
  175.  */
  176. if (!RelationIsValid(LogRelation))
  177. return;
  178. /* ----------------
  179.  * update the log relation
  180.  * ----------------
  181.  */
  182. TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
  183. TransBlockNumberSetXidStatus(LogRelation,
  184.  blockNumber,
  185.  transactionId,
  186.  status,
  187.  &fail);
  188. /*
  189.  * update (invalidate) our single item TransactionLogTest cache.
  190.  *
  191.  * if (status != XID_COMMIT)
  192.  *
  193.  * What's the hell ?! Why != XID_COMMIT ?!
  194.  */
  195. TransactionIdStore(transactionId, &cachedTestXid);
  196. cachedTestXidStatus = status;
  197. }
  198. /* ----------------------------------------------------------------
  199.  *  transaction recovery code
  200.  * ----------------------------------------------------------------
  201.  */
  202. /* --------------------------------
  203.  * TransRecover
  204.  *
  205.  * preform transaction recovery checking.
  206.  *
  207.  * Note: this should only be preformed if no other backends
  208.  *   are running. This is known by the postmaster and
  209.  *   conveyed by the postmaster passing a "do recovery checking"
  210.  *   flag to the backend.
  211.  *
  212.  * here we get the last recorded transaction from the log,
  213.  * get the "last" and "next" transactions from the variable relation
  214.  * and then preform some integrity tests:
  215.  *
  216.  * 1) No transaction may exist higher then the "next" available
  217.  *    transaction recorded in the variable relation.  If this is the
  218.  *    case then it means either the log or the variable relation
  219.  *    has become corrupted.
  220.  *
  221.  * 2) The last committed transaction may not be higher then the
  222.  *    next available transaction for the same reason.
  223.  *
  224.  * 3) The last recorded transaction may not be lower then the
  225.  *    last committed transaction. (the reverse is ok - it means
  226.  *    that some transactions have aborted since the last commit)
  227.  *
  228.  * Here is what the proper situation looks like.  The line
  229.  * represents the data stored in the log. 'c' indicates the
  230.  * transaction was recorded as committed, 'a' indicates an
  231.  * abortted transaction and '.' represents information not
  232.  * recorded.  These may correspond to in progress transactions.
  233.  *
  234.  *  c c  a  c  . .  a  .  . .  .  .  . .  .  .  .
  235.  *   | |
  236.  *  last    next
  237.  *
  238.  * Since "next" is only incremented by GetNewTransactionId() which
  239.  * is called when transactions are started.  Hence if there
  240.  * are commits or aborts after "next", then it means we committed
  241.  * or aborted BEFORE we started the transaction.  This is the
  242.  * rational behind constraint (1).
  243.  *
  244.  * Likewise, "last" should never greater then "next" for essentially
  245.  * the same reason - it would imply we committed before we started.
  246.  * This is the reasoning for (2).
  247.  *
  248.  * (3) implies we may never have a situation such as:
  249.  *
  250.  *  c c  a  c  . .  a  c  . .  .  .  . .  .  .  .
  251.  *   | |
  252.  *  last    next
  253.  *
  254.  * where there is a 'c' greater then "last".
  255.  *
  256.  * Recovery checking is more difficult in the case where
  257.  * several backends are executing concurrently because the
  258.  * transactions may be executing in the other backends.
  259.  * So, we only do recovery stuff when the backend is explicitly
  260.  * passed a flag on the command line.
  261.  * --------------------------------
  262.  */
  263. static void
  264. TransRecover(Relation logRelation)
  265. {
  266. #ifdef NOT_USED
  267. /* ----------------
  268.  *   first get the last recorded transaction in the log.
  269.  * ----------------
  270.  */
  271. TransGetLastRecordedTransaction(logRelation, logLastXid, &fail);
  272. if (fail == true)
  273. elog(ERROR, "TransRecover: failed TransGetLastRecordedTransaction");
  274. /* ----------------
  275.  *   next get the "last" and "next" variables
  276.  * ----------------
  277.  */
  278. VariableRelationGetLastXid(&varLastXid);
  279. VariableRelationGetNextXid(&varNextXid);
  280. /* ----------------
  281.  *   intregity test (1)
  282.  * ----------------
  283.  */
  284. if (TransactionIdIsLessThan(varNextXid, logLastXid))
  285. elog(ERROR, "TransRecover: varNextXid < logLastXid");
  286. /* ----------------
  287.  *   intregity test (2)
  288.  * ----------------
  289.  */
  290. /* ----------------
  291.  *   intregity test (3)
  292.  * ----------------
  293.  */
  294. /* ----------------
  295.  * here we have a valid "
  296.  *
  297.  * **** RESUME HERE ****
  298.  * ----------------
  299.  */
  300. varNextXid = TransactionIdDup(varLastXid);
  301. TransactionIdIncrement(&varNextXid);
  302. VarPut(var, VAR_PUT_LASTXID, varLastXid);
  303. VarPut(var, VAR_PUT_NEXTXID, varNextXid);
  304. #endif
  305. }
  306. /* ----------------------------------------------------------------
  307.  * Interface functions
  308.  *
  309.  * InitializeTransactionLog
  310.  * ========
  311.  *    this function (called near cinit) initializes
  312.  *    the transaction log, time and variable relations.
  313.  *
  314.  * TransactionId DidCommit
  315.  * TransactionId DidAbort
  316.  * TransactionId IsInProgress
  317.  * ========
  318.  *    these functions test the transaction status of
  319.  *    a specified transaction id.
  320.  *
  321.  * TransactionId Commit
  322.  * TransactionId Abort
  323.  * TransactionId SetInProgress
  324.  * ========
  325.  *    these functions set the transaction status
  326.  *    of the specified xid. TransactionIdCommit() also
  327.  *    records the current time in the time relation
  328.  *    and updates the variable relation counter.
  329.  *
  330.  * ----------------------------------------------------------------
  331.  */
  332. /*
  333.  * InitializeTransactionLog
  334.  * Initializes transaction logging.
  335.  */
  336. void
  337. InitializeTransactionLog(void)
  338. {
  339. Relation logRelation;
  340. MemoryContext oldContext;
  341. /* ----------------
  342.  *   don't do anything during bootstrapping
  343.  * ----------------
  344.  */
  345. if (AMI_OVERRIDE)
  346. return;
  347. /* ----------------
  348.  *  disable the transaction system so the access methods
  349.  *  don't interfere during initialization.
  350.  * ----------------
  351.  */
  352. OverrideTransactionSystem(true);
  353. /* ----------------
  354.  * make sure allocations occur within the top memory context
  355.  * so that our log management structures are protected from
  356.  * garbage collection at the end of every transaction.
  357.  * ----------------
  358.  */
  359. oldContext = MemoryContextSwitchTo(TopMemoryContext);
  360. /* ----------------
  361.  *  first open the log and time relations
  362.  *  (these are created by amiint so they are guaranteed to exist)
  363.  * ----------------
  364.  */
  365. logRelation = heap_openr(LogRelationName);
  366. VariableRelation = heap_openr(VariableRelationName);
  367. /* ----------------
  368.  *  XXX TransactionLogUpdate requires that LogRelation
  369.  *  is valid so we temporarily set it so we can initialize
  370.  *  things properly. This could be done cleaner.
  371.  * ----------------
  372.  */
  373. LogRelation = logRelation;
  374. /* ----------------
  375.  *  if we have a virgin database, we initialize the log
  376.  *  relation by committing the AmiTransactionId (id 512) and we
  377.  *  initialize the variable relation by setting the next available
  378.  *  transaction id to FirstTransactionId (id 514).  OID initialization
  379.  *  happens as a side effect of bootstrapping in varsup.c.
  380.  * ----------------
  381.  */
  382. SpinAcquire(OidGenLockId);
  383. if (!TransactionIdDidCommit(AmiTransactionId))
  384. {
  385. /* ----------------
  386.  * SOMEDAY initialize the information stored in
  387.  * the headers of the log/variable relations.
  388.  * ----------------
  389.  */
  390. TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
  391. TransactionIdStore(AmiTransactionId, &cachedTestXid);
  392. cachedTestXidStatus = XID_COMMIT;
  393. VariableRelationPutNextXid(FirstTransactionId);
  394. }
  395. else if (RecoveryCheckingEnabled())
  396. {
  397. /* ----------------
  398.  * if we have a pre-initialized database and if the
  399.  * perform recovery checking flag was passed then we
  400.  * do our database integrity checking.
  401.  * ----------------
  402.  */
  403. TransRecover(logRelation);
  404. }
  405. LogRelation = (Relation) NULL;
  406. SpinRelease(OidGenLockId);
  407. /* ----------------
  408.  * now re-enable the transaction system
  409.  * ----------------
  410.  */
  411. OverrideTransactionSystem(false);
  412. /* ----------------
  413.  * instantiate the global variables
  414.  * ----------------
  415.  */
  416. LogRelation = logRelation;
  417. /* ----------------
  418.  * restore the memory context to the previous context
  419.  * before we return from initialization.
  420.  * ----------------
  421.  */
  422. MemoryContextSwitchTo(oldContext);
  423. }
  424. /* --------------------------------
  425.  * TransactionId DidCommit
  426.  * TransactionId DidAbort
  427.  * TransactionId IsInProgress
  428.  * --------------------------------
  429.  */
  430. /*
  431.  * TransactionIdDidCommit
  432.  * True iff transaction associated with the identifier did commit.
  433.  *
  434.  * Note:
  435.  * Assumes transaction identifier is valid.
  436.  */
  437. bool /* true if given transaction committed */
  438. TransactionIdDidCommit(TransactionId transactionId)
  439. {
  440. if (AMI_OVERRIDE)
  441. return true;
  442. return TransactionLogTest(transactionId, XID_COMMIT);
  443. }
  444. /*
  445.  * TransactionIdDidAborted
  446.  * True iff transaction associated with the identifier did abort.
  447.  *
  448.  * Note:
  449.  * Assumes transaction identifier is valid.
  450.  * XXX Is this unneeded?
  451.  */
  452. bool /* true if given transaction aborted */
  453. TransactionIdDidAbort(TransactionId transactionId)
  454. {
  455. if (AMI_OVERRIDE)
  456. return false;
  457. return TransactionLogTest(transactionId, XID_ABORT);
  458. }
  459. /*
  460.  * Now this func in shmem.c and gives quality answer by scanning
  461.  * PROC structures of all running backend. - vadim 11/26/96
  462.  *
  463.  * Old comments:
  464.  * true if given transaction neither committed nor aborted
  465. bool
  466. TransactionIdIsInProgress(TransactionId transactionId)
  467. {
  468. if (AMI_OVERRIDE)
  469. return false;
  470. return TransactionLogTest(transactionId, XID_INPROGRESS);
  471. }
  472.  */
  473. /* --------------------------------
  474.  * TransactionId Commit
  475.  * TransactionId Abort
  476.  * TransactionId SetInProgress
  477.  * --------------------------------
  478.  */
  479. /*
  480.  * TransactionIdCommit
  481.  * Commits the transaction associated with the identifier.
  482.  *
  483.  * Note:
  484.  * Assumes transaction identifier is valid.
  485.  */
  486. void
  487. TransactionIdCommit(TransactionId transactionId)
  488. {
  489. if (AMI_OVERRIDE)
  490. return;
  491. TransactionLogUpdate(transactionId, XID_COMMIT);
  492. }
  493. /*
  494.  * TransactionIdAbort
  495.  * Aborts the transaction associated with the identifier.
  496.  *
  497.  * Note:
  498.  * Assumes transaction identifier is valid.
  499.  */
  500. void
  501. TransactionIdAbort(TransactionId transactionId)
  502. {
  503. if (AMI_OVERRIDE)
  504. return;
  505. TransactionLogUpdate(transactionId, XID_ABORT);
  506. }