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

    module      : Log_ActionUpdateRec.cpp

    -------------------------------------------------------------------------

    author      : JuergenA
    responsible : UweH

    special area: Logging
    description : defines a class to handle update log entries

    last changed: 2001-04-12

    -------------------------------------------------------------------------

    copyright:    (c) 2000-2004 SAP AG


    ========== 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 "Logging/Log_ActionUpdateRec.hpp"

#include "SAPDBCommon/SAPDB_Types.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_IRawAllocator.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp" // Routine trace macros
#include "KernelCommon/Kernel_VTrace.hpp"
#include "KernelCommon/Kernel_Trace.hpp"
#include "Logging/Log_InvDescMap.hpp"
#include "Logging/Log_ReadWriteActionImage.hpp"
#include "Logging/Log_RecordColumnMap.hpp"
#include "Logging/Log_Transaction.hpp"

#include "ggg00.h"
#include "hbd01_1.h"
#include "hbd02.h"
#include "hgg10.h"

/*===========================================================================*
*  METHODS of Log_ActionUpdateRec                                            *
*============================================================================*/

Log_ActionUpdateRec::Log_ActionUpdateRec():

Log_RecordColumnMap        (Log_UpdateRecord),
m_EntryHeader              (),
m_pKey                     (NULL),
m_pInvDescMap              (NULL),
m_pKeyForRead              (NULL),
m_InvDescMapForRead        (),
m_pKeyAllocator            (NULL)
{ }

//---------------------------------------------------------------------------

Log_ActionUpdateRec::~Log_ActionUpdateRec ()
{
    if (NULL != this->m_pKeyAllocator)
    {    
        this->m_pKeyAllocator->Deallocate (this->m_pKeyForRead);
        this->m_pKeyForRead = NULL;
    }
}

//---------------------------------------------------------------------------

void Log_ActionUpdateRec::CreateNewRecAndInitAfterBeforeImage (const tgg00_Filename &PrimFilename,
                                                               const Log_InvDescMap *pInvDescMap,
                                                               const tgg00_Rec      *pOldRecord,
                                                               tgg00_Rec            *pNewRecord,
                                                               SAPDB_Int4            NewRecSize,
                                                               Log_ActionUpdateRec  &BeforeImage,
                                                               tgg00_BasisError     &Error)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::CreateNewRecAndInitAfterBeforeImage", LogAction_Trace, 5);
    KERNEL_TRACE_BASIS_ERROR (Error, LogAction_Trace, 5);
        
    this->CreateNewRecordAndBeforeImage (pOldRecord, pNewRecord, NewRecSize, BeforeImage, Error);

    if (e_ok != Error) return;

    this->Init       (PrimFilename, pInvDescMap, &(pNewRecord->recKey_gg00()), pNewRecord, 0);
    BeforeImage.Init (PrimFilename, pInvDescMap, &(pOldRecord->recKey_gg00()), pOldRecord, 
        BeforeImage.GetNullValLen());
}

//---------------------------------------------------------------------------
void Log_ActionUpdateRec::WriteToTrace (const char * Title) const
{
    Kernel_VTrace trace;
    if ( Title != 0 )
        trace << Title << FlushLine;
    Kernel_TraceBuffer(&m_EntryHeader.PrimFilename, sizeof(m_EntryHeader.PrimFilename),
                       "PrimFilename", sizeof(m_EntryHeader.PrimFilename));
	if ( m_pKey != 0 )
        Kernel_TraceBuffer(&m_pKey->keyVal_gg00(), m_pKey->keyLen_gg00(),
                           "Key", m_pKey->keyLen_gg00());
	else
        trace << "m_pKey is null" << FlushLine;
}

//---------------------------------------------------------------------------

void Log_ActionUpdateRec::Execute (tgg00_TransContext &TransContext) const
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::Execute", LogAction_Trace, 5);
    KERNEL_TRACE_BASIS_ERROR (TransContext.trError_gg00, LogAction_Trace, 5);
    
    if (e_ok != TransContext.trError_gg00) return; 
    
    tgg00_Rec    OldRecord;
    const bool   IsUndoOrRedo = true;
    tgg00_FileId PrimFileId   = b01niltree_id;

    SAPDBMem_IRawAllocator& RawAllocator =
        *(REINTERPRET_CAST (SAPDBMem_IRawAllocator*, TransContext.trAllocator_gg00));
    
    tgg00_Rec *pNewRecord =
        // PTS 1122131 UH 2003-05-14 allocate full buffer
        REINTERPRET_CAST (tgg00_Rec*, RawAllocator.Allocate(sizeof(tgg00_Rec)));
        // REINTERPRET_CAST (tgg00_Rec*, RawAllocator.Allocate(this->m_EntryHeader.NewRecLen));
    
    if (NULL == pNewRecord)
    {
        TransContext.trError_gg00 = e_no_more_memory;
        return;
    }
    
    PrimFileId.fileName_gg00() = this->m_EntryHeader.PrimFilename;
    PrimFileId.fileType_gg00().clear();
    PrimFileId.fileType_gg00().addElement (ftsPerm_egg00);
    PrimFileId.fileType_gg00().addElement (ftsConcurrent_egg00);
    
    b02get_record (TransContext, PrimFileId, *(this->m_pKey), OldRecord);
    
    if ( e_key_not_found == TransContext.trError_gg00
         &&
         reinterpret_cast<Log_Transaction*>(TransContext.trLogContext_gg00)->WasOpenInLastSavepoint() )
    {
        // PTS 1134699 UH 2005-04-04 ignore this error in this case until better checks are available
        TransContext.trError_gg00 = e_ok;
    }
    else
    {
        if (e_ok == TransContext.trError_gg00)
        {
            pNewRecord->recLen_gg00() = m_EntryHeader.NewRecLen;
            
            CreateNewRecord (&OldRecord, pNewRecord, TransContext.trError_gg00);
        }
    
        if (e_ok == TransContext.trError_gg00)
        {
            ExecuteAction (TransContext, PrimFileId, &OldRecord, pNewRecord, IsUndoOrRedo);
        }
    }
    
    RawAllocator.Deallocate (pNewRecord);
}

//----------------------------------------------------------------------------


void Log_ActionUpdateRec::ExecuteAction (tgg00_TransContext &TransContext,
                                         tgg00_FileId       &PrimFileId,
                                         const tgg00_Rec    *pOldRecord,
                                         const tgg00_Rec    *pNewRecord,
                                         bool                IsUndoOrRedo) const
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::ExecuteAction", LogAction_Trace, 5);
    KERNEL_TRACE_BASIS_ERROR (TransContext.trError_gg00, LogAction_Trace, 5);

    if (e_ok != TransContext.trError_gg00)
        return;
    
    bool inSavepoint     = false;
    bool recordIsUpdated = false;

    if ( IsUndoOrRedo )
    {
        inSavepoint = reinterpret_cast<Log_Transaction*>(TransContext.trLogContext_gg00)
                      ->WasOpenInLastSavepoint();

        if ( inSavepoint )
        {
            // PTS 1124375 UH 2003-09-24 recordIsUpdated became always true
            const SAPDB_Byte *oldbody   = reinterpret_cast<const SAPDB_Byte*>(&(pOldRecord->recBody_gg00()))
                                          + pOldRecord->recKeyLen_gg00();
            const SAPDB_Byte *newbody   = reinterpret_cast<const SAPDB_Byte*>(&(pNewRecord->recBody_gg00()))
                                          + pNewRecord->recKeyLen_gg00();
            const SAPDB_UInt  oldlength = pOldRecord->recLen_gg00() - cgg_rec_key_offset -
                                          pOldRecord->recKeyLen_gg00();
            const SAPDB_UInt  newlength = pNewRecord->recLen_gg00() - cgg_rec_key_offset -
                                          pNewRecord->recKeyLen_gg00();

            recordIsUpdated = oldlength == newlength
                              &&
                              0 == memcmp(oldbody, newbody, oldlength);
        }
    }

    // PTS 1128514 UH 2004-03-19 changed error handling
    // In principle: The intention of the undo or redo action must be reached.
    // If a record must be inserted at the end it must be existing with all indexes
    // and it doesn't matter if (e.g. by savepoint/restart) parts of the actions result
    // are already done.
    // Because the trans error can contain any error code only the result can be tested
    // to decide if the acion  was successfull executed or not.
    
    tgg00_BasisError &result = TransContext.trError_gg00;

    result = e_ok;
    
    if ( ! recordIsUpdated )
    {
        m_pInvDescMap->DelInv ( TransContext, IsUndoOrRedo, inSavepoint,
                                PrimFileId, pOldRecord );
        if (e_ok != result && IsUndoOrRedo)
        {
            Kernel_VTrace() << __FILE__ << __LINE__ << NewLine << "IGNORED ERROR is " << result;
            WriteToTrace("Log_ActionUpdateRec::ExecuteAction error from DelInv");
            result = e_ok;
        }
    }
    
    if ( e_ok == result && ! recordIsUpdated )
    {
        b02repl_record (TransContext, PrimFileId, *pNewRecord);
        if (e_ok != result && IsUndoOrRedo)
        {
            WriteToTrace("Log_ActionUpdateRec::ExecuteAction error from b02repl_record");
            Kernel_VTrace() << __FILE__ << __LINE__ << NewLine << "IGNORED ERROR is " << result;
            Kernel_TraceBuffer(&pNewRecord, pNewRecord->recLen_gg00(),
                               "NewRecord", pNewRecord->recLen_gg00());
            result = e_ok;
        }
    }
    
    if ( e_ok == result )
    {
        m_pInvDescMap->AddInv ( TransContext, IsUndoOrRedo, inSavepoint,
                                PrimFileId, pNewRecord);
        if (e_ok != result && IsUndoOrRedo)
        {
            WriteToTrace("Log_ActionUpdateRec::ExecuteAction error from AddInv");
            Kernel_VTrace() << __FILE__ << __LINE__ << NewLine << "IGNORED ERROR is " << result;
            result = e_ok;
        }
    }
}

//----------------------------------------------------------------------------

void Log_ActionUpdateRec::Init (const tgg00_Filename &PrimFilename,
                                const Log_InvDescMap *pInvDescMap,
                                const tgg00_Lkey     *pKey,
                                const tgg00_Rec      *pNewRecord,
                                const SAPDB_UInt2     NullValLen)
{
    this->m_EntryHeader.PrimFilename = PrimFilename;
    this->m_pKey                     = pKey;
    this->m_pInvDescMap              = pInvDescMap;
    
    this->m_EntryHeader.KeyLen     = sizeof(tgg00_HeaderRec) + pKey->keyLen_gg00();
    this->m_EntryHeader.NewRecLen  = pNewRecord->recLen_gg00() + NullValLen;
    this->m_EntryHeader.ColMapLen  = this->GetPersistentColumnMapLength();
    this->m_EntryHeader.InvDescLen = 
        (NULL == this->m_pInvDescMap) ? 0 : this->m_pInvDescMap->GetPersistentLength();
    
    this->m_EntryHeader.ImageLen = 
        Log_AlignedImageLen   (sizeof(this->m_EntryHeader))
        + Log_AlignedImageLen (this->m_EntryHeader.KeyLen)
        + Log_AlignedImageLen (this->m_EntryHeader.ColMapLen)
        + Log_AlignedImageLen (this->m_EntryHeader.InvDescLen);
}

//----------------------------------------------------------------------------

void Log_ActionUpdateRec::InitAfterBeforeImageWithExistingNewRec (SAPDBMem_IRawAllocator &RawAllocator,
                                                                  const tgg00_Filename   &PrimFilename,
                                                                  const Log_InvDescMap   *pInvDescMap,
                                                                  const tgg00_Rec        *pOldRecord,
                                                                  const tgg00_Rec        *pNewRecord,
                                                                  Log_ActionUpdateRec    &BeforeImage,
                                                                  tgg00_BasisError       &Error)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::InitAfterBeforeImageWithExistingNewRec", LogAction_Trace, 5);
    KERNEL_TRACE_BASIS_ERROR (Error, LogAction_Trace, 5);
    
    this->CreateColDescForExistingNewRec (RawAllocator, pOldRecord, pNewRecord, BeforeImage, Error);

    if (e_ok != Error) return;
    
    this->Init       (PrimFilename, pInvDescMap, &(pNewRecord->recKey_gg00()), pNewRecord, 0);
    BeforeImage.Init (PrimFilename, pInvDescMap, &(pOldRecord->recKey_gg00()), pOldRecord, 0);
}

//----------------------------------------------------------------------------

void Log_ActionUpdateRec::ReadImagePersistent (Log_ActionImageReader &ImageReader,
                                               bool                  &IsOk)
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::ReadImagePersistent", LogAction_Trace, 5);
    KERNEL_TRACE_IS_OK (IsOk, LogAction_Trace, 5);
    
    this->m_pInvDescMap = &(this->m_InvDescMapForRead);
    
    ImageReader.CopyImage (
        REINTERPRET_CAST (SAPDB_Byte*, &(this->m_EntryHeader)),
        sizeof(this->m_EntryHeader),
        IsOk);
    
    if ( ! IsOk ) return;
    
    ImageReader.PersistentRead (REINTERPRET_CAST (SAPDB_Byte*&, this->m_pKeyForRead),
        this->m_EntryHeader.KeyLen, this->m_pKeyAllocator, IsOk);
    
    if ( ! IsOk ) return;
    
    this->m_pKey = this->m_pKeyForRead;

    this->ReadColumnMapPersistent (ImageReader, this->m_EntryHeader.ColMapLen, IsOk);
    
    if ( ! IsOk ) return;
    
    if (this->m_EntryHeader.InvDescLen > 0)
    {
        this->m_InvDescMapForRead.ReadImagePersistent
            (ImageReader, this->m_EntryHeader.InvDescLen, IsOk);
    }
}

//----------------------------------------------------------------------------

SAPDB_UInt4 Log_ActionUpdateRec::RemoveRedundantInvDescEntries (SAPDBMem_IRawAllocator &RawAllocator,
                                                         Log_InvDescMap         *pOwnWriteAccessInvDescMap,
                                                         bool                   &IsOk)
                                                         
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::RemoveRedundantInvDescEntries", LogAction_Trace, 5);
    KERNEL_TRACE_IS_OK (IsOk, LogAction_Trace, 5);
    
    if ((NULL == this->m_pInvDescMap) || 
        (pOwnWriteAccessInvDescMap != this->m_pInvDescMap)) return (0);
    
    pOwnWriteAccessInvDescMap->RemoveRedundantInvDescEntries (RawAllocator, *this, IsOk);

    this->m_EntryHeader.InvDescLen = pOwnWriteAccessInvDescMap->GetPersistentLength ();
    return (this->m_EntryHeader.InvDescLen);
}

//----------------------------------------------------------------------------

void Log_ActionUpdateRec::WriteImagePersistent (Log_ActionImageWriter &ImageWriter,
                                                bool                  &IsOk)       const                         
{
    SAPDBTRACE_METHOD_DEBUG ("Log_ActionUpdateRec::WriteImagePersistent", LogAction_Trace, 5);
    KERNEL_TRACE_IS_OK (IsOk, LogAction_Trace, 5);
    SAPDBTRACE_WRITELN (LogAction_Trace, 6, "ImageLen:" << m_EntryHeader.ImageLen);
    
    ImageWriter.PersistentWrite (&(this->m_EntryHeader), sizeof (this->m_EntryHeader), IsOk);
    
    if ( ! IsOk ) return;
    
    ImageWriter.PersistentWrite (this->m_pKey, this->m_EntryHeader.KeyLen, IsOk);
    
    if ( ! IsOk ) return;
    
    this->WriteColumnMapPersistent (ImageWriter, IsOk);
    
    if ( ! IsOk ) return;
    
    if (this->m_EntryHeader.InvDescLen > 0)
    { 
        this->m_pInvDescMap->WriteImagePersistent (ImageWriter, IsOk);
    }
}
