/*!**************************************************************************

  module      : Log_UndoFile.cpp
  special area: Logging
  responsible : UweH
  last changed: 2000-09-25  12:00
  copyright:    (c) 2000-2004 SAP AG
  description : Implementation for class Log_UndoFile.



    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*****************************************************************************/


/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include "KernelCommon/Kernel_TraceTopics.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"
#include "DataAccess/Data_Types.hpp"
#include "Logging/Log_Exceptions.hpp"
#include "Logging/Log_UndoFile.hpp"

/* --------------------------------------------------------------------------- */
Log_UndoFile::Log_UndoFile (tgg00_TransContext &Trans,
                            const Data_PageNo   Root,
                            const Data_PageNo   Last)
: Data_BaseFile ( Data_UndoFile,
                  Data_PageId(Root, Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable)),
                  Data_PageAccessManager(Trans, Data_UndoFile, 
                  Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Root) ),
  m_Container (m_PageAccessManager, m_RootId, Last),
  m_Iterator  (m_PageAccessManager,*(REINTERPRET_CAST (SAPDBMem_IRawAllocator*, Trans.trAllocator_gg00))),
  m_TransNo   (Trans.trWriteTransId_gg00),
  m_PageCount (0) // PTS 1124083 UH 2003-09-10 added
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::Log_UndoFile", LogTrans_Trace, 5);
}

/* --------------------------------------------------------------------------- */
Log_UndoFile::Log_UndoFile (Data_PageAccessManager &Pam,
                            SAPDBMem_IRawAllocator &Allocator,
                            const tgg91_TransNo    &TransNo,
                            const Data_PageNo       Root,
                            const Data_PageNo       Last)
: Data_BaseFile ( Data_UndoFile,
                  Data_PageId(Root, Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable)),
                  Data_PageAccessManager(Pam, Data_UndoFile, 
                  Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Root) ),
  m_Container (m_PageAccessManager, m_RootId, Last),
  m_Iterator  (m_PageAccessManager, Allocator),
  m_TransNo   (TransNo),
  m_PageCount (0) // PTS 1124083 UH 2003-09-10 added
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::Log_UndoFile(pam)", LogTrans_Trace, 5);
}

/* --------------------------------------------------------------------------- */
Log_UndoFile::~Log_UndoFile ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::~Log_UndoFile", LogTrans_Trace, 5);
    if (IsCreated())
        Drop();
}


/* --------------------------------------------------------------------------- */
bool Log_UndoFile::Create ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::Create", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( ! IsCreated() );

    if ( m_TransNo.gg90IsNil() )
    {
        // PTS 1132283 UH 2004-11-22 changed crash to return false
        return false;
    }

    m_PageAccessManager.Invalidate();
    
    m_RootId.Invalidate();
    
    PageIterator RootPageIter (m_PageAccessManager);

    if ( ! m_Container.Create(RootPageIter) ) // PTS 1121659 UH 2003-04-30
    {
        m_Container.Drop();
        return false;
    }

    if ( ! m_Iterator.Initialize() )
    {
        m_Container.Drop();
        return false;
    }

    m_PageCount = 1;
    
    (*RootPageIter).SetPageCount (m_PageCount);
    (*RootPageIter).SetTransNo   (m_TransNo);
    (*RootPageIter).SaveLastGarbageCollectorPosition (Data_PageNo(), 0); // PTS ? UH 2002-03-15

    return true;
}

/* --------------------------------------------------------------------------- */
void Log_UndoFile::Drop ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::Drop", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    m_Iterator.Invalidate();
    m_Iterator.Delete();
    m_Container.Drop();
    m_RootId.Invalidate();
    m_PageAccessManager.Invalidate();
}

/*! ---------------------------------------------------------------------------
function:    Verify
returns:     true, if the file is consistent
*/
bool Log_UndoFile::Verify(bool isCold)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::Verify", LogTrans_Trace, 5);
    
    if ( m_Container.Verify (isCold) )
    {
        if ( LogTrans_Trace.TracesLevel(6) )
            RTE_Message( Log_Exception(__CONTEXT__, LOG_INFO_VERIFY,
                         "UndoFile", SAPDB_ToString(GetRootId().PageNo()) ) );
    }
    else
    {
        RTE_Message( Log_Exception(__CONTEXT__, LOG_ERROR_VERIFY,
                     "UndoFile",
                     SAPDB_ToString(GetRootId().PageNo()),
                     SAPDB_ToString(m_PageAccessManager.GetLastError()) ) );
        WriteToTrace();
        m_PageAccessManager.ResetLastError();
        return false;
    }
    return true;
}

/*! ---------------------------------------------------------------------------
function:    WriteToTrace
description: This traces some basic information about the file to the trace.
*/
void Log_UndoFile::WriteToTrace (const char * title) const
{
    Data_BaseFile::WriteToTrace(title);
        
    if ( ! IsCreated() )
    {
        Kernel_VTrace() << "LogUndoFile: is no created.";
        return;
    }
    
    Kernel_VTrace() << "TransNo: " << m_TransNo.gg90GetInt4();
    m_Container.WriteToTrace();
}

/* --------------------------------------------------------------------------- */
Log_UndoFile::WriteBeforeImageResult 
    Log_UndoFile::WriteUndoEntry (Log_BeforeImage   &BeforeImage,
                                  const Log_IAction &Action,
                                  Data_PageNo       &pageno,
                                  Data_PageOffset   &offset)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::WriteUndoEntry", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    const Data_SplitRecordLength LengthToReserve = 
                                    BeforeImage.GetPersistentLength() 
                                    +
                                    Action.GetPersistentLength();
    const Data_RecordLength      MinLengthToReserve = 
                                    BeforeImage.GetPersistentLength() 
                                    +
                                    Action.GetMinimalPersistentLength();
    
    SAPDBERR_ASSERT_ARGUMENT ( LengthToReserve >= MinLengthToReserve );
    
    pageno.Invalidate();
    offset = 0;

    SAPDB_UInt addedPages;

    // PTS 1121659 UH 2003-04-30
    if ( ! m_Container.ReserveSpace (LengthToReserve, MinLengthToReserve, m_Iterator, addedPages) )
    {
        m_Iterator.Invalidate (false);
        return writeBeforeImageNoSpaceAvailable;
    }
    m_PageCount += addedPages;
    
    SAPDBERR_ASSERT_STATE ( LengthToReserve == (*m_Iterator).Length() );

    if ( ! m_Iterator.IsValid() )
        return writeBeforeImageError;

    EntrySpace            &space = *m_Iterator;
    Data_SplitSpaceWriter  Writer (space);

    bool isOK = true;
    
    BeforeImage.WritePersistentFormat (Writer, isOK);

    if ( ! isOK )
    {
        m_Container.UndoReserveSpace(space);
        m_Iterator.Invalidate (false);
        return writeBeforeImageError;
    }
    
    Action.WritePersistentFormat (Writer, isOK);

    if ( ! isOK )
    {
        // PTS 1117126 UH 2002-08-07
        m_Container.UndoReserveSpace(space);
        m_Iterator.Invalidate (false);
        return writeBeforeImageError;
    }

    m_Iterator.GetPosition (pageno, offset);
    
    if ( LogHistory_Check.ChecksLevel(5) // PTS 1117693 UH 2002-09-09 added check 
         &&
         offset != Data_PageSplitSpace::AlignRecordSize(offset) )
    {
        Kernel_VTrace() << "pageref not aligned: " << pageno << "." << offset << NewLine;
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"pageref not aligned") );
    }
            
    // PTS 1114461 UH 2002-02-26 begin
    if ( pageno == Data_PageNo(BeforeImage.GetPreviousImage().gg91RefPno())
         &&
         offset == Data_PageOffset(BeforeImage.GetPreviousImage().gg91RefPos()) )
        BeforeImage.SetPreviousImage (Data_PageNo(), 0); // prev entry is set to nil
    // PTS 1114461 UH 2002-02-26 end
    for ( int partno = (*m_Iterator).CurrentPart(); partno >= 0; --partno )
    {
        (*m_Iterator).GetPage(partno).SetLastEntrySequence  (BeforeImage.GetSequence());
    }
    
    m_Iterator.Invalidate (true);
    
    return writeBeforeImageOk;
}

/* --------------------------------------------------------------------------- */
void Log_UndoFile::GetLastUndoEntry (Data_AccessMode  accessmode,
                                     Iterator        &iter)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::GetLastUndoEntry", LogTrans_Trace, 5);
    SAPDBERR_ASSERT_STATE( IsCreated() );
    m_Container.End(iter, accessmode);
}

/* --------------------------------------------------------------------------- */
void Log_UndoFile::GetUndoEntry (Data_PageNo             pageno,
                                 Data_PageOffset         offset,
                                 Data_AccessMode         accessmode,
                                 Data_SplitRecordLength  maxsize,
                                 Iterator               &iter)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Log_UndoFile::GetUndoEntry", LogTrans_Trace, 5);

    if ( ! iter.SetPosition (pageno, offset, accessmode) )
    {
        if ( LogTrans_Trace.TracesLevel(6) )
        {
            Kernel_VTrace() << "position: " << pageno << "." << offset << " with accessmode: " << accessmode;
            WriteToTrace();
        }
        iter.Invalidate();
        return;
    }
    
    if ( ! iter.IsValid() ) // entry begin not found
    {
        iter.Invalidate();
        return;
    }
    
    if ( maxsize > 0 && iter.GetSize() > maxsize )
    {
        if ( LogTrans_Trace.TracesLevel(6) )
        {
            Kernel_VTrace trace;
            trace << "position: " << pageno << "." << offset
                  << " with accessmode: " << accessmode << FlushLine;
            trace << "size: " << iter.GetSize() << " maxsize: " << maxsize << FlushLine;
            WriteToTrace();
        }
        iter.Invalidate();
        return;
    }

    *iter; // assign the record space
}

/* --------------------------------------------------------------------------- */
void Log_UndoFile::SavePageCount ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::SavePageCount", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    Log_UndoFile::PageIterator pageiter (m_Container.GetPageAccessManager());
    m_Container.Begin (pageiter, Data_ForUpdate);  // PTS ? UH 2002-03-15
    if ( ! pageiter.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                                      "root not accessable") );

    (*pageiter).SetPageCount(m_PageCount);
}
    
/* --------------------------------------------------------------------------- */
SAPDB_UInt Log_UndoFile::GetPageCount ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::GetPageCount", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    if ( m_PageCount > 0 )
        return m_PageCount;

    Log_UndoFile::PageIterator pageiter (m_Container.GetPageAccessManager());
    m_Container.Begin (pageiter, Data_ForRead);
    if ( ! pageiter.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                                      "root not accessable") );

    m_PageCount = (*pageiter).GetPageCount();
    return m_PageCount;
}
    
/* --------------------------------------------------------------------------- */
void Log_UndoFile::SaveLastGarbageCollectorPosition (Data_PageNo     pageno,
                                                     Data_PageOffset offset)
{
    // PTS ? UH 2002-03-15 new
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::SaveLastGarbageCollectorPosition", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    Log_UndoFile::PageIterator pageiter (m_Container.GetPageAccessManager());
    m_Container.Begin (pageiter, Data_ForUpdate);
    if ( ! pageiter.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                                      "root not accessable") );

    (*pageiter).SaveLastGarbageCollectorPosition(pageno, offset);
}

/* --------------------------------------------------------------------------- */
void Log_UndoFile::GetLastGarbageCollectorPosition (Data_PageNo     &pageno,
                                                    Data_PageOffset &offset)
{
    // PTS ? UH 2002-03-15 new
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::GetLastGarbageCollectorPosition", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    Log_UndoFile::PageIterator pageiter (m_Container.GetPageAccessManager());
    m_Container.Begin (pageiter, Data_ForRead);
    if ( ! pageiter.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                                      "root not accessable") );

    (*pageiter).GetLastGarbageCollectorPosition(pageno, offset);
}

/* --------------------------------------------------------------------------- */
Log_EntrySequence Log_UndoFile::GetLastEntrySequence ()
{
    SAPDBTRACE_METHOD_DEBUG ("Log_UndoFile::GetLastEntrySequence", LogTrans_Trace, 5);

    m_Container.End (m_Iterator, Data_ForRead);

    if ( m_Iterator.IsValid() )
        return m_Iterator.GetCurrentPage().GetLastEntrySequence();
    else
        return Log_EntrySequence();
}

