/*!
    @file     SharedSQL_Plan.hpp
    @ingroup  SharedSQL
    @author   DirkT
    @brief    AVLTree to store the execution plan for SQLCommands
    @see            

\if EMIT_LICENCE
    ========== 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

\endif
*/

#ifndef SHAREDSQL_PLAN_HPP
#define SHAREDSQL_PLAN_HPP

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

#include "SAPDB/SQLManager/SharedSQL/SharedSQL_Types.hpp"
#include "SAPDB/SQLManager/Catalog/Catalog_ISharedSQLInterface.hpp"
#include "SAPDB/SQLManager/SharedSQL/SharedSQL_Messages.hpp"

#include "SAPDB/Container/Container_AVLTree.hpp"
#include "SAPDB/Container/Container_Vector.hpp"

#include "SAPDB/SAPDBCommon/SAPDB_Types.hpp"
#include "SAPDB/SAPDBCommon/MemoryManagement/SAPDBMem_IRawAllocator.hpp"
#include "SAPDB/SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"
#include "SAPDB/SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"

#include "SAPDB/RunTime/Synchronisation/RTESync_RWRegion.hpp"

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

/// PlanComparator
typedef Catalog_ISharedSQLInterface SharedSQL_PlanComparator;

/*---------------------------------------------------------------------------*/
/*!
    @class          SharedSQL_PlanNode
    @brief          Node within the SharedSQL_PlanTree
 */
class SharedSQL_PlanNode : public Container_AVLNode<void*, SharedSQL_PlanComparator>
{
public:
    /*!
        @brief  Constructor
        @param  k [in]
    */
    SharedSQL_PlanNode( void *const& k  ) : 
        Container_AVLNode<void*, SharedSQL_PlanComparator>(k) {};

    /*!
        @copydoc        Container_AVLNode
    */
    virtual void CleanUp(SAPDBMem_IRawAllocator& alloc, SharedSQL_PlanComparator& cmp)
    {
        cmp.DestroyPlanObject(alloc, m_Key);
    }
};  // SharedSQL_PlanNode

/*---------------------------------------------------------------------------*/
/*!
    @brief          Container_AVLTree to store SharedSQ_PlanNode 
 */
typedef Container_AVLTree<SharedSQL_PlanNode, void*, SharedSQL_PlanComparator>
        SharedSQL_PlanTree;

/*---------------------------------------------------------------------------*/
/*!
    @class          SharedSQL_Plan
    @brief          Class to handle execution plans
 */
class SharedSQL_Plan
{
public: 

    /*!
        @brief  Destructor
    */
    ~SharedSQL_Plan()
    {
    }

	class PlanPartIterator
	{
		friend class SharedSQL_Plan;
		friend class SharedSQL_PrepareHandle;

	protected:

		PlanPartIterator( SharedSQL_PlanTree& PlanTree )
			: Iter(PlanTree.First())
		{
		}

		PlanPartIterator( void )
			: Iter()
		{
		}

	public :
        /*!
           @brief steps to the next plan part
         */
        void operator++()
		{
			Iter++;
		}
        /*!
          @brief true, if the iterator points to a column; otherwise false
         */
        operator bool() const
		{
			return Iter.IsValid();
		}
        /*!
           @brief returns true, if 2 iterators are equal
         */
        bool operator== (const PlanPartIterator& Compare) const
		{
			return ( Iter() == Compare.Iter() );
		}
        /*!
           @brief returns true, if 2 iterators are not equal
         */
        bool operator!= (const PlanPartIterator& Compare) const
		{
			return ( Iter() != Compare.Iter() );
		}
        /*!
          @brief yields a column descriptor for the current column
         */
        const void* operator*() const
		{
			return *Iter()->GetKey();
		}

	private:

		Container_AVLTree<SharedSQL_PlanNode, void*, SharedSQL_PlanComparator>::Iterator Iter;
	};

protected:
	friend class SharedSQL_CachedCommand;	// no one but SharedSQL_CachedCommand must access SharedSQL_Plan !!!
	friend class SharedSQL_PrepareHandle;
	friend class SharedSQL_ExecuteHandle;

	/*!
        @brief  Constructor
        @param  Allocator    [in]
    */
    SharedSQL_Plan( SAPDBMem_IRawAllocator& Allocator)
		: mNextPlan(0)
		, mPrevPlan(0)
        , mPlanComparator()
	    , mPlanTree(&mPlanComparator, &Allocator)
        , mPlanSize(0)
        , mMasterParseID()
        , mTables(Allocator)
		, mStatus(New)
		, mParseIDCount(0)
		, mExecuteCount(0)
    {
    }

    /*!
        @brief  Returns the allocator used to store the execution plan
        @return SAPDBMem_IRawAllocator*
    */
    inline SAPDBMem_IRawAllocator* GetAllocator( void )
    {
        return mPlanTree.GetAllocator();
    }

    /*!
        @brief  Returns wheather or not there is a plan
        @return SAPDB_Bool
    */
    inline SAPDB_Bool IsEmpty( void ) const
    {
        return ( mPlanTree.GetSize() == 0);
    }

    /*!
        @brief  Returns the size of the execution plan
        @return SAPDB_Int4
    */
    inline SAPDB_Int4 GetSize( void ) const
    {
        return mPlanSize;
    }

	inline PlanPartIterator GetPlanPartIterator( void )
	{
		return PlanPartIterator(mPlanTree);
	}

    /*!
        @brief  Returns some plan element identified by the key
        @param  Key [in]
        @return const void*
    */
    inline const void* Find( void* Key ) const
    {
        if (SharedSQL_PlanNode* N = mPlanTree.Find(Key))
                return (*(N->GetKey()));
        return 0;
    }

    /*!
        @brief  Insert the execution plan
        @param  PID  [in] - ParseID used during preparation
        @param  CPID [in] - ParseID for comparing with the parseid cache
        @param  P    [in] - Pointer to the execution plan
        @param  Size [in] - Size of the execution plan
        @param  MsgList [out] - Message list
        @return ...
    */
    inline SAPDB_Bool Insert( SharedSQL_ParseID MPID, void *const P, SAPDB_ULong Size )
    {
        if ( mMasterParseID == SharedSQL_ParseID() )
            mMasterParseID = MPID;

        if ( MPID == mMasterParseID )
        {
            Container_AVLTreeError rc;
            if (mPlanTree.Insert(P, rc) )
            {
                mPlanSize += Size;
                return SAPDB_TRUE;
            }
            else
            {
                switch (rc)
                {
                case AVLTree_DuplicateKey:
                    RTE_Message(SAPDBErr_MessageList("SharedSQL", __CONTEXT__, SHAREDSQL_ERR_PUTPLAN_DUPKEY));
                    break;
                case AVLTree_OutOfMemory:
                    RTE_Message(SAPDBErr_MessageList("SharedSQL", __CONTEXT__, SHAREDSQL_ERR_PUTPLAN_NOMEM));
                    break;
                }
            }
        }
        else
        {
            RTE_Message(SAPDBErr_MessageList("SharedSQL", __CONTEXT__, SHAREDSQL_ERR_PUTPLAN_INVALIDID));
        }
        return SAPDB_FALSE;
    }


    inline SAPDB_Bool InsertTableInfo( SharedSQL_Table Table )
    {
        if ( !mTables.InsertEnd(Table) )
        {
            RTE_Message(SAPDBErr_MessageList("SharedSQL", __CONTEXT__, SHAREDSQL_ERR_PUTTABLES));
            return false;
        }
        return true;
    }

	inline void ClearTableInfo( void )
	{
		mTables.Clear();
	}


    /*!
        @brief  ContainsTable
        @param  Tables   [in] pointer to vector of SharedSQL_Table
        @return true if the command contains one of the tables given by Tables
    */
    inline SAPDB_Bool ContainsTable( SharedSQL_TableTree* Tables ) const
    {
        SAPDBERR_ASSERT_ARGUMENT( Tables );

        SAPDB_Int2 m = mTables.GetSize();
        if ( m>0 && Tables )
        {
            for ( SAPDB_Int2 i=0; i<m; i++ )
            {
                SharedSQL_TableTree::Iterator iter = Tables->First();
                while (iter)
                {
                     if ( mTables[i] == *iter()->GetKey())
                     {
                          return true;
                     }
                     ++iter;

                }
            }
            return false;
        }
        return true;
    }

    /*!
        @brief  ContainsSchema
        @param  Schema   [in]
        @return true if the command contains one of the tables given by Tables
    */
    inline SAPDB_Bool ContainsSchema( SharedSQL_Schema Schema ) const
    {
        SAPDB_Int2 m = mTables.GetSize();
        if ( (m > 0) && (Schema != SharedSQL_Schema()) )
        {
            for ( SAPDB_Int2 i=0; i<m; i++ )
            {
	            if ( (mTables[i].mSchema == Schema) || 
					 (mTables[i].mSchema == SharedSQL_Schema()) )
	                return true;
			}
            return false;
        }
        return true;
    }

    /*!
        @brief  Returns the master parseid. e.g. the parse id used to prepare the command.
        @return SharedSQL_ParseID&
    */
    inline SharedSQL_ParseID& GetMasterParseID( void )
    {
        return mMasterParseID;
    }

	inline SharedSQL_Plan*& NextPlan( void )
	{
		return mNextPlan;
	}

	inline SharedSQL_Plan*& PrevPlan( void )
	{
		return mPrevPlan;
	}

	inline void DeletePlanElements( void )
	{
		mPlanTree.DeleteAll();
		mPlanSize = 0;		
		SetStatus(Dropped);
	}

	inline void IncExecuteCount( void )
	{
		SAPDBERR_ASSERT_STATE( mParseIDCount >= mExecuteCount );
		++mExecuteCount;
	}

	inline void DecExecuteCount( bool MayDelete )
	{
		--mExecuteCount;
		SAPDBERR_ASSERT_STATE( mParseIDCount >= mExecuteCount );
		if ( MayDelete && !mExecuteCount && (mStatus != Prepared) )
		{
			mPlanTree.DeleteAll();
			mPlanSize = 0;		
		}
	}

	inline SAPDB_Int4 GetExecuteCount( void )
	{
		SAPDBERR_ASSERT_STATE( mParseIDCount >= mExecuteCount );
		return mExecuteCount;
	}

	inline void IncParseIDCount( void )
	{
		SAPDBERR_ASSERT_STATE( mParseIDCount >= mExecuteCount );
		++mParseIDCount;
	}

	inline SAPDB_Int4 DecParseIDCount( void )
	{
		--mParseIDCount;
		SAPDBERR_ASSERT_STATE( mParseIDCount >= mExecuteCount );
		return mParseIDCount;
	}

	inline SAPDB_Int4 GetParseIDCount( void )
	{
		return mParseIDCount;
	}

	SharedSQL_CommandStatus GetStatus( void )
	{
		return mStatus;
	}

	void SetStatus( SharedSQL_CommandStatus Status )
	{
	    if ( !((mStatus == Invalid) && (Status==Dropped)) )
		    mStatus = Status; 
	}

private:
	/// ...
	SharedSQL_Plan*						mNextPlan;
	/// ...
	SharedSQL_Plan*						mPrevPlan;
    /// AVL Tree that stores the execution plan
    SharedSQL_PlanTree	   			    mPlanTree;
	/// ...
	SAPDB_Int4                          mExecuteCount;
	SAPDB_Int4                          mParseIDCount;
	/// Size of the plan elements stored in the plan tree
    SAPDB_Int4                          mPlanSize;
    /// ParseID used to create the execution plan
    SharedSQL_ParseID                   mMasterParseID;
    /// Comparator to compare and find plan elements
    SharedSQL_PlanComparator            mPlanComparator;
	/// ...
	SharedSQL_CommandStatus				mStatus;

    /// The tables belonging to this command
    Container_Vector<SharedSQL_Table>   mTables;
};

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

#endif  /* SHAREDSQL_PLAN_HPP */
