home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / access / transam / transam.c next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  18.6 KB  |  670 lines

  1. /* ----------------------------------------------------------------
  2.  *   FILE
  3.  *    transam.c
  4.  *    
  5.  *   DESCRIPTION
  6.  *    postgres transaction log/time interface routines
  7.  *
  8.  *   INTERFACE ROUTINES
  9.  *    RecoveryCheckingEnabled
  10.  *    SetRecoveryCheckingEnabled
  11.  *    InitializeTransactionLog
  12.  *    TransactionIdDidCommit
  13.  *    TransactionIdDidAbort
  14.  *    TransactionIdIsInProgress
  15.  *    TransactionIdCommit
  16.  *    TransactionIdAbort
  17.  *    TransactionIdSetInProgress
  18.  *       
  19.  *   NOTES
  20.  *    This file contains the high level access-method
  21.  *    interface to the transaction system.
  22.  *
  23.  *   IDENTIFICATION
  24.  *    $Header: /private/postgres/src/access/transam/RCS/transam.c,v 1.16 1992/08/13 23:26:25 mer Exp $
  25.  * ----------------------------------------------------------------
  26.  */
  27.  
  28. #include "tmp/postgres.h"
  29.  
  30.  RcsId("$Header: /private/postgres/src/access/transam/RCS/transam.c,v 1.16 1992/08/13 23:26:25 mer Exp $");
  31.  
  32. #include "machine.h"        /* in port/ directory (needed for BLCKSZ) */
  33.  
  34. #include "access/heapam.h"
  35. #include "storage/buf.h"
  36. #include "storage/bufmgr.h"
  37.  
  38. #include "utils/memutils.h"
  39. #include "utils/mcxt.h"
  40. #include "utils/rel.h"
  41. #include "utils/log.h"
  42.  
  43. #include "utils/nabstime.h"
  44. #include "catalog/catname.h"
  45.  
  46. #include "access/transam.h"
  47. #include "access/xact.h"
  48.  
  49. /* ----------------
  50.  *    global variables holding pointers to relations used
  51.  *    by the transaction system.  These are initialized by
  52.  *    InitializeTransactionLog().
  53.  * ----------------
  54.  */
  55.  
  56. Relation LogRelation      = (Relation) NULL;
  57. Relation TimeRelation      = (Relation) NULL;
  58. Relation VariableRelation = (Relation) NULL;
  59.  
  60. /* ----------------
  61.  *        global variables holding cached transaction id's and statuses.
  62.  * ----------------
  63.  */
  64. TransactionId    cachedGetCommitTimeXid;
  65. Time         cachedGetCommitTime;
  66. TransactionId    cachedTestXid;
  67. XidStatus    cachedTestXidStatus;
  68.  
  69. /* ----------------
  70.  *    transaction system constants
  71.  * ----------------
  72.  */
  73. TransactionId NullTransactionId = (TransactionId) 0;
  74.  
  75. TransactionId AmiTransactionId = (TransactionId) 512;
  76.  
  77. TransactionId FirstTransactionId = (TransactionId) 514;
  78.  
  79. /* ----------------
  80.  *    transaction recovery state variables
  81.  *
  82.  *    When the transaction system is initialized, we may
  83.  *    need to do recovery checking.  This decision is decided
  84.  *    by the postmaster or the user by supplying the backend
  85.  *    with a special flag.  In general, we want to do recovery
  86.  *    checking whenever we are running without a postmaster
  87.  *    or when the number of backends running under the postmaster
  88.  *    goes from zero to one. -cim 3/21/90
  89.  * ----------------
  90.  */
  91. int RecoveryCheckingEnableState = 0;
  92.  
  93. /* ------------------
  94.  *    spinlock for oid generation
  95.  * -----------------
  96.  */
  97. extern int OidGenLockId;
  98.  
  99. /* ----------------
  100.  *    globals that must be reset at abort
  101.  * ----------------
  102.  */
  103. extern bool    BuildingBtree;
  104. extern bool    VacuumRunning;
  105.  
  106. /* ----------------
  107.  *    recovery checking accessors
  108.  * ----------------
  109.  */
  110. int
  111. RecoveryCheckingEnabled()
  112. {    
  113.     return RecoveryCheckingEnableState;
  114. }
  115.  
  116. void
  117. SetRecoveryCheckingEnabled(state)
  118.     bool state;
  119. {    
  120.     RecoveryCheckingEnableState = (state == true);
  121. }
  122.  
  123. /* ----------------------------------------------------------------
  124.  *    postgres log/time access method interface
  125.  *
  126.  *    TransactionLogTest
  127.  *    TransactionLogUpdate
  128.  *    ========
  129.  *       these functions do work for the interface
  130.  *       functions - they search/retrieve and append/update
  131.  *       information in the log and time relations.
  132.  * ----------------------------------------------------------------
  133.  */
  134.  
  135. /* --------------------------------
  136.  *    TransactionLogTest
  137.  * --------------------------------
  138.  */
  139.  
  140. bool    /* true/false: does transaction id have specified status? */
  141. TransactionLogTest(transactionId, status)
  142.     TransactionId     transactionId;     /* transaction id to test */
  143.     XidStatus         status;        /* transaction status */
  144. {
  145.     BlockNumber        blockNumber;
  146.     XidStatus        xidstatus;    /* recorded status of xid */
  147.     bool        fail = false;          /* success/failure */
  148.     
  149.     /* ----------------
  150.      *     during initialization consider all transactions
  151.      *  as having been committed
  152.      * ----------------
  153.      */
  154.     if (! RelationIsValid(LogRelation))
  155.     return (bool) (status == XID_COMMIT);
  156.  
  157.     /* ----------------
  158.      *     before going to the buffer manager, check our single
  159.      *   item cache to see if we didn't just check the transaction
  160.      *   status a moment ago.
  161.      * ----------------
  162.      */
  163.     if (TransactionIdEquals(transactionId, cachedTestXid))
  164.     return (bool)
  165.         (status == cachedTestXidStatus);
  166.         
  167.     /* ----------------
  168.      *    compute the item pointer corresponding to the
  169.      *  page containing our transaction id.  We save the item in
  170.      *  our cache to speed up things if we happen to ask for the
  171.      *  same xid's status more than once.
  172.      * ----------------
  173.      */
  174.     TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
  175.     xidstatus = TransBlockNumberGetXidStatus(LogRelation,
  176.                          blockNumber,
  177.                          transactionId,
  178.                          &fail);
  179.  
  180.     if (! fail) {
  181.     TransactionIdStore(transactionId, &cachedTestXid);
  182.     cachedTestXidStatus = xidstatus;
  183.     return (bool)
  184.         (status == xidstatus);
  185.     }
  186.     
  187.     /* ----------------
  188.      *      here the block didn't contain the information we wanted
  189.      * ----------------
  190.      */
  191.     elog(WARN, "TransactionLogTest: failed to get xidstatus");
  192.  
  193.     /*
  194.      * so lint is happy...
  195.      */
  196.     return(false);
  197. }
  198.  
  199. /* --------------------------------
  200.  *    TransactionLogUpdate
  201.  * --------------------------------
  202.  */
  203. void
  204. TransactionLogUpdate(transactionId, status)
  205.     TransactionId     transactionId;    /* trans id to update */
  206.     XidStatus         status;        /* new trans status */
  207. {
  208.     BlockNumber        blockNumber;
  209.     bool        fail = false;          /* success/failure */
  210.     Time         currentTime;    /* time of this transaction */
  211.  
  212.     /* ----------------
  213.      *     during initialization we don't record any updates.
  214.      * ----------------
  215.      */
  216.     if (! RelationIsValid(LogRelation))
  217.     return;
  218.     
  219.     /* ----------------
  220.      *  get the transaction commit time
  221.      * ----------------
  222.      */
  223.     currentTime = GetSystemTime();
  224.  
  225.     /* ----------------
  226.      *  update the log relation
  227.      * ----------------
  228.      */
  229.     TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
  230.     TransBlockNumberSetXidStatus(LogRelation,
  231.                  blockNumber,
  232.                  transactionId,
  233.                  status,
  234.                  &fail);
  235.  
  236.     /* ----------------
  237.      *     update (invalidate) our single item TransactionLogTest cache.
  238.      * ----------------
  239.      */
  240.     TransactionIdStore(transactionId, &cachedTestXid);
  241.     cachedTestXidStatus = status;
  242.     
  243.     /* ----------------
  244.      *    now we update the time relation, if necessary
  245.      *  (we only record commit times)
  246.      * ----------------
  247.      */
  248.     if (RelationIsValid(TimeRelation) && status == XID_COMMIT) {
  249.     TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber);
  250.     TransBlockNumberSetCommitTime(TimeRelation,
  251.                       blockNumber,
  252.                       transactionId,
  253.                       currentTime,
  254.                       &fail);
  255.     /* ----------------
  256.      *   update (invalidate) our single item GetCommitTime cache.
  257.      * ----------------
  258.      */
  259.     TransactionIdStore(transactionId, &cachedGetCommitTimeXid);
  260.     cachedGetCommitTime = currentTime;
  261.     }
  262.  
  263.     /* ----------------
  264.      *    now we update the "last committed transaction" field
  265.      *  in the variable relation if we are recording a commit.
  266.      * ----------------
  267.      */
  268.     if (RelationIsValid(VariableRelation) && status == XID_COMMIT)
  269.     UpdateLastCommittedXid(transactionId);
  270. }
  271.  
  272. /* --------------------------------
  273.  *    TransactionIdGetCommitTime
  274.  * --------------------------------
  275.  */
  276.  
  277. Time  /* commit time of transaction id */
  278. TransactionIdGetCommitTime(transactionId)
  279.     TransactionId     transactionId;     /* transaction id to test */
  280. {
  281.     BlockNumber        blockNumber;
  282.     Time        commitTime;     /* commit time */
  283.     bool        fail = false;          /* success/failure */
  284.     
  285.     /* ----------------
  286.      *   return invalid if we aren't running yet...
  287.      * ----------------
  288.      */
  289.     if (! RelationIsValid(TimeRelation))
  290.     return InvalidTime;
  291.  
  292.     /* ----------------
  293.      *     before going to the buffer manager, check our single
  294.      *   item cache to see if we didn't just get the commit time
  295.      *   a moment ago.
  296.      * ----------------
  297.      */
  298.     if (TransactionIdEquals(transactionId, cachedGetCommitTimeXid))
  299.     return cachedGetCommitTime;
  300.     
  301.     /* ----------------
  302.      *    compute the item pointer corresponding to the
  303.      *  page containing our transaction commit time
  304.      * ----------------
  305.      */
  306.     TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber);
  307.     commitTime = TransBlockNumberGetCommitTime(TimeRelation,
  308.                            blockNumber,
  309.                            transactionId,
  310.                            &fail);
  311.  
  312.     /* ----------------
  313.      *    update our cache and return the transaction commit time
  314.      * ----------------
  315.      */
  316.     if (! fail) {
  317.     TransactionIdStore(transactionId, &cachedGetCommitTimeXid);
  318.     cachedGetCommitTime = commitTime;
  319.     return commitTime;
  320.     } else
  321.     return InvalidTime;
  322. }
  323.  
  324. /* ----------------------------------------------------------------
  325.  *             transaction recovery code
  326.  * ----------------------------------------------------------------
  327.  */
  328.  
  329. /* --------------------------------
  330.  *    TransRecover
  331.  *
  332.  *        preform transaction recovery checking.
  333.  *
  334.  *    Note: this should only be preformed if no other backends
  335.  *          are running.  This is known by the postmaster and
  336.  *          conveyed by the postmaster passing a "do recovery checking"
  337.  *          flag to the backend.
  338.  *
  339.  *    here we get the last recorded transaction from the log,
  340.  *    get the "last" and "next" transactions from the variable relation
  341.  *    and then preform some integrity tests:
  342.  *
  343.  *        1) No transaction may exist higher then the "next" available
  344.  *         transaction recorded in the variable relation.  If this is the
  345.  *         case then it means either the log or the variable relation
  346.  *         has become corrupted.
  347.  *
  348.  *      2) The last committed transaction may not be higher then the
  349.  *         next available transaction for the same reason.
  350.  *
  351.  *      3) The last recorded transaction may not be lower then the
  352.  *         last committed transaction.  (the reverse is ok - it means
  353.  *         that some transactions have aborted since the last commit)
  354.  *
  355.  *    Here is what the proper situation looks like.  The line
  356.  *    represents the data stored in the log.  'c' indicates the
  357.  *      transaction was recorded as committed, 'a' indicates an
  358.  *      abortted transaction and '.' represents information not
  359.  *      recorded.  These may correspond to in progress transactions.
  360.  *
  361.  *         c  c  a  c  .  .  a  .  .  .  .  .  .  .  .  .  .
  362.  *              |                 |
  363.  *             last           next
  364.  *
  365.  *    Since "next" is only incremented by GetNewTransactionId() which
  366.  *      is called when transactions are started.  Hence if there
  367.  *      are commits or aborts after "next", then it means we committed
  368.  *      or aborted BEFORE we started the transaction.  This is the
  369.  *    rational behind constraint (1).
  370.  *
  371.  *      Likewise, "last" should never greater then "next" for essentially
  372.  *      the same reason - it would imply we committed before we started.
  373.  *      This is the reasoning for (2).
  374.  *
  375.  *    (3) implies we may never have a situation such as:
  376.  *
  377.  *         c  c  a  c  .  .  a  c  .  .  .  .  .  .  .  .  .
  378.  *              |                 |
  379.  *             last           next
  380.  *
  381.  *      where there is a 'c' greater then "last".
  382.  *
  383.  *      Recovery checking is more difficult in the case where
  384.  *      several backends are executing concurrently because the
  385.  *    transactions may be executing in the other backends.
  386.  *      So, we only do recovery stuff when the backend is explicitly
  387.  *      passed a flag on the command line.
  388.  * --------------------------------
  389.  */
  390.  
  391. void
  392. TransRecover(logRelation)
  393.     Relation logRelation;
  394. {
  395.     TransactionId logLastXid;
  396.     TransactionId varLastXid;
  397.     TransactionId varNextXid;
  398.  
  399. #if 0    
  400.     /* ----------------
  401.      *    first get the last recorded transaction in the log.
  402.      * ----------------
  403.      */
  404.     TransGetLastRecordedTransaction(logRelation, logLastXid, &fail);
  405.     if (fail == true)
  406.     elog(WARN, "TransRecover: failed TransGetLastRecordedTransaction");
  407.     
  408.     /* ----------------
  409.      *    next get the "last" and "next" variables
  410.      * ----------------
  411.      */
  412.     VariableRelationGetLastXid(&varLastXid);
  413.     VariableRelationGetNextXid(&varNextXid);
  414.  
  415.     /* ----------------
  416.      *    intregity test (1)
  417.      * ----------------
  418.      */
  419.     if (TransactionIdIsLessThan(varNextXid, logLastXid))
  420.     elog(WARN, "TransRecover: varNextXid < logLastXid");
  421.         
  422.     /* ----------------
  423.      *    intregity test (2)
  424.      * ----------------
  425.      */
  426.             
  427.     /* ----------------
  428.      *    intregity test (3)
  429.      * ----------------
  430.      */
  431.  
  432.     /* ----------------
  433.      *  here we have a valid "
  434.      *
  435.      *        **** RESUME HERE ****
  436.      * ----------------
  437.      */
  438.     varNextXid = TransactionIdDup(varLastXid);
  439.     TransactionIdIncrement(&varNextXid);
  440.  
  441.     VarPut(var, VAR_PUT_LASTXID, varLastXid);
  442.     VarPut(var, VAR_PUT_NEXTXID, varNextXid);
  443. #endif
  444. }
  445.     
  446. /* ----------------------------------------------------------------
  447.  *            Interface functions
  448.  *
  449.  *    InitializeTransactionLog
  450.  *    ========
  451.  *       this function (called near cinit) initializes
  452.  *       the transaction log, time and variable relations.
  453.  *
  454.  *    TransactionId DidCommit
  455.  *    TransactionId DidAbort
  456.  *    TransactionId IsInProgress
  457.  *    ========
  458.  *       these functions test the transaction status of
  459.  *       a specified transaction id.
  460.  *
  461.  *    TransactionId Commit
  462.  *    TransactionId Abort
  463.  *    TransactionId SetInProgress
  464.  *    ========
  465.  *       these functions set the transaction status
  466.  *       of the specified xid. TransactionIdCommit() also
  467.  *       records the current time in the time relation
  468.  *       and updates the variable relation counter.
  469.  *
  470.  * ----------------------------------------------------------------
  471.  */
  472.  
  473. /* --------------------------------
  474.  *    InitializeTransactionLog
  475.  * --------------------------------
  476.  */
  477.  
  478. void
  479. InitializeTransactionLog()
  480. {
  481.     Relation      logRelation;
  482.     Relation      timeRelation;
  483.     Relation      varRelation;
  484.     MemoryContext oldContext;
  485.     BlockNumber      n;
  486.     bool      fail = false;
  487.     
  488.     /* ----------------
  489.      *    don't do anything during bootstrapping
  490.      * ----------------
  491.      */
  492.     if (AMI_OVERRIDE)
  493.     return;
  494.     
  495.     /* ----------------
  496.      *     disable the transaction system so the access methods
  497.      *   don't interfere during initialization.
  498.      * ----------------
  499.      */
  500.     OverrideTransactionSystem(true);
  501.     
  502.     /* ----------------
  503.      *    make sure allocations occur within the top memory context
  504.      *  so that our log management structures are protected from
  505.      *  garbage collection at the end of every transaction.
  506.      * ----------------
  507.      */
  508.     oldContext = MemoryContextSwitchTo(TopMemoryContext); 
  509.     
  510.     /* ----------------
  511.      *   first open the log and time relations
  512.      *   (these are created by amiint so they are guarantted to exist)
  513.      * ----------------
  514.      */
  515.     logRelation =     heap_openr(LogRelationName);
  516.     timeRelation =     heap_openr(TimeRelationName);
  517.     VariableRelation =     heap_openr(VariableRelationName);
  518.    /* ----------------
  519.     *   XXX TransactionLogUpdate requires that LogRelation
  520.     *     and TimeRelation are valid so we temporarily set
  521.     *     them so we can initialize things properly.
  522.     *     This could be done cleaner.
  523.     * ----------------
  524.     */
  525.     LogRelation =  logRelation;
  526.     TimeRelation = timeRelation;
  527.  
  528.     /* ----------------
  529.      *   if we have a virgin database, we initialize the log and time
  530.      *     relation by committing the AmiTransactionId (id 512) and we
  531.      *   initialize the variable relation by setting the next available
  532.      *   transaction id to FirstTransactionId (id 514).  OID initialization
  533.      *   happens as a side effect of bootstrapping in varsup.c.
  534.      * ----------------
  535.      */
  536.     SpinAcquire(OidGenLockId);
  537.     if (!TransactionIdDidCommit(AmiTransactionId)) {
  538.  
  539.     /* ----------------
  540.      *  SOMEDAY initialize the information stored in
  541.      *          the headers of the log/time/variable relations.
  542.      * ----------------
  543.      */
  544.     TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
  545.     VariableRelationPutNextXid(FirstTransactionId);
  546.     
  547.     } else if (RecoveryCheckingEnabled()) {
  548.     /* ----------------
  549.      *    if we have a pre-initialized database and if the
  550.      *    preform recovery checking flag was passed then we
  551.      *    do our database integrity checking.
  552.      * ----------------
  553.      */
  554.     TransRecover();
  555.     }
  556.     LogRelation =  (Relation) NULL;
  557.     TimeRelation = (Relation) NULL;
  558.     SpinRelease(OidGenLockId);
  559.     
  560.     /* ----------------
  561.      *    now re-enable the transaction system
  562.      * ----------------
  563.      */
  564.     OverrideTransactionSystem(false);
  565.     
  566.     /* ----------------
  567.      *    instantiate the global variables
  568.      * ----------------
  569.      */
  570.     LogRelation =     logRelation;
  571.     TimeRelation =     timeRelation;
  572.  
  573.     /* ----------------
  574.      *    restore the memory context to the previous context
  575.      *  before we return from initialization.
  576.      * ----------------
  577.      */
  578.     MemoryContextSwitchTo(oldContext);
  579. }
  580.  
  581. /* --------------------------------
  582.  *    TransactionId DidCommit
  583.  *    TransactionId DidAbort
  584.  *    TransactionId IsInProgress
  585.  * --------------------------------
  586.  */
  587.  
  588. bool    /* true if given transaction committed */
  589. TransactionIdDidCommit(transactionId)
  590.     TransactionId transactionId;
  591. {
  592.     if (AMI_OVERRIDE)
  593.     return true;
  594.     
  595.     return
  596.     TransactionLogTest(transactionId, XID_COMMIT);
  597. }
  598.  
  599. bool    /* true if given transaction aborted */
  600. TransactionIdDidAbort(transactionId)
  601.     TransactionId transactionId;
  602. {
  603.     if (AMI_OVERRIDE)
  604.     return false;
  605.     
  606.     return
  607.     TransactionLogTest(transactionId, XID_ABORT);
  608. }
  609.  
  610. bool    /* true if given transaction neither committed nor aborted */
  611. TransactionIdIsInProgress(transactionId)
  612.     TransactionId transactionId;
  613. {
  614.     if (AMI_OVERRIDE)
  615.     return false;
  616.     
  617.     return
  618.     TransactionLogTest(transactionId, XID_INPROGRESS);
  619. }
  620.  
  621. /* --------------------------------
  622.  *    TransactionId Commit
  623.  *    TransactionId Abort
  624.  *    TransactionId SetInProgress
  625.  * --------------------------------
  626.  */
  627.  
  628. void
  629. TransactionIdCommit(transactionId)
  630.     TransactionId transactionId;
  631. {
  632.     if (AMI_OVERRIDE)
  633.     return;
  634.     
  635.     /*
  636.      * Within TransactionLogUpdate we call UpdateLastCommited()
  637.      * which assumes we have exclusive access to pg_variable.
  638.      * Therefore we need to get exclusive access before calling
  639.      * TransactionLogUpdate. -mer 18 Aug 1992
  640.      */
  641.     SpinAcquire(OidGenLockId);
  642.     TransactionLogUpdate(transactionId, XID_COMMIT);
  643.     SpinRelease(OidGenLockId);
  644. }
  645.  
  646. void
  647. TransactionIdAbort(transactionId)
  648.     TransactionId transactionId;
  649. {
  650.     BuildingBtree = false;
  651.  
  652.     if (VacuumRunning)
  653.     vc_abort();
  654.  
  655.     if (AMI_OVERRIDE)
  656.     return;
  657.     
  658.     TransactionLogUpdate(transactionId, XID_ABORT);
  659. }
  660.  
  661. void
  662. TransactionIdSetInProgress(transactionId)
  663.     TransactionId transactionId;
  664. {
  665.     if (AMI_OVERRIDE)
  666.     return;
  667.     
  668.     TransactionLogUpdate(transactionId, XID_INPROGRESS);
  669. }
  670.