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

  module      : gbd494.h

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

  author      : TorstenS
  responsible : TorstenS

  special area: FunnelHandling
  description : 


  last changed: 1999-10-20  10:00
  see also    : 

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

  copyright:    (c) 1999-2004 SAP AG



    ========== licence begin  GPL
    Copyright (c) 1999-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


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


#ifndef GBD494_H
#define GBD494_H

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

// Content of include files

#include "gsp00.h"   // PASCAL: SP_basic_constants_and_types
#include "ggg00.h"   // PASCAL: GG_kernel_constants_and_types
#include "gbd00.h"   // PASCAL: BD_kernel_constants_and_types

#include "gbd400.h"  // CPP   : InvTree Handling
#include "gbd495.h"  // CPP   : DoubleQueueHandling
#include "gbd497.h"  // CPP   : StackDescInfo
#include "gbd500.h"  // CPP   : Tree Handling
#include "gbd600.h"  // CPP   : NodeHandling
#include "ggg200.h"  // CPP   : SortTemplates
#include "hsp30.h"   // PASCAL: Buffer handling and comparison routines


#include <new.h>

#if COMPILEMODE_MEO00 >= SLOW_MEO00 
#include "hta99.h"
#endif

/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/


#define MAX_RETRIES_BD494           10

#define CANCEL_MSG_1_BD494          "Funnel: Init Phase Canceled             "
#define CANCEL_MSG_2_BD494          "Funnel: Build Up Phase Canceled         "
#define MERGE_MSG_1_BD494           "Funnel Merge Failed: Retry Funnel       "
#define MERGE_MSG_2_BD494           "Funnel: Not More Memory "


/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/


/*===========================================================================*
 *  FORWARD DECLARATION                                                      *
 *===========================================================================*/


/*===========================================================================*
 *  CLASSES, STRUCTURES, TYPES, UNIONS ...                                   *
 *===========================================================================*/

struct tbd494_SortItem // Size is 16 or 24 Bytes
{
    tsp00_BytePtr   pPrimKey_bd494;
    tsp00_BytePtr   pSecKey_bd494;
    tsp00_Int2      PrimKeyLen_bd494;
    tsp00_Int2      SecKeyLen_bd494;
    tsp00_Bool      bInvListRefNeeded_bd494;
    tsp00_Bool      bNextSecKey_bd494;
    tsp00_Bool      bLastPrimKey_bd494;
    tsp00_Bool      Filler1_bd494;
};

typedef tbd494_SortItem* tbd494_SortItemPtr;

/*---------------------------------------------------------------------------*/

class cbd494_CompareOperator : public cgg200ComparisonOperator <tbd494_SortItemPtr>
{
public:

    cbd494_CompareOperator(    // PTS 1124759 2003-10-21
        tgg00_BasisError            &basisError,
        const cbd497_StackDescInfo  &stackDescInfo )  
    :
    m_TrError( basisError ),
    m_Stack( stackDescInfo ),
    m_CompResult( tsp00_LcompResult::fromConst( l_undef ))
    {}

    tsp00_LcompResult_Enum gg200Compare(
        const tbd494_SortItemPtr    &pSortItem1,
        const tbd494_SortItemPtr    &pSortItem2 );

private:

    bool IsIndexUnique() const{
        return m_Stack.bd497IsInvUnique();
    }

    bool AllColumnsAreNull( const tsp00_BytePtr pSecKey ) const // PTS 1124759 2003-10-21
    {
        tsp00_Int4 offset = 0;

        for( tsp00_Int4 currColumn = m_Stack.bd497FirstColumn();
                currColumn <= m_Stack.bd497LastColumn(); currColumn++ )
        {
            const tsp00_Byte defineByte =
                m_Stack.bd497IsColumnDescending( currColumn ) ?
                ~csp_undef_byte : csp_undef_byte;

            if( defineByte != *( pSecKey + offset )){
                return false; // at least one column is not null
            }
            offset += m_Stack.bd497GetColumnLength( currColumn );
        }
        return true;
    };

private:

    tgg00_BasisError            &m_TrError;
    const cbd497_StackDescInfo  &m_Stack;  // PTS 1124759 2003-10-21
    tsp00_LcompResult           m_CompResult;
};

/*---------------------------------------------------------------------------*/

class cbd494_SortableDoubleWriteQueue :
            public cbd495_DoubleWriteQueue, public cgg200_MergeTarget <tbd494_SortItemPtr>
{
public:

    cbd494_SortableDoubleWriteQueue(
        tbd_current_tree    &QueueCurrent,
        tsp00_PageNo         PrimQueuePno,
        tsp00_PageNo         SecQueuePno);

private:

    void bd494_PushPrimKey(
        tsp00_BytePtr   pPrimKey,
        tsp00_Int4      PrimKeyLen);

    void bd494_PushSecKey(
        tsp00_BytePtr   pSecKey,
        tsp00_Int4      SecKeyLen);

    bool gg200SetNextElement(
        tbd494_SortItemPtr  pSortItem);

protected:

    tgg00_BasisError    &m_TrError;

private:

    tsp00_Int4  m_NumPrimKeys;
    tsp00_Int4  m_MinRequiredPrimKeySpace;
};

/*---------------------------------------------------------------------------*/

class cbd494_SortableInvTree :
            public cbd400_InvTree, public cgg200_MergeTarget <tbd494_SortItemPtr>
{
public:

    cbd494_SortableInvTree(
        cbd600_Node &LeafNode,
        tsp00_Int4  &SecKeyCount,
        tsp00_Int4  &SecLeafCount);

    ~cbd494_SortableInvTree();

private:

    bool gg200SetNextElement(
        tbd494_SortItemPtr  pSortItem);

private:

    tsp00_Int4  &m_SecKeyCount;
    tsp00_Int4  &m_SecLeafCount;
    /* */
    tsp00_Int4  m_InvRecIndex;
};

/*---------------------------------------------------------------------------*/

class cbd494_Funnel : public cgg200_MergeSource <tbd494_SortItemPtr>
{
public:

    cbd494_Funnel(
        tbd_current_tree            &QueueCurrent,
        const cbd497_StackDescInfo  &stackDescInfo,
        tsp00_BytePtr               pFunnel,
        tsp00_Int4                  NumFunnelItems);

    ~cbd494_Funnel();

    void bd494Add(
        bool            bIsQueue,
        tsp00_PageNo    PrimKeyQueue,
        tsp00_PageNo    SecKeyQueue = NIL_PAGE_NO_GG00);

    static tsp00_Int4 bd494GetFunnelItemSize();

    static bool bd494MemoryAvailable(
        tsp00_Int4  NumFunnelItems,
        tsp00_Int4  NumQueues,
        tsp00_Int4  NumTrees);

    void bd494Merge(
        cgg200_MergeTarget <tbd494_SortItemPtr> &DestinationFile);

private:

    bool gg200GetCurrentListElement(
        unsigned int         CurrQueueIndex,
        tbd494_SortItemPtr  &pSortItem);

    void gg200GotoNextListElement(
        unsigned int    CurrQueueIndex);

    unsigned int gg200GetListCount();

private:

    tbd_current_tree        &m_QueueCurrent;
    tgg00_BasisError        &m_TrError;
    tgg00_TransContext      &m_Trans;
    /* */
    tsp00_Int4              m_NumFunnelItems;
    tsp00_Int4              m_NumMergeQueues;
    tsp00_Int4              m_QueuePos; // interacts with m_pQueue
    tsp00_Int4              m_QueueSpaceAvailable;
    tsp00_BytePtr           m_pFunnel;
    tsp00_BytePtr           m_pQueue;
    tbd495_InvQueuePtr*     m_ppQueue;
    tbd494_SortItemPtr      m_pSortItem;

    cbd494_CompareOperator  m_CompareOperator;
};


/*===========================================================================*
*  DEFINITION OF METHODS                                                    *
*===========================================================================*/

inline tsp00_LcompResult_Enum
cbd494_CompareOperator::gg200Compare (
    const tbd494_SortItemPtr   &pSortItem1,
    const tbd494_SortItemPtr   &pSortItem2)
{
    ROUTINE_DBG_MEO00 ("bd494Compare");


    s30cmp (pSortItem1->pSecKey_bd494, POS_OFF_DIFF_BD00, pSortItem1->SecKeyLen_bd494,
            pSortItem2->pSecKey_bd494, POS_OFF_DIFF_BD00, pSortItem2->SecKeyLen_bd494,
            m_CompResult);

    if( l_equal == m_CompResult )
    {
        if(
            ( IsIndexUnique())
            &&
            ( ! AllColumnsAreNull( pSortItem1->pSecKey_bd494 )) // PTS 1124759 2003-10-21
        )
        {
            m_TrError = e_duplicate_invkey;
            return m_CompResult;
        }

        s30cmp (
            pSortItem1->pPrimKey_bd494, POS_OFF_DIFF_BD00, pSortItem1->PrimKeyLen_bd494,
            pSortItem2->pPrimKey_bd494, POS_OFF_DIFF_BD00, pSortItem2->PrimKeyLen_bd494,
            m_CompResult);

        // Note that the Funnel Merge uses the smaller key as the winner

        if (l_greater == m_CompResult)
        {
            pSortItem1->bNextSecKey_bd494  = false; // The greater one couldn't contain the next secondary key
            pSortItem2->bLastPrimKey_bd494 = false; // The smaller one couldn't contain the last primary key
        }
        else // could be l_less only;
        {
            pSortItem1->bLastPrimKey_bd494 = false; // see above
            pSortItem2->bNextSecKey_bd494  = false; // see above
        }
    }
    return m_CompResult;

}

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

inline
cbd494_Funnel::cbd494_Funnel(
    tbd_current_tree            &QueueCurrent,
    const cbd497_StackDescInfo  &stackDescInfo,
    tsp00_BytePtr               pFunnel,
    tsp00_Int4                  NumFunnelItems )
        :
        m_QueueCurrent( QueueCurrent ),
        m_TrError( QueueCurrent.curr_trans->trError_gg00 ),
        m_Trans( *QueueCurrent.curr_trans ),
        m_NumFunnelItems( NumFunnelItems ),
        m_NumMergeQueues( 0 ),
        m_QueuePos( 0 ),
        m_QueueSpaceAvailable( NumFunnelItems * cbd495_DoubleReadQueue::bd495GetSize()),
        m_pFunnel( pFunnel), // m_pFunnel, memory for Funnel Merge in ggg200.h
        m_CompareOperator( QueueCurrent.curr_trans->trError_gg00, stackDescInfo )
{
    ROUTINE_DBG_MEO00 ("cbd494Funnel");


    // m_pSortItem: memory for SortItems because the Funnel Merge works with
    // Pointers only for two reasons:
    // [1] to reduce copy costs
    // [2] to enable modifing parts of the SortItem within the Compare(!) methode

    tsp00_Int4 RequiredSpace = bd494GetFunnelItemSize() * m_NumFunnelItems;
    m_pSortItem = REINTERPRET_CAST (tbd494_SortItemPtr, (m_pFunnel + RequiredSpace));

    // m_ppQueue: memory for cbd495InvQueue pointers

    RequiredSpace += (sizeof (tbd494_SortItem) * m_NumFunnelItems);
    m_ppQueue = REINTERPRET_CAST (tbd495_InvQueuePtr*, (m_pFunnel + RequiredSpace));

    // m_pQueue: memory for cbd495InvQueues itself (placement new)

    RequiredSpace += (sizeof (m_ppQueue) * m_NumFunnelItems);
    m_pQueue       = m_pFunnel + RequiredSpace;
}

/*---------------------------------------------------------------------------*/

inline
cbd494_Funnel::~cbd494_Funnel()
{
    ROUTINE_DBG_MEO00 ("~cbd494Funnel");


    // Destroy all InvQueue objects generated before in Funnel Merge memory.
    // This is necessary only if gg200Merge failed, because the Funnel Merge
    // frees page after page if it isn't needed anymore.

    for (tsp00_Int4 CurrQueueIndex = 0; CurrQueueIndex < m_NumMergeQueues; ++CurrQueueIndex)
        (*(m_ppQueue + CurrQueueIndex))->bd495Free();
}

/*---------------------------------------------------------------------------*/

inline tsp00_Int4
cbd494_Funnel::bd494GetFunnelItemSize()
{
#   ifdef BIT64
    return ALIGN_8BYTE_EO00 (sizeof (cgg200_MergeNode <tbd494_SortItemPtr>));
#   else
    return ALIGN_4BYTE_EO00 (sizeof (cgg200_MergeNode <tbd494_SortItemPtr>));
#   endif
}

/*---------------------------------------------------------------------------*/

inline bool
cbd494_Funnel::bd494MemoryAvailable(
    tsp00_Int4  NumFunnelItems,
    tsp00_Int4  NumQueues,
    tsp00_Int4  NumTrees)
{
    return (
               (
                   (NumQueues * cbd495_DoubleReadQueue::bd495GetSize()) +
                   (NumTrees  * cbd495_TempInvTree::bd495GetSize())
               )
               <=
               (NumFunnelItems * cbd495_DoubleReadQueue::bd495GetSize())
           );
}

/*---------------------------------------------------------------------------*/

inline unsigned int
cbd494_Funnel::gg200GetListCount()
{
    ROUTINE_DBG_MEO00 ("bd494GetListCount");


    return m_NumMergeQueues;
}

/*---------------------------------------------------------------------------*/

inline void
cbd494_Funnel::gg200GotoNextListElement (
    unsigned int    CurrQueueIndex)
{
    ROUTINE_DBG_MEO00 ("bd494GotoNextListElement");


    (*(m_ppQueue + CurrQueueIndex))->bd495GotoNextItem();
}

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

inline
cbd494_SortableDoubleWriteQueue::cbd494_SortableDoubleWriteQueue(
    tbd_current_tree    &QueueCurrent,
    tsp00_PageNo         PrimQueuePno,
    tsp00_PageNo         SecQueuePno)
        :
        cbd495_DoubleWriteQueue     (QueueCurrent, PrimQueuePno, SecQueuePno),
        m_TrError                   (QueueCurrent.curr_trans->trError_gg00),
        m_NumPrimKeys               (0),
        m_MinRequiredPrimKeySpace   (0)
{
    ROUTINE_DBG_MEO00 ("cbd494_SortableDoubleWriteQueue");

    //Accessing an existing DoubleWriteQueue
}

/*---------------------------------------------------------------------------*/

inline void
cbd494_SortableDoubleWriteQueue::bd494_PushPrimKey(
    tsp00_BytePtr   pPrimKey,
    tsp00_Int4      PrimKeyLen)
{
    m_PrimKeyQueue.bd550Push (PrimKeyLen, pPrimKey );
}

/*---------------------------------------------------------------------------*/


inline void
cbd494_SortableDoubleWriteQueue::bd494_PushSecKey(
    tsp00_BytePtr   pSecKey,
    tsp00_Int4      SecKeyLen)
{
    m_SecKeyQueue.bd550Push (SecKeyLen, pSecKey);
}

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

inline
cbd494_SortableInvTree::cbd494_SortableInvTree(
    cbd600_Node &LeafNode,
    tsp00_Int4  &SecKeyCount,
    tsp00_Int4  &SecLeafCount)
        :
        cbd400_InvTree  (LeafNode),
        m_SecKeyCount   (SecKeyCount),
        m_SecLeafCount  (SecLeafCount),
        m_InvRecIndex   (NIL_RECINDEX_BD00)
{
    ROUTINE_DBG_MEO00 ("cbd494_SortableInvTree");
}

/*---------------------------------------------------------------------------*/

inline
cbd494_SortableInvTree::~cbd494_SortableInvTree()
{
    ROUTINE_DBG_MEO00 ("~cbd494_SortableInvTree");

    // Maintain leaf count information of the rightmost leaf up to
    // the root, if the last append has been made.

    if (e_ok != m_TrError)  // PTS 1120130 TS 2003-01-17
        return;

    bd520LeafCountPropagation();

    m_SecLeafCount = bd500LeafCount();
}
#endif  /* GBD494_H */

