///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBTreeCursor.cc
// ------------------
// Cego index cursor class implementation
//         
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoBTreeCursor
// 
// Description: Cursor class for traversing btree objects
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// POSIX INCLUDES
#include <string.h>
#include <stdlib.h>

// CEGO INCLUDES
#include "CegoBTreeCursor.h"
#include "CegoTupleState.h"
#include "CegoBTreeNode.h"
#include "CegoBTreeManager.h"

CegoBTreeCursor::CegoBTreeCursor()
{
}

CegoBTreeCursor::CegoBTreeCursor(CegoTableManager *pTabMng, unsigned tabSetId, const Chain& btreeName, CegoObject::ObjectType type, CegoAttrCond* pAttrCond, bool ignoreTouched, bool readUncommitted)
{
    _pTabMng = pTabMng;
    _pDBMng = pTabMng->getDBMng();
    _btreeName = btreeName;
    _type = type;
    _pAttrCond = pAttrCond;

    _tabSetId = tabSetId;
    _ignoreTouched = ignoreTouched;

    _readUncommitted = readUncommitted;
    _numComp=0;
    _cursorCached = false;
    _eoc=true;
    _lockId = 0;

    _modId = _pTabMng->getDBMng()->getModId("CegoBTreeCursor");
}

CegoBTreeCursor::~CegoBTreeCursor()
{
    abort();
}

bool CegoBTreeCursor::getFirst(ListT<CegoField>& fl, CegoDataPointer& dp)
{
    _eoc=false;

    if ( fl.isEmpty() )
    {
	throw Exception(EXLOC, "Empty field list");
    }

    if ( _cursorCached == false )
    {
	CegoBTreeObject btoe;

	_pTabMng->getObject(_tabSetId, _btreeName, _type, btoe);

	_btreeSchema = btoe.getSchema();

	_keyLen = CegoBTreeValue::getKeyLen(_btreeSchema);

	_rootPageId = btoe.getDataPageId();

	Chain tabName = btoe.getTabName();

	if ( _pAttrCond )
	    prepareComp();
	
	_cursorCached = true;
    }

    _lockId = _pTabMng->getLockHandler()->lockData(CegoObject::BTREE, _rootPageId, CegoLockHandler::READ);
    
    bool found = false;
    
    if (_pAttrCond)
    {
	CegoAttrComp* pAC = _pAttrCond->getAttrCompSet().First();
	while ( pAC )
	{
	    if ( pAC->getCompMode() == CegoAttrComp::BTWN )
		pAC->setComparison( MORE_EQUAL_THAN );	    
	    pAC = _pAttrCond->getAttrCompSet().Next();
	}

	_btwCompAdjusted = true;

	switch ( _pAttrCond->getPrimaryComparison() )
	{
	case NOT_EQUAL:
	case LESS_THAN:
	case LESS_EQUAL_THAN:
	{
	    // go to the beginning
	    
	    bool leafFound = false;
	    _curPageId = _rootPageId;

	    while ( leafFound == false )
	    {		
		CegoBufferPage bp;
		_pDBMng->bufferFix(bp, _tabSetId, _curPageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());
				
		if ( bp.getType() == CegoBufferPage::BTREE_NODE )
		{		    
		    CegoBTreeNode traceNode;
		    traceNode.setPtr(bp.getChunkEntry(), bp.getChunkLen());	    
		    traceNode.setSchema(CegoBTreeNode::NODE, &_btreeSchema, _keyLen);
		    traceNode.setPageId(bp.getPageId());
		    
		    traceNode.reset();
		    traceNode.nextChildPointer(_curPageId);
		    
		    _parentPageStack.Push(bp);
		}
		else if ( bp.getType() == CegoBufferPage::BTREE_LEAF )
		{
		    _curPage = bp;
		    _curLeaf.setPtr(_curPage.getChunkEntry(), _curPage.getChunkLen());
		    _curLeaf.setPageId(_curPage.getPageId());
		    _curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);

		    leafFound=true;

		    _curLeaf.reset();

		    bool goOn = true;
		    while ( goOn )
		    {
			CegoBTreeValue iv;
			unsigned numEntries = _curLeaf.numEntries();
					
			if ( numEntries > 0 )
			{
			    // cout << "Detected " << numEntries << " entries" << endl; 
			    unsigned u = numEntries;
			    unsigned d = 0;

			    unsigned pos = 0;
			    
			    while ( u > d + 1 )
			    {
				pos = ( u + d ) / 2;
				_curLeaf.getValue(pos, iv, dp);

				// cout << "Leaf trace value on position " << pos << " is  " << iv.toChain(&_btreeSchema) << endl;
				if ( traceLow(iv) == DOWN )
				{
				    // cout << "Trace down" << endl;
				    u = pos;
				    pos=d;
				}
				else
				{
				    // cout << "Trace up" << endl;
				    d = pos;
				    pos=u;
				}
				// cout << "Leaf range = " <<  d << " - " << u << endl;				
			    }		    
			    
			    // have we already reached end of page, if yes skip this page and get next value with getNext
			    if  ( pos == numEntries )
			    {
				_curLeaf.setPosition(pos - 1);
				return getNext(fl, dp);
			    }
			    else
			    {			    
				_curLeaf.getValue(pos, iv, dp);
				_curLeaf.setPosition(pos  + 1);
			    
				if ( fullMatch(iv) )
				{
				    found = true;
				    goOn = false;
				}
				else
				{
				    return getNext(fl, dp);
				}
			    }
			}
			else
			{   
			    while ( numEntries == 0 && goOn )
			    {				
				PageIdType pageId = _curPage.getNextPageId();

				_pDBMng->bufferUnfix(_curPage, false, _pTabMng->getLockHandler());
				
				if ( pageId )
				{
				    _pDBMng->bufferFix(_curPage, _tabSetId, pageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());
				    
				    _curLeaf.setPtr(_curPage.getChunkEntry(), _curPage.getChunkLen());
				    _curLeaf.setPageId(_curPage.getPageId());
				    _curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);
				
				    _curLeaf.reset();
				    numEntries = _curLeaf.numEntries();	    
				}
				else
				{
				    goOn = false;
				}
			    }
			}
		    }	    
		}   
	    }
	    break;
	}
	case EQUAL:
	case MORE_THAN:
	case MORE_EQUAL_THAN:
	{	    	    
	    bool leafFound = false;

	    _curPageId = _rootPageId;

	    while ( leafFound == false )
	    {
		CegoBufferPage bp;
		
		_pDBMng->bufferFix(bp, _tabSetId, _curPageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());

		if ( bp.getType() == CegoBufferPage::BTREE_NODE )
		{	    
		    CegoBTreeNode traceNode;
		    traceNode.setPtr(bp.getChunkEntry(), bp.getChunkLen());	    
		    traceNode.setSchema(CegoBTreeNode::NODE, &_btreeSchema, _keyLen);
		    traceNode.setPageId(bp.getPageId());

		    /* The new implementation uses a more efficient trace algorithm to find the
		       appropriate subnode. The variables lb and rb mark the current boundaries,
		       where the mean value is calculated and checked
		    */
			
		    unsigned lb = 0;
		    unsigned rb = traceNode.numEntries();
		    unsigned pos;
		    
		    bool goOn = true;
		    
		    while ( goOn )
		    {	
			pos = traceNode.getMedPage(lb, rb, _curPageId);		
			
			if ( rb - lb <= 1 )
			{
			    if ( lb != rb )
			    {				
				// pos is already set up for left side
				
				CegoBTreeValue ivLeft;
				if ( traceNode.rightValue(pos, ivLeft) )
				{		      	    
				    Direction d = traceLow(ivLeft);

				    /*
				    
				    if ( d == DOWN )
					cout << "Final Tracing low with " << ivLeft.toChain(&_btreeSchema) << endl;
				    else
					cout << "Final Tracing up with " << ivLeft.toChain(&_btreeSchema) << endl;
				    
				    */
				    
				    if ( d == DOWN ) 
				    {
					// _curPageId is already setup
					// _curPageId = leftPageId;
				    }				    
				    else
				    {
					// retrieving right pageid		
					PageIdType rightPageId;
					pos = traceNode.getMedPage(rb, rb, rightPageId);
					_curPageId = rightPageId;
				    }				    
				}
			    }			    
			    goOn = false;		    
			}
			else
			{			    
			    CegoBTreeValue ivRight;
			    
			    if ( traceNode.rightValue(pos, ivRight) )
			    {
				Direction d = traceLow(ivRight);

				/*
				if ( d == DOWN )
				    cout << "Tracing low with " << ivRight.toChain(&_btreeSchema) << endl;
				else
				    cout << "Tracing up with " << ivRight.toChain(&_btreeSchema) << endl;
				*/				
				
				if ( d == DOWN )
				{	  
				    rb = pos;
				}
				else if ( d == UP )
				{
				    lb = pos;
				}
			    }
			    else
			    {
				// right end reached, pageId points to next node or leaf
				goOn = false;
			    }			    
			}
		    }	    
		    _parentPageStack.Push(bp);
		}
		else if ( bp.getType() == CegoBufferPage::BTREE_LEAF )
		{				    
		    _curPage = bp;
		    _curLeaf.setPtr(_curPage.getChunkEntry(), _curPage.getChunkLen());
		    _curLeaf.setPageId(_curPage.getPageId());
		    _curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);

		    leafFound=true;
		    
		    _curLeaf.reset();

		    bool goOn = true;
		    while ( goOn )
		    {			
			CegoBTreeValue iv;
		        unsigned numEntries = _curLeaf.numEntries();

			// cout << "Starting leaf trace .." << endl;
			
			if ( numEntries > 0 )
			{
			    // cout << "Detected " << numEntries << " entries" << endl; 
			    unsigned u = numEntries;
			    unsigned d = 0;

			    unsigned pos = 0;
			    
			    while ( u > d + 1 )
			    {
				pos = ( u + d ) / 2;
				_curLeaf.getValue(pos, iv, dp);

				// cout << "Leaf trace value on position " << pos << " is  " << iv.toChain(&_btreeSchema) << endl;
				if ( traceLow(iv) == DOWN )
				{
				    // cout << "Trace down" << endl;
				    u = pos;
				    pos = d;
				}
				else
				{
				    // cout << "Trace up" << endl;
				    d = pos;
				    pos = u;
				}
				// cout << "Leaf range = " <<  d << " - " << u << endl;				
			    }

			    // have we already reached end of page, if yes skip this page and get next value with getNext
			    if  ( pos == numEntries )
			    {
				_curLeaf.setPosition(pos - 1);
				return getNext(fl, dp);
			    }
			    else
			    {	    			    
				_curLeaf.getValue(pos, iv, dp);
				_curLeaf.setPosition(pos + 1);
				
				if ( fullMatch(iv) )
				{
				    goOn = false;
				    found = true;
				}
				else
				{
				    return getNext(fl, dp);
				}
			    }
			}
			else
			{
			    while ( numEntries == 0  && goOn )
			    {								
				PageIdType pageId = _curPage.getNextPageId();
				
				// cout << "Skipping leave, going to next " << pageId << endl;
				
				_pDBMng->bufferUnfix(_curPage, false, _pTabMng->getLockHandler());
			
				if ( pageId )
				{
				    _pDBMng->bufferFix(_curPage, _tabSetId, pageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());
				    
				    _curLeaf.setPtr(_curPage.getChunkEntry(), _curPage.getChunkLen());
				    _curLeaf.setPageId(_curPage.getPageId());
				    _curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);
				    
				    _curLeaf.reset();
				    numEntries = _curLeaf.numEntries();
				}
				else
				{
				    goOn = false;
				}
			    }
			}
		    }
		}
	    }
	    break;	    
	}
	}
    }
    else
    {
	// go to the beginning
	
	bool leafFound = false;
	_curPageId = _rootPageId;
		
	while ( leafFound == false )
	{	    
	    CegoBufferPage bp;
	   	    
	    _pDBMng->bufferFix(bp, _tabSetId, _curPageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());
	    
	    if ( bp.getType() == CegoBufferPage::BTREE_NODE )
	    {
		CegoBTreeNode traceNode;
		traceNode.setPtr(bp.getChunkEntry(), bp.getChunkLen());	    
		traceNode.setSchema(CegoBTreeNode::NODE, &_btreeSchema, _keyLen);
		traceNode.setPageId(bp.getPageId());
				    	
		traceNode.reset();
		traceNode.nextChildPointer(_curPageId);

		_parentPageStack.Push(bp);
	    }
	    else if ( bp.getType() == CegoBufferPage::BTREE_LEAF )
	    {	
		_curPage = bp;
		_curLeaf.setPtr(bp.getChunkEntry(), bp.getChunkLen());
		_curLeaf.setPageId(bp.getPageId());
		_curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);

		CegoBTreeValue iv;
		
		_curLeaf.reset();
		if ( _curLeaf.nextValue(iv, dp) )
		    found=true;
		else		
		    return getNext(fl, dp);
		
		leafFound=true;
	    }	   	    
	}
    }

    if ( found )
    {	
	char* p;
	unsigned len;

	// optimized way, we could save some unfix/fix operations
	_pTabMng->releaseAndClaimDataPtrUnlocked(_dataPtr, false, _tabSetId, CegoBufferPool::SYNC, dp, p, len, _dataPtr);
	
	// skipping tid
	
	unsigned long long tid;
	unsigned long long tastep;
	CegoTupleState ts;

	unsigned toff = _qh.decodeTupleHeader(tid, tastep, ts, p);

	char* tp = p + toff;
	unsigned tlen = len - toff;

	if ( tid != 0 )
	{

	    /* for temp debugging */
	    /*
	    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tuple check => ts = ") + Chain(ts)
			 + Chain(" tid : ") + Chain(tid) + Chain(" = ") + Chain(_pTabMng->getTID(_tabSetId))
			 + Chain(" tastep : ") + Chain(tastep) + Chain(" < ") + Chain(_pTabMng->getTAStep(_tabSetId)));
	    
	    */
	    
	    if ( _ignoreTouched )
	    {
		if ( ts == CegoTupleState::INSERTED 
		     && tid == _pTabMng->getTID(_tabSetId) 
		     && tastep < _pTabMng->getTAStep(_tabSetId) )
		{
		    _qh.decodeFVL(fl, tp, tlen);
		    return true;   
		}
		else
		{
		    return getNext(fl,dp);
		}
	    }
	    else
	    {		
		if ( ( _readUncommitted == true 
		       && ts == CegoTupleState::INSERTED )
		     || ( _readUncommitted == false 
			  && (( ts == CegoTupleState::INSERTED && tid == _pTabMng->getTID(_tabSetId)) 
			      || ( ts == CegoTupleState::DELETED && tid != _pTabMng->getTID(_tabSetId)))))
		{
		    _qh.decodeFVL(fl, tp, tlen);
		    return true;   
		}
		else
		{
		    return getNext(fl,dp);		    
		}
	    }
	}
	else
	{
	    _qh.decodeFVL(fl, tp, tlen);	   	    
	    return true;
	}
    }
    else
    {
	_eoc=true;
    }   
    return false;
}

bool CegoBTreeCursor::getNext(ListT<CegoField>& fl, CegoDataPointer& dp)
{	
    if (_pAttrCond && _btwCompAdjusted )
    {
	// adjust comparison fpr between conditions
	CegoAttrComp* pAC = _pAttrCond->getAttrCompSet().First();
	while ( pAC )
	{
	    if ( pAC->getCompMode() == CegoAttrComp::BTWN )
		pAC->setComparison( LESS_EQUAL_THAN );	    
	    pAC = _pAttrCond->getAttrCompSet().Next();
	}
	_btwCompAdjusted=false;
    }
		    
    bool endReached = false;
   
    do
    {	
	if ( _eoc )
	{	
	    return false;
	}
	if ( fl.isEmpty() )
	{
	    throw Exception(EXLOC, "Empty field list");
	}
	
	CegoBTreeValue iv;
	
	bool moreValue = _curLeaf.nextValue(iv, dp);
	
	while ( moreValue == false )
	{
	    PageIdType pageId = _curPage.getNextPageId();
	    
	    _pDBMng->bufferUnfix(_curPage, false, _pTabMng->getLockHandler());
	    
	    if ( pageId )
	    {
		_pDBMng->bufferFix(_curPage, _tabSetId, pageId, CegoBufferPool::SYNC, _pTabMng->getLockHandler());
		
		_curLeaf.setPtr(_curPage.getChunkEntry(), _curPage.getChunkLen());
		_curLeaf.setPageId(_curPage.getPageId());
		_curLeaf.setSchema(CegoBTreeNode::LEAF, &_btreeSchema, _keyLen);
	        
		_curLeaf.reset();
		moreValue = _curLeaf.nextValue(iv, dp);
	    }
	    else
	    {
		_eoc = true;		
		return false;	    
	    }	    
	}

	bool attrMatch = false;
	
	// get next available entry from leaf page

	if (_pAttrCond)
	{   
	    // cout << "Check range for " << iv.toChain(&_btreeSchema) << endl;
	    if ( inRange(iv) )
	    {
		if ( fullMatch(iv) )
		{
		    attrMatch=true;
		}
	    }
	    else
	    {
		endReached=true;
	    }	   
	}
	else
	{
	    attrMatch = true;
	}
	
	if ( attrMatch )
	{   
	    char* p;
	    unsigned len;

	    // cout << "Releasing pageid " << _dataPtr.getPageId() << " and claiming pageid " << dp.getPageId() << endl;
	    
	    // _pTabMng->releaseDataPtrUnlocked(_dataPtr, false);	    	   
	    // _pTabMng->claimDataPtrUnlocked(_tabSetId, CegoBufferPool::SYNC, dp, p, len, _dataPtr);

	    _pTabMng->releaseAndClaimDataPtrUnlocked(_dataPtr, false, _tabSetId, CegoBufferPool::SYNC, dp, p, len, _dataPtr);
	    
	    // checking transaction tag
	    
	    unsigned long long tid;
	    unsigned long long tastep;
	    CegoTupleState ts;

	    unsigned toff = _qh.decodeTupleHeader(tid, tastep, ts, p);

	    char* tp = p + toff;
	    unsigned tlen = len - toff;

	    if ( tid != 0 )
	    {		    
		if ( _ignoreTouched )
		{
		    /* for temp debugging */

		    /*
		    _pDBMng->log(_modId, Logger::NOTICE, Chain("Tuple check => ts = ") + Chain(ts)
				 + Chain(" tid : ") + Chain(tid) + Chain(" = ") + Chain(_pTabMng->getTID(_tabSetId))
				 + Chain(" tastep : ") + Chain(tastep) + Chain(" < ") + Chain(_pTabMng->getTAStep(_tabSetId)));

		    */

		    if ( ts == CegoTupleState::INSERTED 
			 && tid == _pTabMng->getTID(_tabSetId) 
			 && tastep < _pTabMng->getTAStep(_tabSetId) )
		    {			
			_qh.decodeFVL(fl, tp, tlen);
			return true;   
		    }
		}
		else
		{
		    if ( ( _readUncommitted == true 
			   && ts == CegoTupleState::INSERTED )
			 || ( _readUncommitted == false 
			      && (( ts == CegoTupleState::INSERTED && tid == _pTabMng->getTID(_tabSetId)) 
				  || ( ts == CegoTupleState::DELETED && tid != _pTabMng->getTID(_tabSetId)))))
		    {
			_qh.decodeFVL(fl, tp, tlen);		
			return true;
		    }
		}
	    }
	    else
	    {
		// cout << "Decoding size " << fl.Size() << endl;
		_qh.decodeFVL(fl, tp, tlen);	
		return true;
	    }
	}
	
	if ( endReached )  
	{
	    abort();
	    _eoc = true;
	    return false;
	}
    }  while (1);
}

void CegoBTreeCursor::abort()
{
    _pTabMng->releaseDataPtrUnlocked(_dataPtr, false);
    _dataPtr = CegoBufferPage();
	
    if ( _curPage.isFixed() )
	_pDBMng->bufferUnfix(_curPage, false, _pTabMng->getLockHandler());	

    // unfix node pages on stack
    CegoBufferPage bp;
    while ( _parentPageStack.Pop(bp) )
    {
	// cout << "Unfixing parent page " << bp.getFileId() << " " << bp.getPageId() << endl;
	_pDBMng->bufferUnfix(bp, false, _pTabMng->getLockHandler());
    }

    if ( _lockId )
    {
	_pTabMng->getLockHandler()->unlockData(CegoObject::BTREE, _lockId);
	_lockId = 0;
    }
}

void CegoBTreeCursor::reset()
{
    abort();
}

void CegoBTreeCursor::prepareComp()
{    
    CegoField* pF = _btreeSchema.First();

    unsigned i=0;
    while ( pF ) 
    {
	CegoAttrComp* pAC = _pAttrCond->getAttrCompSet().First();

	while ( pAC )
	{
	    if  ( (Chain)pAC->getAttrName() == (Chain)pF->getAttrName()  )
	    {
		unsigned pos = CegoBTreeValue::getKeyPos(pF->getAttrName(), _btreeSchema);

		if ( i >= TABMNG_MAXBTREECOMP )
		    throw Exception(EXLOC, "BTree compare array exceeded");

		_compInfo[i].pAttrComp=pAC;
		_compInfo[i].compType=pF->getType();
		_compInfo[i].compLen=pF->getLength();		
		_compInfo[i].compPos=pos;
		i++;
	    }
	    pAC = _pAttrCond->getAttrCompSet().Next();
	}
	pF = _btreeSchema.Next();
    }
    _numComp=i;
}

CegoBTreeCursor::Direction CegoBTreeCursor::traceLow(const CegoBTreeValue& val)
{    
    unsigned i=0;
    unsigned pos=0;
    while ( i < _numComp )
    {	
	CegoFieldValue fv;
	
	// nullIndicator is at compPos - 1
	char* valPtr = (char*)((long long)val.getPtr() + (long long)_compInfo[i].compPos - 1);
       	    
	if ( *valPtr == 1 )
	{
	    valPtr++;
	}
	else
	{
	    valPtr=0;
	}
	
	CegoFieldValue::Comparison comp = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
								   _compInfo[i].pAttrComp->getFieldValue().getType(),
								   _compInfo[i].pAttrComp->getFieldValue().getValue(),
								   _compInfo[i].pAttrComp->getFieldValue().getLength());

	if ( comp == CegoFieldValue::LESS )
	{
	    if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		 || _compInfo[i].pAttrComp->getComparison() == LESS_EQUAL_THAN )
	    {
		return DOWN;
	    }
	    else if ( _compInfo[i].pAttrComp->getComparison() == EQUAL )
	    {
		return UP;
	    }
	    else if ( _compInfo[i].pAttrComp->getComparison() == MORE_THAN
		      || _compInfo[i].pAttrComp->getComparison() == MORE_EQUAL_THAN )
	    {
		return UP; 
	    }
	}
	else if ( comp == CegoFieldValue::MORE )
	{
	    return DOWN;
	}
	
	pos++;
	i++;
	
        // search for next appropriate attribute on next position
	while ( i < _numComp && _compInfo[i].pAttrComp->getPos() != pos )
	    i++;	   
    }
    return CegoBTreeCursor::DOWN;
}

bool CegoBTreeCursor::inRange(const CegoBTreeValue& val)
{    
    unsigned i=0;
    unsigned pos=0;
    while ( i < _numComp )
    {	
	CegoFieldValue fv;
	
	// nullIndicator is at compPos - 1
	char* valPtr = (char*)((long long)val.getPtr() + (long long)_compInfo[i].compPos - 1);

	if ( *valPtr == 1 )
	{
	    valPtr++;	    
	}
	else
	{
	    valPtr = 0;
	}

	CegoFieldValue::Comparison comp = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
								   _compInfo[i].pAttrComp->getFieldValue().getType(),
								   _compInfo[i].pAttrComp->getFieldValue().getValue(),
								   _compInfo[i].pAttrComp->getFieldValue().getLength());
		
	if ( _compInfo[i].pAttrComp->getCompMode() != CegoAttrComp::BTWN )
	{
	    if ( comp == CegoFieldValue::LESS )
	    {		
		return true;
	    }
	    if ( comp == CegoFieldValue::MORE )
	    {		
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == LESS_EQUAL_THAN )
		{
		    return false;
		}
		else if ( _compInfo[i].pAttrComp->getComparison() == EQUAL )
		{
		    return false;
		}
		else
		{
		    return true;
		}				
	    }
	    else // equal
	    {
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN )
		{
		    return false;
		}	    
	    }
	}
	else
	{	    
	    CegoFieldValue::Comparison comp2 = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
								       _compInfo[i].pAttrComp->getFieldValue2().getType(),
								       _compInfo[i].pAttrComp->getFieldValue2().getValue(),
								       _compInfo[i].pAttrComp->getFieldValue2().getLength());
       	
	    if ( comp2 == CegoFieldValue::MORE )
	    {
		return false;
	    }
	    else if ( comp2 == CegoFieldValue::LESS )
	    {
		return true;
	    }
	}
	    
	pos++;
	i++;
	
        // search for next appropriate attribute on next position
	while ( i < _numComp && _compInfo[i].pAttrComp->getPos() != pos )
	    i++;	   
    }
    return true;
}

bool CegoBTreeCursor::fullMatch(const CegoBTreeValue& val)
{    
    for ( unsigned i=0; i < _numComp; i++ ) 
    {
	// Chain v = _compInfo[i].pAttrComp->getFieldValue().toChain();
	// _pDBMng->log(_modId, Logger::NOTICE, Chain("compare to ") + v);
	
	// nullIndicator is at compPos - 1
	char* valPtr = (char*)((long long)val.getPtr() + (long long)_compInfo[i].compPos - 1);
	
	if ( *valPtr == 1 )
	{
	    valPtr++;
	}
	else
	{
	    valPtr=0;
	}
	
	CegoFieldValue::Comparison comp = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
								   _compInfo[i].pAttrComp->getFieldValue().getType(),
								   _compInfo[i].pAttrComp->getFieldValue().getValue(),
								   _compInfo[i].pAttrComp->getFieldValue().getLength());

	
	if ( _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::VAL
	     || _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::ATTR )
	{
	    if ( comp == CegoFieldValue::EQUAL )
	    {
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == MORE_THAN 
		     || _compInfo[i].pAttrComp->getComparison() == NOT_EQUAL )
		    return false;
	    }
	    if ( comp == CegoFieldValue::LESS )
	    {		
		if ( _compInfo[i].pAttrComp->getComparison() == EQUAL
		     || _compInfo[i].pAttrComp->getComparison() == MORE_THAN
		     || _compInfo[i].pAttrComp->getComparison() == MORE_EQUAL_THAN )
		{
		    return false;
		}	
	    }
	    if ( comp == CegoFieldValue::MORE )
	    {
		if ( _compInfo[i].pAttrComp->getComparison() == EQUAL
		     || _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == LESS_EQUAL_THAN )
		    return false;		
	    }	    
	}
	else if ( _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::BTWN )
	{
	    CegoFieldValue::Comparison comp2 = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
									_compInfo[i].pAttrComp->getFieldValue2().getType(),
									_compInfo[i].pAttrComp->getFieldValue2().getValue(),
									_compInfo[i].pAttrComp->getFieldValue2().getLength());
	    	    
	    if ( comp == CegoFieldValue::LESS || comp2 == CegoFieldValue::MORE )
		return false;	   
	}	
    }
    return true;
}

/* checkRangeAndMatch was introduced to save some value compare operations
   The idea is just to evaluate inRange and doMatch values in one loop
   In fact, no significat performance improvements has been observed
*/ 
   
void CegoBTreeCursor::checkRangeAndMatch(const CegoBTreeValue& val, bool& inRange, bool& doMatch)
{
    bool rangeSetup=false;
    bool matchSetup=false;
    inRange=true;
    doMatch=true;
    
    unsigned i=0;

    while ( i < _numComp && ( rangeSetup == false || matchSetup == false ) )
    {	
	// nullIndicator is at compPos - 1
	char* valPtr = (char*)((long long)val.getPtr() + (long long)_compInfo[i].compPos - 1);
	
	if ( *valPtr == 1 )
	{
	    valPtr++;
	}
	else
	{
	    valPtr=0;
	}
	
	CegoFieldValue::Comparison comp = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
								   _compInfo[i].pAttrComp->getFieldValue().getType(),
								   _compInfo[i].pAttrComp->getFieldValue().getValue(),
								   _compInfo[i].pAttrComp->getFieldValue().getLength());

	if ( _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::VAL
	     || _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::ATTR )
	{
	    if ( comp == CegoFieldValue::EQUAL )
	    {
		// range condition
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN )
		{
		    if ( rangeSetup == false )
		    {
			inRange=false;
			rangeSetup=true;
		    }
		}	    

		// match condition
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == MORE_THAN 
		     || _compInfo[i].pAttrComp->getComparison() == NOT_EQUAL )
		{
		    if ( matchSetup == false )
		    {
			doMatch=false;
			matchSetup=true;
		    }
		    // return false;
		}
	    }
	    if ( comp == CegoFieldValue::LESS )
	    {
		// range condition
	        if ( rangeSetup == false )
		{
		    inRange=true;
		    rangeSetup=true;
		}
		
		// match condition
		if ( _compInfo[i].pAttrComp->getComparison() == EQUAL
		     || _compInfo[i].pAttrComp->getComparison() == MORE_THAN
		     || _compInfo[i].pAttrComp->getComparison() == MORE_EQUAL_THAN )
		{
		    if ( matchSetup == false )
		    {
			doMatch=false;
			matchSetup=true;
		    }
		}	
	    }
	    if ( comp == CegoFieldValue::MORE )
	    {
		// range condition
		if ( _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == LESS_EQUAL_THAN 
		     || _compInfo[i].pAttrComp->getComparison() == EQUAL )
		{
		    if ( rangeSetup == false )
		    {
			inRange=false;
			rangeSetup=true;
			// return false;
		    }
		}
		else
		{
		    if ( rangeSetup == false )
		    {
			inRange=true;
			rangeSetup=true;
		    }
		}
		
		// match condition
		if ( _compInfo[i].pAttrComp->getComparison() == EQUAL
		     || _compInfo[i].pAttrComp->getComparison() == LESS_THAN
		     || _compInfo[i].pAttrComp->getComparison() == LESS_EQUAL_THAN )
		{
		    if ( matchSetup == false )
		    {
			doMatch=false;
			matchSetup=true;
		    }
		}
	    }	    
	}
	else if ( _compInfo[i].pAttrComp->getCompMode() == CegoAttrComp::BTWN )
	{
	    CegoFieldValue::Comparison comp2 = CegoFieldValue::fastComp(_compInfo[i].compType, valPtr, _compInfo[i].compLen,
									_compInfo[i].pAttrComp->getFieldValue2().getType(),
									_compInfo[i].pAttrComp->getFieldValue2().getValue(),
									_compInfo[i].pAttrComp->getFieldValue2().getLength());

	    // range condition
	    if ( comp2 == CegoFieldValue::MORE )
	    {		
		if ( rangeSetup == false )
		{
		    inRange=false;
		    rangeSetup=true;
		}		
		// return false;
	    }
	    else if ( comp2 == CegoFieldValue::LESS )
	    {
		if ( rangeSetup == false )
		{
		    inRange=true;
		    rangeSetup=true;
		}		
		// return true;
	    }

	    // match condition
	    if ( comp == CegoFieldValue::LESS || comp2 == CegoFieldValue::MORE )
	    {
		if ( matchSetup == false )
		{
		    doMatch=false;
		    matchSetup=true;
		}	
		// return false;
	    }
	}
	i++;
    }
}
