/*
 * SQL MIX (Memory Index) Database Driver
 *
 * Copyright 2007 Mindaugas Kavaliauskas <dbtopas at dbtopas.lt>
 *
 * 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, 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; see the file LICENSE.txt.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbapi.h"
#include "hbinit.h"
#include "hbapiitm.h"
#include "hbapirdd.h"
#include "hbapierr.h"
#include "hbdbferr.h"
#include "hbapilng.h"
#include "hbdate.h"
#include "hbset.h"
#include "hbvm.h"
#include "rddsys.ch"

#include "hbrddsql.h"

#define SUPERTABLE  ( &sqlmixSuper )


#define MIX_KEY( tag, node, index ) \
   ( ( PMIXKEY ) &( ( HB_BYTE * ) ( node ) )[ ( ( node )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( index ) * ( tag )->uiTotalLen ] )

#define MIX_COPY_KEYS_INTERNAL( tag, node, dst, src, count ) \
   memmove( ( ( HB_BYTE * ) ( node ) ) + ( ( node )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( dst ) * ( tag )->uiTotalLen, \
            ( ( HB_BYTE * ) ( node ) ) + ( ( node )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( src ) * ( tag )->uiTotalLen, \
            ( count ) * ( tag )->uiTotalLen )

#define MIX_COPY_KEYS_EXTERNAL( tag, ndst, dst, nsrc, src, count ) \
   memmove( ( ( HB_BYTE * ) ( ndst ) ) + ( ( ndst )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( dst ) * ( tag )->uiTotalLen, \
            ( ( HB_BYTE * ) ( nsrc ) ) + ( ( nsrc )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( src ) * ( tag )->uiTotalLen, \
            ( count ) * ( tag )->uiTotalLen )

#define MIX_ASSIGN_KEY( tag, node, dst, src ) \
   memmove( ( ( HB_BYTE * ) ( node ) ) + ( ( node )->Leaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + ( dst ) * ( tag )->uiTotalLen, \
            ( src ), ( tag )->uiTotalLen )

#define MIX_COPY_CHILDS_INTERNAL( tag, node, dst, src, count ) \
   memmove( &( ( node )->Child[ ( dst ) ] ), &( ( node )->Child[ ( src ) ] ), ( count ) * sizeof( void * ) )

#define MIX_COPY_CHILDS_EXTERNAL( tag, ndst, dst, nsrc, src, count ) \
   memmove( &( ( ndst )->Child[ ( dst ) ] ), &( ( nsrc )->Child[ ( src ) ] ), ( count ) * sizeof( void * ) )


static HB_USHORT s_uiRddIdSQLMIX = ( HB_USHORT ) -1;

static RDDFUNCS sqlmixSuper;


/* --- Misc functions --- */


static HB_ERRCODE sqlmixErrorRT( SQLMIXAREAP pArea, HB_ERRCODE errGenCode, HB_ERRCODE errSubCode, char * filename, HB_ERRCODE errOsCode, HB_USHORT uiFlags )
{
   HB_ERRCODE iRet = HB_FAILURE;

   if( hb_vmRequestQuery() == 0 )
   {
      PHB_ITEM pError = hb_errNew();
      hb_errPutGenCode( pError, errGenCode );
      hb_errPutSubCode( pError, errSubCode );
      hb_errPutOsCode( pError, errOsCode );
      hb_errPutDescription( pError, hb_langDGetErrorDesc( errGenCode ) );
      if( filename )
         hb_errPutFileName( pError, filename );
      if( uiFlags )
         hb_errPutFlags( pError, uiFlags );
      iRet = SELF_ERROR( &pArea->sqlarea.area, pError );
      hb_errRelease( pError );
   }
   return iRet;
}


/* --- Memory Index --- */

/* --- Key management --- */

/* hb_mixKey*() */

static PMIXKEY hb_mixKeyNew( PMIXTAG pTag )
{
   return ( PMIXKEY ) hb_xgrab( pTag->uiTotalLen );
}


static PMIXKEY hb_mixKeyPutItem( PMIXKEY pKey, PHB_ITEM pItem, HB_ULONG ulRecNo, PMIXTAG pTag )
{
   double  dbl;
   HB_BYTE buf[ 8 ];

   if( ! pKey )
      pKey = hb_mixKeyNew( pTag );

   pKey->rec    = ulRecNo;
   pKey->notnul = 1;

   /* TODO: check valtype */
   switch( pTag->bType )
   {
      case 'C':
      {
         HB_SIZE nLen = hb_itemGetCLen( pItem );

         if( nLen > ( HB_SIZE ) pTag->uiKeyLen )
            nLen = pTag->uiKeyLen;

         memcpy( pKey->val, hb_itemGetCPtr( pItem ), nLen );

         if( nLen < ( HB_SIZE ) pTag->uiKeyLen )
            memset( pKey->val + nLen, ' ', ( HB_SIZE ) pTag->uiKeyLen - nLen );

         break;
      }
      case 'N':
         dbl = hb_itemGetND( pItem );
         HB_DBL2ORD( &dbl, buf );
         memcpy( pKey->val, buf, 8 );
         break;

      case 'D':
         dbl = ( double ) hb_itemGetDL( pItem );
         HB_DBL2ORD( &dbl, buf );
         memcpy( pKey->val, buf, 8 );
         break;

      case 'L':
         pKey->val[ 0 ] = ( HB_BYTE ) ( hb_itemGetL( pItem ) ? 'T' : 'F' );
         break;

      default:
         pKey->notnul = 0;
         memset( pKey->val, ' ', pTag->uiKeyLen );
   }
   return pKey;
}


static PMIXKEY hb_mixKeyEval( PMIXKEY pKey, PMIXTAG pTag )
{
   PHB_ITEM     pItem;
   SQLMIXAREAP  pArea     = pTag->pArea;
   int          iCurrArea = hb_rddGetCurrentWorkAreaNumber();
   PHB_CODEPAGE pCodepage = hb_cdpSelect( pArea->sqlarea.area.cdPage );

   if( iCurrArea != pArea->sqlarea.area.uiArea )
      hb_rddSelectWorkAreaNumber( pArea->sqlarea.area.uiArea );
   else
      iCurrArea = 0;

   pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem );

   pKey = hb_mixKeyPutItem( pKey, pItem, pArea->sqlarea.ulRecNo, pTag );

   if( iCurrArea )
      hb_rddSelectWorkAreaNumber( iCurrArea );

   hb_cdpSelect( pCodepage );

   return pKey;
}


static HB_BOOL hb_mixEvalCond( SQLMIXAREAP pArea, PHB_ITEM pCondItem )
{
   int     iCurrArea = 0;
   HB_BOOL fRet;

   if( pArea )
   {
      iCurrArea = hb_rddGetCurrentWorkAreaNumber();

      if( iCurrArea != pArea->sqlarea.area.uiArea )
         hb_rddSelectWorkAreaNumber( pArea->sqlarea.area.uiArea );
      else
         iCurrArea = 0;
   }

   fRet = hb_itemGetL( hb_vmEvalBlockOrMacro( pCondItem ) );

   if( iCurrArea )
      hb_rddSelectWorkAreaNumber( iCurrArea );

   return fRet;
}


static void hb_mixKeyFree( PMIXKEY pKey )
{
   hb_xfree( pKey );
}


static int hb_mixKeyCompare( PMIXTAG pTag, PMIXKEY pKey1, PMIXKEY pKey2, unsigned int uiLen )
{
   unsigned int uiSize;
   int          i;

   if( ! pKey1->notnul || ! pKey2->notnul )
      return ( int ) pKey1->notnul - ( int ) pKey2->notnul;

   i      = 0;
   uiSize = pTag->uiKeyLen > uiLen ? uiLen : pTag->uiKeyLen;

   if( pTag->pCodepage )
      i = hb_cdpcmp( ( const char * ) pKey1->val, ( HB_SIZE ) uiSize, ( const char * ) pKey2->val, ( HB_SIZE ) uiSize, pTag->pCodepage, 0 );
   else if( uiSize > 0 )
      i = memcmp( pKey1->val, pKey2->val, uiSize );

   if( i == 0 )
   {
      if( pKey2->rec == ( HB_ULONG ) -1 )
      {
         /* This condition seems inverted, but it's ok for seek last */
         if( pTag->uiKeyLen > uiLen )
            i = -1;
      }
      else
      {
         if( pTag->uiKeyLen > uiLen )
            i = 1;
         else if( pTag->uiKeyLen < uiLen )
            i = -1;
      }
   }


   if( i != 0 )
   {
      if( i < 0 )
         return -2;

      return 2;
   }

   if( pKey1->rec < pKey2->rec )
      return -1;
   else if( pKey1->rec > pKey2->rec )
      return 1;

   return 0;
}


/* --- Tag management --- */

/* hb_mixTag*() */

/* This function is used for debugging purposes. Uncomment it, if you need it. */
#if 0
static void hb_mixTagPrintNode( PMIXTAG pTag, PMIXNODE pNode, int iLevel )
{
   unsigned int i;

   if( ! pNode )
      return;

   if( pNode->KeyCount < MIX_NODE_ORDER / 2 && pNode->Parent )
      printf( "!!! Too few keys\n" );

   for( i = 0; i < pNode->KeyCount; i++ )
   {
      if( ! pNode->Leaf )
      {
         if( pNode->Child[ i ]->Parent != pNode )
            printf( "!!! Invalid parent\n" );

         hb_mixTagPrintNode( pTag, pNode->Child[ i ], iLevel + 1 );
      }
      printf( "%*ld %*s\n", iLevel * 10 + 5, MIX_KEY( pTag, pNode, i )->rec, pTag->uiKeyLen,
              MIX_KEY( pTag, pNode, i )->notnul ? ( char * ) MIX_KEY( pTag, pNode, i )->val : "NULL" );
   }

   if( ! pNode->Leaf )
   {
      if( pNode->Child[ pNode->KeyCount ]->Parent != pNode )
         printf( "!!! Invalid parent\n" );

      hb_mixTagPrintNode( pTag, pNode->Child[ pNode->KeyCount ], iLevel + 1 );
   }
}
#endif

static PMIXNODE hb_mixTagCreateNode( PMIXTAG pTag, HB_BOOL fLeaf )
{
   HB_SIZE  nSize = ( fLeaf ? sizeof( MIXNODELEAF ) : sizeof( MIXNODE ) ) + MIX_NODE_ORDER * pTag->uiTotalLen;
   PMIXNODE pNode = ( PMIXNODE ) hb_xgrabz( nSize );

   pNode->Leaf = fLeaf ? 1 : 0;

   return pNode;
}


static unsigned int hb_mixTagNodeParentIndex( PMIXNODE pNode )
{
   PMIXNODE     pParent = pNode->Parent;
   unsigned int ui;

   /* Find position in the parent node */
   ui = pParent->KeyCount;
   do
   {
      if( pParent->Child[ ui ] == pNode )
         return ui;
   }
   while( ui-- );

   return ( unsigned int ) -1;
}


static int hb_mixTagFindKey( PMIXTAG pTag, PMIXKEY pKey, unsigned int uiLen, PMIXNODE * ppNode, unsigned int * puiPos, HB_BOOL fValidKey )
{
   PMIXNODE     pNode;
   unsigned int ui;
   int          i;

   pNode = pTag->Root;

   for( ;; )
   {
      i = -2;

      /* TODO: binary search */
      for( ui = 0; ui < pNode->KeyCount; ui++ )
      {
         i = hb_mixKeyCompare( pTag, MIX_KEY( pTag, pNode, ui ), pKey, uiLen );

         if( i >= 0 )
            break;
      }

      if( i == 0 || pNode->Leaf )
         break;
      else
         pNode = pNode->Child[ ui ];
   }

   if( fValidKey && ui >= pNode->KeyCount )
   {
      /* unsuccessful find always finds position in leaf */

      while( pNode->Parent && pNode->Parent->Child[ pNode->Parent->KeyCount ] == pNode )
         pNode = pNode->Parent;

      if( pNode->Parent )
      {
         for( ui = 0; ui < pNode->Parent->KeyCount; ui++ )
         {
            if( pNode->Parent->Child[ ui ] == pNode )
            {
               pNode = pNode->Parent;
               break;
            }
         }
      }
      else
         ui = pNode->KeyCount + 1;   /* EOF */
   }

   *ppNode = pNode;
   *puiPos = ui;
   return i;
}


static void hb_mixTagSetCurrent( PMIXTAG pTag, PMIXNODE pNode, unsigned int uiPos )
{
   if( uiPos < pNode->KeyCount )
   {
      pTag->CurNode = pNode;
      pTag->CurPos  = uiPos;
      pTag->CurKey  = MIX_KEY( pTag, pNode, uiPos );
      pTag->fEof    = HB_FALSE;
   }
   else
      pTag->fEof = HB_TRUE;
}


static HB_BOOL hb_mixTagRefreshKey( PMIXTAG pTag )
{
   SQLMIXAREAP pArea;

   pArea = pTag->pArea;

   if( pArea->sqlarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->sqlarea.area );

   if( ! pArea->sqlarea.fPositioned )
   {
      pTag->fEof = HB_TRUE;
      return HB_FALSE;
   }
   else if( pTag->fEof || pTag->CurKey->rec != pArea->sqlarea.ulRecNo )
   {
      PMIXKEY      pKey;
      PMIXNODE     pNode;
      unsigned int ui;

      pKey = hb_mixKeyEval( NULL, pTag );

      hb_mixTagFindKey( pTag, pKey, pTag->uiKeyLen, &pNode, &ui, HB_FALSE );
      hb_mixTagSetCurrent( pTag, pNode, ui );

      hb_mixKeyFree( pKey );
      return ! pTag->fEof && pTag->CurKey->rec == pArea->sqlarea.ulRecNo;
   }
   pTag->fBof = pTag->fEof = HB_FALSE;
   return HB_TRUE;
}


static void hb_mixTagAddKeyNode( PMIXTAG pTag, PMIXNODE pNode, unsigned int uiPos, PMIXKEY pKey, PMIXNODE pChildLeft, PMIXNODE pChildRight )
{
   MIX_COPY_KEYS_INTERNAL( pTag, pNode, uiPos + 1, uiPos, pNode->KeyCount - uiPos );
   if( ! pNode->Leaf )
   {
      MIX_COPY_CHILDS_INTERNAL( pTag, pNode, uiPos + 2, uiPos + 1, pNode->KeyCount - uiPos );
      pNode->Child[ uiPos ]     = pChildLeft;
      pNode->Child[ uiPos + 1 ] = pChildRight;
      pChildLeft->Parent        = pNode;
      pChildRight->Parent       = pNode;
   }
   MIX_ASSIGN_KEY( pTag, pNode, uiPos, pKey );
   pNode->KeyCount++;
}


static void hb_mixTagAddKeyPos( PMIXTAG pTag, PMIXNODE pNode, unsigned int uiPos, PMIXKEY pKey, PMIXNODE pChildLeft, PMIXNODE pChildRight )
{
   PMIXNODE     pNewNode;
   unsigned int j, k;

   if( pNode->KeyCount < MIX_NODE_ORDER )
   {
      hb_mixTagAddKeyNode( pTag, pNode, uiPos, pKey, pChildLeft, pChildRight );
      return;
   }

#ifdef USE_SIBLINGS
   /* Try use siblings, if leaf node is full */

   if( pNode->Leaf && pNode->Parent )
   {
      j = hb_mixTagNodeParentIndex( pNode );

      if( j > 0 && pNode->Parent->Child[ j - 1 ]->KeyCount < MIX_NODE_ORDER )
      {
         MIX_COPY_KEYS_EXTERNAL( pTag, pNode->Parent->Child[ j - 1 ], pNode->Parent->Child[ j - 1 ]->KeyCount, pNode->Parent, j - 1, 1 );
         pNode->Parent->Child[ j - 1 ]->KeyCount++;

         if( uiPos == 0 )
         {
            MIX_ASSIGN_KEY( pTag, pNode->Parent, j - 1, pKey );
            pNode->Parent->Key[ j - 1 ] = pKey;
         }
         else
         {
            MIX_COPY_KEYS_EXTERNAL( pTag, pNode->Parent, j - 1, pNode, 0, 1 );
            uiPos--;
            MIX_COPY_KEYS_INTERNAL( pTag, pNode, 0, 1, uiPos );
            MIX_ASSIGN_KEY( pTag, pNode, uiPos, pKey );
         }

         return;
      }
      else if( j < pNode->Parent->KeyCount && pNode->Parent->Child[ j + 1 ]->KeyCount < MIX_NODE_ORDER )
      {
         MIX_COPY_KEYS_INTERNAL( pTag, pNode->Parent->Child[ j + 1 ], 1, 0, pNode->Parent->Child[ j + 1 ]->KeyCount );
         MIX_COPY_KEYS_EXTERNAL( pTag, pNode->Parent->Child[ j + 1 ], 0, pNode->Parent, j, 1 );
         pNode->Parent->Child[ j + 1 ]->KeyCount++;

         if( uiPos == MIX_NODE_ORDER )
            MIX_ASSIGN_KEY( pTag, pNode->Parent, j, pKey );
         else
         {
            MIX_COPY_KEYS_EXTERNAL( pTag, pNode->Parent, j, pNode, MIX_NODE_ORDER - 1, 1 );
            MIX_COPY_KEYS_INTERNAL( pTag, pNode, uiPos + 1, uiPos, MIX_NODE_ORDER - uiPos - 1 );
            MIX_ASSIGN_KEY( pTag, pNode, uiPos, pKey );
         }
         return;
      }
   }
#endif /* USE_SIBLINGS */


   /* Create new node */
   pNewNode = hb_mixTagCreateNode( pTag, pNode->Leaf );

   /* Move half of items to new node */
   k = MIX_NODE_ORDER / 2 + ( ( uiPos <= MIX_NODE_ORDER / 2 ) ? 0 : 1 );
   MIX_COPY_KEYS_EXTERNAL( pTag, pNewNode, 0, pNode, k, MIX_NODE_ORDER - k );
   if( ! pNode->Leaf )
   {
      MIX_COPY_CHILDS_EXTERNAL( pTag, pNewNode, 1, pNode, k + 1, MIX_NODE_ORDER - k );
      for( j = 1; j <= MIX_NODE_ORDER - k; j++ )
         pNewNode->Child[ j ]->Parent = pNewNode;  /* Do NOT forget to re-parent */
   }
   pNode->KeyCount    = k;
   pNewNode->KeyCount = MIX_NODE_ORDER - k;


   /* Insert new item to the left node or right node */
   if( uiPos <= MIX_NODE_ORDER / 2 )
      hb_mixTagAddKeyNode( pTag, pNode, uiPos, pKey, pChildLeft, pChildRight );
   else
      hb_mixTagAddKeyNode( pTag, pNewNode, uiPos - MIX_NODE_ORDER / 2 - 1, pKey, pChildLeft, pChildRight );


   /* Assign the leftmost child of the new node */
   if( ! pNode->Leaf )
   {
      pNewNode->Child[ 0 ]         = pNode->Child[ pNode->KeyCount ];
      pNewNode->Child[ 0 ]->Parent = pNewNode;
   }

   pNode->KeyCount--;

   /* Move middle (last+1 in first node) item up */
   if( pNode->Parent )
      hb_mixTagAddKeyPos( pTag, pNode->Parent, hb_mixTagNodeParentIndex( pNode ),
                          MIX_KEY( pTag, pNode, pNode->KeyCount ), pNode, pNewNode );
   else
   {
      pTag->Root = hb_mixTagCreateNode( pTag, 0 );
      hb_mixTagAddKeyNode( pTag, pTag->Root, 0, MIX_KEY( pTag, pNode, pNode->KeyCount ), pNode, pNewNode );
   }

}


static HB_BOOL hb_mixTagAddKey( PMIXTAG pTag, PMIXKEY pKey )
{
   PMIXNODE     pNode;
   unsigned int ui;
   int          i;

   i = hb_mixTagFindKey( pTag, pKey, pTag->uiKeyLen, &pNode, &ui, HB_FALSE );

   /* Key can not be duplicated */
   if( ! i )
      return HB_FALSE;

   hb_mixTagAddKeyPos( pTag, pNode, ui, pKey, NULL, NULL );
   return HB_TRUE;
}


static void hb_mixTagDelKeyNode( PMIXTAG pTag, PMIXNODE pNode, unsigned int uiPos )
{
   MIX_COPY_KEYS_INTERNAL( pTag, pNode, uiPos, uiPos + 1, pNode->KeyCount - uiPos - 1 );
   if( ! pNode->Leaf )
   {
      MIX_COPY_CHILDS_INTERNAL( pTag, pNode, uiPos, uiPos + 1, pNode->KeyCount - uiPos );
   }
   pNode->KeyCount--;
}


static void hb_mixTagNodeAdjust( PMIXTAG pTag, PMIXNODE pNode )
{
   unsigned int i, j;
   PMIXNODE     pParent, pSibling;

   for( ;; )
   {
      if( pNode->KeyCount >= MIX_NODE_ORDER / 2 )
         return;

      /* Check siblings */

      if( pNode->Parent )
      {
         pParent = pNode->Parent;
         j       = hb_mixTagNodeParentIndex( pNode );

         if( j > 0 && pParent->Child[ j - 1 ]->KeyCount > MIX_NODE_ORDER / 2 )
         {
            /* Borrow from left */

            pSibling = pParent->Child[ j - 1 ];

            /* It could not be pNode->Child[ 0 ] if it is not Leaf!!! */
            hb_mixTagAddKeyNode( pTag, pNode, 0, MIX_KEY( pTag, pParent, j - 1 ), pSibling->Child[ pSibling->KeyCount ], pNode->Child[ 0 ] );
            MIX_COPY_KEYS_EXTERNAL( pTag, pParent, j - 1, pSibling, pSibling->KeyCount - 1, 1 );
            pSibling->KeyCount--;
            return;
         }
         else if( j < pParent->KeyCount && pParent->Child[ j + 1 ]->KeyCount > MIX_NODE_ORDER / 2 )
         {
            /* Borrow from right */

            pSibling = pParent->Child[ j + 1 ];
            hb_mixTagAddKeyNode( pTag, pNode, pNode->KeyCount, MIX_KEY( pTag, pParent, j ), pNode->Child[ pNode->KeyCount ], pSibling->Child[ 0 ] );
            MIX_COPY_KEYS_EXTERNAL( pTag, pParent, j, pSibling, 0, 1 );
            hb_mixTagDelKeyNode( pTag, pSibling, 0 );
            return;
         }
         else if( j > 0 )
         {
            /* Join with left */

            pSibling = pParent->Child[ j - 1 ];
            MIX_COPY_KEYS_EXTERNAL( pTag, pSibling, pSibling->KeyCount, pParent, j - 1, 1 );
            pSibling->KeyCount++;
            MIX_COPY_KEYS_EXTERNAL( pTag, pSibling, pSibling->KeyCount, pNode, 0, pNode->KeyCount );

            if( pNode->Leaf )
               pSibling->KeyCount += pNode->KeyCount;
            else
            {
               MIX_COPY_CHILDS_EXTERNAL( pTag, pSibling, pSibling->KeyCount, pNode, 0, pNode->KeyCount );
               for( i = 0; i < pNode->KeyCount; i++ )
                  pSibling->Child[ pSibling->KeyCount++ ]->Parent = pSibling;

               pSibling->Child[ pSibling->KeyCount ]         = pNode->Child[ i ];
               pSibling->Child[ pSibling->KeyCount ]->Parent = pSibling;
            }
            hb_xfree( pNode );
            pParent->Child[ j ] = pSibling;
            hb_mixTagDelKeyNode( pTag, pParent, j - 1 );
            pNode = pParent;
         }
         else if( j < pParent->KeyCount )
         {
            /* Join with right */

            pSibling = pParent->Child[ j + 1 ];
            MIX_COPY_KEYS_EXTERNAL( pTag, pNode, pNode->KeyCount, pParent, j, 1 );
            pNode->KeyCount++;
            MIX_COPY_KEYS_EXTERNAL( pTag, pNode, pNode->KeyCount, pSibling, 0, pSibling->KeyCount );
            if( pNode->Leaf )
               pNode->KeyCount += pSibling->KeyCount;
            else
            {
               MIX_COPY_CHILDS_EXTERNAL( pTag, pNode, pNode->KeyCount, pSibling, 0, pSibling->KeyCount );
               for( i = 0; i < pSibling->KeyCount; i++ )
                  pNode->Child[ pNode->KeyCount++ ]->Parent = pNode;

               pNode->Child[ pNode->KeyCount ]         = pSibling->Child[ i ];
               pNode->Child[ pNode->KeyCount ]->Parent = pNode;
            }
            hb_xfree( pSibling );
            pParent->Child[ j + 1 ] = pNode;
            hb_mixTagDelKeyNode( pTag, pParent, j );
            pNode = pParent;
         }
      }
      else
      {
         /* Adjust root */

         if( ! pNode->KeyCount && ! pNode->Leaf )
         {
            pTag->Root         = pNode->Child[ 0 ];
            pTag->Root->Parent = NULL;
            hb_xfree( pNode );
         }
         return;
      }
   }
}


static void hb_mixTagDelKeyPos( PMIXTAG pTag, PMIXNODE pNode, unsigned int uiPos )
{
   if( pNode->Leaf )
   {
      hb_mixTagDelKeyNode( pTag, pNode, uiPos );
      hb_mixTagNodeAdjust( pTag, pNode );
   }
   else
   {
      PMIXNODE pLeaf;

      pLeaf = pNode->Child[ uiPos + 1 ];
      while( ! pLeaf->Leaf )
         pLeaf = pLeaf->Child[ 0 ];

      MIX_COPY_KEYS_EXTERNAL( pTag, pNode, uiPos, pLeaf, 0, 1 );
      hb_mixTagDelKeyNode( pTag, pLeaf, 0 );
      hb_mixTagNodeAdjust( pTag, pLeaf );
   }
}


static HB_BOOL hb_mixTagDelKey( PMIXTAG pTag, PMIXKEY pKey )
{
   PMIXNODE     pNode;
   unsigned int ui;
   int          i;

   i = hb_mixTagFindKey( pTag, pKey, pTag->uiKeyLen, &pNode, &ui, HB_FALSE );

   if( i )
      return HB_FALSE;

   hb_mixTagDelKeyPos( pTag, pNode, ui );
   return HB_TRUE;
}


static PMIXTAG hb_mixTagCreate( const char * szTagName, PHB_ITEM pKeyExpr, PHB_ITEM pKeyItem, PHB_ITEM pForItem, PHB_ITEM pWhileItem, HB_BYTE bType, unsigned int uiKeyLen, SQLMIXAREAP pArea )
{
   PMIXTAG pTag;
   PMIXKEY pKey = NULL;
   LPDBORDERCONDINFO pOrdCondInfo = pArea->sqlarea.area.lpdbOrdCondInfo;
   HB_ULONG          ulStartRec, ulNextCount = 0;
   HB_LONG  lStep = 0;
   PHB_ITEM pItem, pEvalItem = NULL;

   pTag = ( PMIXTAG ) hb_xgrabz( sizeof( MIXTAG ) );

   pTag->pArea = pArea;

   pTag->szName = ( char * ) hb_xgrab( MIX_MAXTAGNAMELEN + 1 );
   hb_strncpyUpperTrim( pTag->szName, szTagName, MIX_MAXTAGNAMELEN );

   pTag->szKeyExpr = ( char * ) hb_xgrab( hb_itemGetCLen( pKeyExpr ) + 1 );
   hb_strncpyTrim( pTag->szKeyExpr, hb_itemGetCPtr( pKeyExpr ), hb_itemGetCLen( pKeyExpr ) );

   /* TODO: FOR expression */
   pTag->szForExpr = NULL;

   pTag->pKeyItem = pKeyItem;
   pTag->pForItem = pForItem;
   pTag->bType    = bType;
   pTag->uiKeyLen = uiKeyLen;

   pTag->uiTotalLen = sizeof( MIXKEY ) + pTag->uiKeyLen;

   /* Use national support */
   if( bType == 'C' )
   {
      if( pArea->sqlarea.area.cdPage &&
          ! HB_CDP_ISBINSORT( pArea->sqlarea.area.cdPage ) )
         pTag->pCodepage = pArea->sqlarea.area.cdPage;
   }

   pTag->Root = hb_mixTagCreateNode( pTag, HB_TRUE );

   ulStartRec = 0;

   if( pOrdCondInfo )
   {
      pEvalItem = pOrdCondInfo->itmCobEval;
      lStep     = pOrdCondInfo->lStep;
   }

   if( ! pOrdCondInfo || pOrdCondInfo->fAll )
      pArea->pTag = NULL;
   else
   {
      if( pOrdCondInfo->itmRecID )
         ulStartRec = hb_itemGetNL( pOrdCondInfo->itmRecID );

      if( ulStartRec )
         ulNextCount = 1;
      else if( pOrdCondInfo->fRest || pOrdCondInfo->lNextCount > 0 )
      {
         if( pOrdCondInfo->itmStartRecID )
            ulStartRec = hb_itemGetNL( pOrdCondInfo->itmStartRecID );

         if( ! ulStartRec )
            ulStartRec = pArea->sqlarea.ulRecNo;

         if( pArea->sqlarea.area.lpdbOrdCondInfo->lNextCount > 0 )
            ulNextCount = pOrdCondInfo->lNextCount;
      }
      else if( ! pOrdCondInfo->fUseCurrent )
         pArea->pTag = NULL;
   }

   if( ulStartRec == 0 && pArea->pTag == NULL )
      ulStartRec = 1;

   if( ulStartRec )
      SELF_GOTO( &pArea->sqlarea.area, ulStartRec );
   else
      SELF_GOTOP( &pArea->sqlarea.area );

   while( ! pArea->sqlarea.area.fEof )
   {
      if( pEvalItem )
      {
         if( lStep >= pOrdCondInfo->lStep )
         {
            lStep = 0;
            if( ! hb_mixEvalCond( NULL, pEvalItem ) )
               break;
         }
         ++lStep;
      }

      if( pWhileItem && ! hb_mixEvalCond( NULL, pWhileItem ) )
         break;

      if( pForItem == NULL || hb_mixEvalCond( NULL, pForItem ) )
      {
         pItem = hb_vmEvalBlockOrMacro( pKeyItem );

         pKey = hb_mixKeyPutItem( pKey, pItem, pArea->sqlarea.ulRecNo, pTag );
         hb_mixTagAddKey( pTag, pKey );
      }

      if( ulNextCount )
      {
         ulNextCount--;
         if( ! ulNextCount )
            break;
      }
      if( SELF_SKIPRAW( &pArea->sqlarea.area, 1 ) == HB_FAILURE )
         break;
   }
   if( pKey )
      hb_mixKeyFree( pKey );

   return pTag;
}


static void hb_mixTagDestroyNode( PMIXNODE pNode )
{
   if( ! pNode->Leaf )
   {
      unsigned int ui;

      for( ui = 0; ui <= pNode->KeyCount; ui++ )
         hb_mixTagDestroyNode( pNode->Child[ ui ] );
   }
   hb_xfree( pNode );
}


static void hb_mixTagDestroy( PMIXTAG pTag )
{
   if( pTag->szName )
      hb_xfree( pTag->szName );

   if( pTag->szKeyExpr )
      hb_xfree( pTag->szKeyExpr );

   if( pTag->szForExpr )
      hb_xfree( pTag->szForExpr );

   if( pTag->pKeyItem )
      hb_vmDestroyBlockOrMacro( pTag->pKeyItem );

   if( pTag->pForItem )
      hb_vmDestroyBlockOrMacro( pTag->pForItem );

   if( pTag->Root )
      hb_mixTagDestroyNode( pTag->Root );

   if( pTag->HotKey )
      hb_mixKeyFree( pTag->HotKey );

   hb_xfree( pTag );
}


static void hb_mixTagGoTop( PMIXTAG pTag )
{
   PMIXNODE pNode;

   pNode = pTag->Root;
   while( ! pNode->Leaf )
      pNode = pNode->Child[ 0 ];

   if( ! pNode->KeyCount )
   {
      pTag->fEof = HB_TRUE;
      return;
   }
   pTag->fEof    = HB_FALSE;
   pTag->CurNode = pNode;
   pTag->CurPos  = 0;
   pTag->CurKey  = MIX_KEY( pTag, pTag->CurNode, 0 );
}


static void hb_mixTagGoBottom( PMIXTAG pTag )
{
   PMIXNODE pNode;

   pNode = pTag->Root;
   while( ! pNode->Leaf )
      pNode = pNode->Child[ pNode->KeyCount ];

   if( ! pNode->KeyCount )
   {
      pTag->fEof = HB_TRUE;
      return;
   }
   pTag->fEof    = HB_FALSE;
   pTag->CurNode = pNode;
   pTag->CurPos  = pNode->KeyCount - 1;
   pTag->CurKey  = MIX_KEY( pTag, pTag->CurNode, pTag->CurPos );
}


static void hb_mixTagSkip( PMIXTAG pTag, HB_LONG lSkip )
{
   PMIXNODE     pNode, pNode2;
   unsigned int uiPos, uiPos2;

   pNode = pTag->CurNode;
   uiPos = pTag->CurPos;

   #if 0
   printf( "hb_mixTagSkip: CurNode=%p, CurPos=%d lSkip=%d\n", pNode, uiPos, lSkip );
   #endif

   if( lSkip > 0 )
   {
      pTag->fBof = HB_FALSE;
      while( ! pTag->fEof && lSkip > 0 )
      {
         if( pNode->Leaf )
         {
            if( ( HB_LONG ) ( pNode->KeyCount - 1 - uiPos ) >= lSkip )
            {
               uiPos += lSkip;
               lSkip  = 0;
            }
            else if( pNode->KeyCount - 1 > uiPos )
            {
               lSkip -= ( HB_LONG ) ( pNode->KeyCount - 1 - uiPos );
               uiPos  = pNode->KeyCount - 1;
            }
            if( lSkip )
            {
               do
               {
                  if( pNode->Parent )
                     uiPos = hb_mixTagNodeParentIndex( pNode );
                  pNode = pNode->Parent;
               }
               while( pNode && uiPos == pNode->KeyCount );

               if( pNode )
                  lSkip--;
               else
                  pTag->fEof = HB_TRUE;
            }
         }
         else
         {
            pNode = pNode->Child[ uiPos + 1 ];
            while( ! pNode->Leaf )
               pNode = pNode->Child[ 0 ];

            uiPos = 0;
            lSkip--;
         }
      }
   }
   else if( lSkip < 0 )
   {
      lSkip = -lSkip;

      /* This is not needed. skip(-1) from Eof is processed inside sqlmixSkipRaw */
#if 0
      if( pTag->fEof )
      {
         hb_mixTagGoBottom( pTag );
         lSkip--;
         pTag->fBof = pTag->fEof;
      }
#endif
      pTag->fBof = pTag->fEof;

      while( ! pTag->fBof && lSkip > 0 )
      {
         if( pNode->Leaf )
         {
            if( ( HB_LONG ) uiPos >= lSkip )
            {
               uiPos -= lSkip;
               lSkip  = 0;
            }
            else if( uiPos )
            {
               lSkip -= uiPos;
               uiPos  = 0;
            }
            if( lSkip )
            {
               pNode2 = pNode;
               uiPos2 = uiPos;
               do
               {
                  if( pNode->Parent )
                     uiPos = hb_mixTagNodeParentIndex( pNode );
                  pNode = pNode->Parent;
               }
               while( pNode && uiPos == 0 );

               if( pNode )
               {
                  uiPos--;
                  lSkip--;
               }
               else
               {
                  pNode      = pNode2;
                  uiPos      = uiPos2;
                  pTag->fBof = HB_TRUE;
               }
            }
         }
         else
         {
            do
            {
               pNode = pNode->Child[ uiPos ];
               uiPos = pNode->KeyCount;
            }
            while( ! pNode->Leaf );
            uiPos--;
            lSkip--;
         }
      }
   }
   if( ! pTag->fEof )
   {
      pTag->CurNode = pNode;
      pTag->CurPos  = uiPos;
      pTag->CurKey  = MIX_KEY( pTag, pNode, uiPos );
   }
}

/* --- Misc functions --- */

/* hb_mix*() */

static PMIXTAG hb_mixFindTag( SQLMIXAREAP pArea, PHB_ITEM pOrder )
{
   PMIXTAG pTag;

   if( HB_IS_NUMBER( pOrder ) )
   {
      int iOrder, iCurr = 0;

      iOrder = hb_itemGetNI( pOrder );

      pTag = pArea->pTagList;
      while( pTag && iOrder != ++iCurr )
         pTag = pTag->pNext;
   }
   else
   {
      char szTag[ MIX_MAXTAGNAMELEN + 1 ];

      hb_strncpyUpperTrim( szTag, hb_itemGetCPtr( pOrder ), MIX_MAXTAGNAMELEN );
      pTag = pArea->pTagList;
      while( pTag && hb_stricmp( szTag, pTag->szName ) )
         pTag = pTag->pNext;
   }
   return pTag;
}


/* --- */


static HB_ULONG hb_mixTagNodeKeyCount( PMIXNODE pNode )
{
   HB_ULONG ulKeyCount = pNode->KeyCount;

   if( ! pNode->Leaf )
   {
      unsigned int ui;
      for( ui = 0; ui <= pNode->KeyCount; ui++ )
         ulKeyCount += hb_mixTagNodeKeyCount( pNode->Child[ ui ] );
   }

   return ulKeyCount;
}


static HB_BOOL hb_mixCheckRecordFilter( SQLMIXAREAP pArea, HB_ULONG ulRecNo )
{
   HB_BOOL lResult = HB_FALSE;

   if( pArea->sqlarea.area.dbfi.itmCobExpr || hb_setGetDeleted() )
   {
      if( pArea->sqlarea.ulRecNo != ulRecNo || pArea->sqlarea.lpdbPendingRel )
         SELF_GOTO( &pArea->sqlarea.area, ulRecNo );

      if( hb_setGetDeleted() )
         SUPER_DELETED( &pArea->sqlarea.area, &lResult );

      if( ! lResult && pArea->sqlarea.area.dbfi.itmCobExpr )
      {
         PHB_ITEM pResult = hb_vmEvalBlock( pArea->sqlarea.area.dbfi.itmCobExpr );
         lResult = HB_IS_LOGICAL( pResult ) && ! hb_itemGetL( pResult );
      }
   }
   return ! lResult;
}


static HB_ULONG hb_mixDBOIKeyCount( PMIXTAG pTag, HB_BOOL fFilter )
{
   HB_ULONG ulKeyCount;

   if( ! pTag )
      return 0;

   if( fFilter && pTag->pArea->sqlarea.area.dbfi.fFilter )
   {
      PMIXNODE     pNode   = pTag->CurNode;
      unsigned int uiPos   = pTag->CurPos;
      HB_ULONG     ulRecNo = pTag->pArea->sqlarea.ulRecNo;

      ulKeyCount = 0;

      hb_mixTagGoTop( pTag );
      while( ! pTag->fEof )
      {
         if( hb_mixCheckRecordFilter( pTag->pArea, pTag->CurKey->rec ) )
            ulKeyCount++;
         hb_mixTagSkip( pTag, 1 );
      }
      hb_mixTagSetCurrent( pTag, pNode, uiPos );
      SELF_GOTO( &pTag->pArea->sqlarea.area, ulRecNo );

   }
   else
      ulKeyCount = hb_mixTagNodeKeyCount( pTag->Root );

   return ulKeyCount;
}


static HB_ULONG hb_mixDBOIKeyNo( PMIXTAG pTag, HB_BOOL fFilter )
{
   HB_ULONG ulKeyCount;

   if( ! pTag )
      return 0;

   if( fFilter )
      ulKeyCount = 0;
   else
   {
      PMIXNODE     pNode = pTag->CurNode;
      unsigned int ui, uiPos = pTag->CurPos;

      ulKeyCount = 1;

      while( pNode )
      {
         ulKeyCount += uiPos;
         if( ! pNode->Leaf )
         {
            for( ui = 0; ui < uiPos; ui++ )
               ulKeyCount += hb_mixTagNodeKeyCount( pNode->Child[ ui ] );
         }
         pNode = pNode->Parent;
         if( pNode )
            uiPos = hb_mixTagNodeParentIndex( pNode );
      }
   }
   return ulKeyCount;
}


/* --- SQLMIX RDD METHODS --- */

static HB_ERRCODE sqlmixGoBottom( SQLMIXAREAP pArea )
{
   HB_ERRCODE retval;

   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->pTag )
      return SUPER_GOBOTTOM( &pArea->sqlarea.area );

   if( pArea->sqlarea.lpdbPendingRel && pArea->sqlarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->sqlarea.area );

   hb_mixTagGoBottom( pArea->pTag );

   pArea->sqlarea.area.fTop    = HB_FALSE;
   pArea->sqlarea.area.fBottom = HB_TRUE;

   retval = SELF_GOTO( &pArea->sqlarea.area, pArea->pTag->CurKey ? pArea->pTag->CurKey->rec : 0 );
   if( retval != HB_FAILURE && pArea->sqlarea.fPositioned )
      retval = SELF_SKIPFILTER( &pArea->sqlarea.area, -1 );

   return retval;
}


static HB_ERRCODE sqlmixGoTop( SQLMIXAREAP pArea )
{
   HB_ERRCODE retval;

   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->pTag )
      return SUPER_GOTOP( &pArea->sqlarea.area );

   if( pArea->sqlarea.lpdbPendingRel && pArea->sqlarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->sqlarea.area );

   hb_mixTagGoTop( pArea->pTag );

   pArea->sqlarea.area.fTop    = HB_TRUE;
   pArea->sqlarea.area.fBottom = HB_FALSE;

   retval = SELF_GOTO( &pArea->sqlarea.area, pArea->pTag->CurKey ? pArea->pTag->CurKey->rec : 0 );
   if( retval != HB_FAILURE && pArea->sqlarea.fPositioned )
      retval = SELF_SKIPFILTER( &pArea->sqlarea.area, 1 );

   return retval;
}


static HB_ERRCODE sqlmixSeek( SQLMIXAREAP pArea, HB_BOOL fSoftSeek, PHB_ITEM pItem, HB_BOOL fFindLast )
{
   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->pTag )
   {
      sqlmixErrorRT( pArea, EG_NOORDER, 1201, NULL, 0, EF_CANDEFAULT );
      return HB_FAILURE;
   }
   else
   {
      PMIXKEY      pKey;
      HB_ERRCODE   errCode = HB_SUCCESS;
      HB_BOOL      fEOF;
      PMIXTAG      pTag = pArea->pTag;
      PMIXNODE     pNode;
      unsigned int uiKeyLen, ui;

      if( pArea->sqlarea.lpdbPendingRel && pArea->sqlarea.lpdbPendingRel->isScoped )
         SELF_FORCEREL( &pArea->sqlarea.area );

      pArea->sqlarea.area.fTop = pArea->sqlarea.area.fBottom = HB_FALSE;
      pArea->sqlarea.area.fEof = HB_FALSE;

      pKey = hb_mixKeyPutItem( NULL, pItem, fFindLast ? ( HB_ULONG ) -1 : 0, pTag );

      uiKeyLen = pTag->uiKeyLen;
      if( pTag->bType == 'C' )
      {
         uiKeyLen = ( unsigned int ) hb_itemGetCLen( pItem );
         if( uiKeyLen > pTag->uiKeyLen )
            uiKeyLen = pTag->uiKeyLen;
      }

      hb_mixTagFindKey( pTag, pKey, uiKeyLen, &pNode, &ui, HB_TRUE );
      hb_mixTagSetCurrent( pTag, pNode, ui );

      if( fFindLast )
      {
         if( pTag->fEof )
            hb_mixTagGoBottom( pTag );
         else
            hb_mixTagSkip( pTag, -1 );

         pArea->sqlarea.area.fFound = ! pTag->fEof && ( uiKeyLen == 0 || memcmp( pTag->CurKey->val, pKey->val, ( HB_ULONG ) uiKeyLen ) == 0 );

         if( ! pArea->sqlarea.area.fFound )
            hb_mixTagSetCurrent( pTag, pNode, ui );
      }
      else
         pArea->sqlarea.area.fFound = ! pTag->fEof && ( uiKeyLen == 0 || memcmp( pTag->CurKey->val, pKey->val, ( HB_ULONG ) uiKeyLen ) == 0 );

      fEOF = pTag->fEof;

      if( ! fEOF )
      {
         errCode = SELF_GOTO( &pArea->sqlarea.area, pTag->CurKey->rec );
         if( errCode != HB_FAILURE && pArea->sqlarea.fPositioned )
         {
            errCode = SELF_SKIPFILTER( &pArea->sqlarea.area, fFindLast ? -1 : 1 );
            if( errCode != HB_FAILURE && pArea->sqlarea.fPositioned )
            {
               pArea->sqlarea.area.fFound = ( uiKeyLen == 0 || memcmp( pTag->CurKey->val, pKey->val, ( HB_ULONG ) uiKeyLen ) == 0 );
               if( ! pArea->sqlarea.area.fFound && ! fSoftSeek )
                  fEOF = HB_TRUE;
            }
         }
      }

      if( errCode != HB_FAILURE && fEOF )
         errCode = SELF_GOTO( &pArea->sqlarea.area, 0 );

      pArea->sqlarea.area.fBof = HB_FALSE;

      hb_mixKeyFree( pKey );
      return errCode;
   }
}


static HB_ERRCODE sqlmixSkipRaw( SQLMIXAREAP pArea, HB_LONG lToSkip )
{
   PMIXTAG pTag = pArea->pTag;
   HB_BOOL fOut = HB_FALSE;

   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pTag || lToSkip == 0 )
      return SUPER_SKIPRAW( &pArea->sqlarea.area, lToSkip );

   if( pArea->sqlarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->sqlarea.area );

   if( ! hb_mixTagRefreshKey( pTag ) )
   {
      if( lToSkip > 0 || pArea->sqlarea.fPositioned )
         fOut = HB_TRUE;
      else
      {
         hb_mixTagGoBottom( pTag );
         fOut = pTag->fEof;
         lToSkip++;
      }
   }

   if( ! fOut )
      hb_mixTagSkip( pTag, lToSkip );

   if( SELF_GOTO( &pArea->sqlarea.area, ( pTag->fEof || fOut ) ? 0 : pTag->CurKey->rec ) != HB_SUCCESS )
      return HB_FAILURE;
   pArea->sqlarea.area.fEof = pTag->fEof;
   pArea->sqlarea.area.fBof = pTag->fBof;
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixGoCold( SQLMIXAREAP pArea )
{
   HB_BOOL fRecordChanged = pArea->sqlarea.fRecordChanged;
   HB_BOOL fAppend        = pArea->sqlarea.fAppend;

   if( SUPER_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( fRecordChanged && pArea->pTagList )
   {
      PMIXTAG     pTag;
      HB_BOOL     fAdd, fDel;
      LPDBRELINFO lpdbPendingRel;

      lpdbPendingRel = pArea->sqlarea.lpdbPendingRel;
      pArea->sqlarea.lpdbPendingRel = NULL;

      pTag = pArea->pTagList;
      while( pTag )
      {
         if( ! pTag->fCustom )
         {
            PMIXKEY pKey = hb_mixKeyEval( NULL, pTag );

            if( pTag->pForItem != NULL )
               fAdd = hb_mixEvalCond( pArea, pTag->pForItem );
            else
               fAdd = HB_TRUE;

            if( fAppend )
               fDel = HB_FALSE;
            else
            {
               if( hb_mixKeyCompare( pTag, pKey, pTag->HotKey, pTag->uiKeyLen ) == 0 )
               {
                  fDel = ! fAdd && pTag->HotFor;
                  fAdd = fAdd && ! pTag->HotFor;
               }
               else
                  fDel = pTag->HotFor;
            }

            if( fDel )
               hb_mixTagDelKey( pTag, pTag->HotKey );

            if( fAdd )
               hb_mixTagAddKey( pTag, pKey );

            hb_mixKeyFree( pKey );
         }
         pTag = pTag->pNext;
      }
      pArea->sqlarea.lpdbPendingRel = lpdbPendingRel;
   }

   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixGoHot( SQLMIXAREAP pArea )
{
   PMIXTAG pTag;

#if 0
   if( pArea->fRecordChanged )
      printf( "sqlmixGoHot: multiple marking buffer as hot." );
#endif

   if( SUPER_GOHOT( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   pTag = pArea->pTagList;
   while( pTag )
   {
      if( ! pTag->fCustom )
      {
         pTag->HotKey = hb_mixKeyEval( pTag->HotKey, pTag );
         pTag->HotFor = pTag->pForItem == NULL || hb_mixEvalCond( pArea, pTag->pForItem );
      }
      pTag = pTag->pNext;
   }
   return HB_SUCCESS;
}

static HB_ERRCODE sqlmixZap( SQLMIXAREAP pArea )
{
   PMIXTAG pTag;

   if( SUPER_ZAP( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   pTag = pArea->pTagList;

   while( pTag )
   {
      if( pTag->Root )
         hb_mixTagDestroyNode( pTag->Root );

      pTag->Root = hb_mixTagCreateNode( pTag, HB_TRUE );
      pTag->fEof = HB_TRUE;

      pTag = pTag->pNext;
   }

   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixClose( SQLMIXAREAP pArea )
{
   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( SUPER_CLOSE( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( SELF_ORDLSTCLEAR( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixStructSize( SQLMIXAREAP pArea, HB_USHORT * StructSize )
{
   HB_SYMBOL_UNUSED( pArea );

   *StructSize = sizeof( SQLMIXAREA );
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixOrderListClear( SQLMIXAREAP pArea )
{
   PMIXTAG pTag;

   while( pArea->pTagList )
   {
      pTag = pArea->pTagList;
      pArea->pTagList = pTag->pNext;
      hb_mixTagDestroy( pTag );
   }
   pArea->pTag = NULL;
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixOrderListFocus( SQLMIXAREAP pArea, LPDBORDERINFO pOrderInfo )
{
   if( pArea->pTag )
      pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pArea->pTag->szName );

   if( pOrderInfo->itmOrder )
      pArea->pTag = hb_mixFindTag( pArea, pOrderInfo->itmOrder );

   return pArea->pTag ? HB_SUCCESS : HB_FAILURE;
}


static HB_ERRCODE sqlmixOrderCreate( SQLMIXAREAP pArea, LPDBORDERCREATEINFO pOrderInfo )
{
   PMIXTAG   pTagNew, pTag;
   PHB_ITEM  pKeyItem, pForItem = NULL, pWhileItem = NULL, pResult;
   HB_ULONG  ulRecNo;
   HB_USHORT uiLen;
   HB_BYTE   bType;

   /* Obtain key codeblock */
   if( pOrderInfo->itmCobExpr )
      pKeyItem = hb_itemNew( pOrderInfo->itmCobExpr );
   else
   {
      if( SELF_COMPILE( &pArea->sqlarea.area, hb_itemGetCPtr( pOrderInfo->abExpr ) ) == HB_FAILURE )
         return HB_FAILURE;
      pKeyItem = pArea->sqlarea.area.valResult;
      pArea->sqlarea.area.valResult = NULL;
   }

   /* Test key codeblock on EOF */
   ulRecNo = pArea->sqlarea.ulRecNo;
   SELF_GOTO( &pArea->sqlarea.area, 0 );
   if( SELF_EVALBLOCK( &pArea->sqlarea.area, pKeyItem ) == HB_FAILURE )
   {
      hb_vmDestroyBlockOrMacro( pKeyItem );
      SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
      return HB_FAILURE;
   }

   pResult = pArea->sqlarea.area.valResult;
   pArea->sqlarea.area.valResult = NULL;

   switch( hb_itemType( pResult ) )
   {
      case HB_IT_STRING:
      case HB_IT_MEMO:
         bType = 'C';
         uiLen = ( HB_USHORT ) hb_itemGetCLen( pResult );
         if( uiLen > MIX_MAXKEYLEN )
            uiLen = MIX_MAXKEYLEN;
         break;

      case HB_IT_INTEGER:
      case HB_IT_LONG:
      case HB_IT_DOUBLE:
         bType = 'N';
         uiLen = 8;
         break;

      case HB_IT_DATE:
         bType = 'D';
         uiLen = 8;
         break;

      case HB_IT_TIMESTAMP:
         bType = '@';
         uiLen = 26;
         break;

      case HB_IT_LOGICAL:
         bType = 'L';
         uiLen = 1;
         break;

      default:
         bType = 'U';
         uiLen = 0;
   }
   hb_itemRelease( pResult );

   if( bType == 'U' || uiLen == 0 )
   {
      hb_vmDestroyBlockOrMacro( pKeyItem );
      SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
      sqlmixErrorRT( pArea, bType == 'U' ? EG_DATATYPE : EG_DATAWIDTH, 1026, NULL, 0, 0 );
      return HB_FAILURE;
   }

   if( pArea->sqlarea.area.lpdbOrdCondInfo )
   {
      /* Obtain FOR codeblock */
      if( pArea->sqlarea.area.lpdbOrdCondInfo->itmCobFor )
         pForItem = hb_itemNew( pArea->sqlarea.area.lpdbOrdCondInfo->itmCobFor );
      else if( pArea->sqlarea.area.lpdbOrdCondInfo->abFor )
      {
         if( SELF_COMPILE( &pArea->sqlarea.area, pArea->sqlarea.area.lpdbOrdCondInfo->abFor ) == HB_FAILURE )
         {
            hb_vmDestroyBlockOrMacro( pKeyItem );
            SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
            return HB_FAILURE;
         }
         pForItem = pArea->sqlarea.area.valResult;
         pArea->sqlarea.area.valResult = NULL;
      }

      /* Obtain WHILE codeblock */
      if( pArea->sqlarea.area.lpdbOrdCondInfo->itmCobWhile )
         pWhileItem = hb_itemNew( pArea->sqlarea.area.lpdbOrdCondInfo->itmCobWhile );
      else if( pArea->sqlarea.area.lpdbOrdCondInfo->abWhile )
      {
         if( SELF_COMPILE( &pArea->sqlarea.area, pArea->sqlarea.area.lpdbOrdCondInfo->abWhile ) == HB_FAILURE )
         {
            hb_vmDestroyBlockOrMacro( pKeyItem );
            if( pForItem )
               hb_vmDestroyBlockOrMacro( pForItem );
            SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
            return HB_FAILURE;
         }
         pWhileItem = pArea->sqlarea.area.valResult;
         pArea->sqlarea.area.valResult = NULL;
      }
   }

   /* Test FOR codeblock on EOF */
   if( pForItem )
   {
      if( SELF_EVALBLOCK( &pArea->sqlarea.area, pForItem ) == HB_FAILURE )
      {
         hb_vmDestroyBlockOrMacro( pKeyItem );
         hb_vmDestroyBlockOrMacro( pForItem );
         if( pWhileItem )
            hb_vmDestroyBlockOrMacro( pWhileItem );
         SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
         return HB_FAILURE;
      }
      if( ( hb_itemType( pArea->sqlarea.area.valResult ) & HB_IT_LOGICAL ) == 0 )
      {
         hb_itemRelease( pArea->sqlarea.area.valResult );
         pArea->sqlarea.area.valResult = 0;
         hb_vmDestroyBlockOrMacro( pKeyItem );
         hb_vmDestroyBlockOrMacro( pForItem );
         if( pWhileItem )
            hb_vmDestroyBlockOrMacro( pWhileItem );
         SELF_GOTO( &pArea->sqlarea.area, ulRecNo );
         sqlmixErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDFOR, NULL, 0, 0 );
         return HB_FAILURE;
      }
      hb_itemRelease( pArea->sqlarea.area.valResult );
      pArea->sqlarea.area.valResult = 0;
   }

   /* TODO: WHILE condition is not tested, like in DBFCDX. Why? Compatibility with Clipper? */

   SELF_GOTO( &pArea->sqlarea.area, ulRecNo );

   pTagNew = hb_mixTagCreate( pOrderInfo->atomBagName, pOrderInfo->abExpr, pKeyItem, pForItem, pWhileItem, bType, uiLen, pArea );

   if( pWhileItem )
      hb_vmDestroyBlockOrMacro( pWhileItem );

   /* Append the tag to the end of list */
   if( pArea->pTagList )
   {
      pTag = pArea->pTagList;
      while( pTag->pNext )
         pTag = pTag->pNext;

      pTag->pNext = pTagNew;
   }
   else
      pArea->pTagList = pTagNew;

   pArea->pTag = pTagNew;
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixOrderInfo( SQLMIXAREAP pArea, HB_USHORT uiIndex, LPDBORDERINFO pOrderInfo )
{
   PMIXTAG   pTag;
   HB_USHORT uiTag = 0;

   switch( uiIndex )
   {
      case DBOI_EVALSTEP:
         pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult,
                                               pArea->sqlarea.area.lpdbOrdCondInfo ? pArea->sqlarea.area.lpdbOrdCondInfo->lStep : 0 );
         return HB_SUCCESS;

      case DBOI_ORDERCOUNT:
         pTag = pArea->pTagList;
         while( pTag )
         {
            pTag = pTag->pNext;
            uiTag++;
         }
         pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, uiTag );
         return HB_SUCCESS;
   }

   if( SELF_GOCOLD( &pArea->sqlarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( pOrderInfo->itmOrder )
      pTag = hb_mixFindTag( pArea, pOrderInfo->itmOrder );
   else
      pTag = pArea->pTag;

   switch( uiIndex )
   {
      case DBOI_CONDITION:
         pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, ( pTag ? pTag->szForExpr : NULL ) );
         if( pTag && pOrderInfo->itmNewVal && HB_IS_STRING( pOrderInfo->itmNewVal ) )
         {
            if( pTag->szForExpr != NULL )
            {
               hb_xfree( pTag->szForExpr );
               pTag->szForExpr = NULL;
            }
            if( pTag->pForItem != NULL )
            {
               hb_vmDestroyBlockOrMacro( pTag->pForItem );
               pTag->pForItem = NULL;
            }
            if( hb_itemGetCLen( pOrderInfo->itmNewVal ) > 0 )
            {
               const char * pForExpr = hb_itemGetCPtr( pOrderInfo->itmNewVal );

               if( SELF_COMPILE( &pArea->sqlarea.area, pForExpr ) == HB_SUCCESS )
               {
                  PHB_ITEM pForItem = pArea->sqlarea.area.valResult;

                  pArea->sqlarea.area.valResult = NULL;
                  if( SELF_EVALBLOCK( &pArea->sqlarea.area, pForItem ) == HB_SUCCESS )
                  {
                     if( hb_itemType( pArea->sqlarea.area.valResult ) & HB_IT_LOGICAL )
                     {
                        pTag->szForExpr = hb_strdup( pForExpr );
                        pTag->pForItem  = pForItem;
                        pForItem        = NULL;
                     }
                     hb_itemRelease( pArea->sqlarea.area.valResult );
                     pArea->sqlarea.area.valResult = NULL;
                  }
                  if( pForItem )
                     hb_vmDestroyBlockOrMacro( pForItem );
               }
            }
         }
         break;

      case DBOI_EXPRESSION:
         pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag ? pTag->szKeyExpr : NULL );
         break;

      case DBOI_POSITION:
      case DBOI_KEYNORAW:
         if( pOrderInfo->itmNewVal && HB_IS_NUMERIC( pOrderInfo->itmNewVal ) )
         {
            /* TODO */
#if 0
            pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult,
                                                 hb_cdxDBOIKeyGoto( pArea, pTag,
                                                                    hb_itemGetNL( pOrderInfo->itmNewVal ), uiIndex == DBOI_POSITION ) == HB_SUCCESS );
#endif
         }
         else
            pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult,
                                                  hb_mixDBOIKeyNo( pTag, uiIndex == DBOI_POSITION ) );
         break;

      case DBOI_KEYCOUNT:
      case DBOI_KEYCOUNTRAW:
         pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult,
                                               hb_mixDBOIKeyCount( pTag, uiIndex == DBOI_KEYCOUNT ) );
         break;

         /* TODO */
#if 0
      case DBOI_FINDREC:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult,
                                              hb_cdxDBOIFindRec( pArea, pTag,
                                                                 hb_itemGetNL( pOrderInfo->itmNewVal ), HB_FALSE ) );
         break;

      case DBOI_FINDRECCONT:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult,
                                              hb_cdxDBOIFindRec( pArea, pTag,
                                                                 hb_itemGetNL( pOrderInfo->itmNewVal ), HB_TRUE ) );
         break;
#endif
      case DBOI_NAME:
         pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag ? pTag->szName : NULL );
         break;

      case DBOI_NUMBER:
         pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, uiTag );  /* otherwise */
         break;

      case DBOI_ISCOND:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, pTag && pTag->szForExpr != NULL );
         break;

      case DBOI_ISDESC:
      case DBOI_UNIQUE:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
         break;

      case DBOI_SCOPETOP:
      case DBOI_SCOPEBOTTOM:
         if( pOrderInfo->itmResult )
            hb_itemClear( pOrderInfo->itmResult );
         break;

      case DBOI_KEYTYPE:
         if( pTag )
         {
            char szType[ 2 ];

            szType[ 0 ] = ( char ) pTag->bType;
            szType[ 1 ] = 0;
            pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, szType );
         }
         else
            pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, NULL );
         break;

      case DBOI_KEYSIZE:
         pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, pTag ? pTag->uiKeyLen : 0 );
         break;

      case DBOI_KEYDEC:
         pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, 0 );
         break;

         /* TODO */
#if 0
      case DBOI_KEYVAL:
         hb_itemClear( pOrderInfo->itmResult );
         if( pArea->sqlarea.lpdbPendingRel )
            SELF_FORCEREL( &pArea->sqlarea.area );
         if( pTag && pArea->sqlarea.fPositioned )
         {
            if( pTag->CurKey->rec != pArea->sqlarea.ulRecNo )
            {
               hb_cdxIndexLockRead( pTag->pIndex );
               hb_cdxCurKeyRefresh( pArea, pTag );
               hb_cdxIndexUnLockRead( pTag->pIndex );
            }
            if( pTag->CurKey->rec == pArea->sqlarea.ulRecNo )
               pOrderInfo->itmResult = hb_cdxKeyGetItem( pTag->CurKey,
                                                         pOrderInfo->itmResult, pTag, HB_TRUE );
         }
         break;

      case DBOI_CUSTOM:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, ( pTag ? pTag->fCustom : HB_FALSE ) );
         if( pOrderInfo->itmNewVal && HB_IS_LOGICAL( pOrderInfo->itmNewVal )
             && hb_itemGetL( pOrderInfo->itmNewVal ) )
            pTag->fCustom = HB_TRUE;
         break;

      case DBOI_KEYADD:
         if( ! pTag )
            pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
         else
         {
            if( pTag->fCustom )
            {
               if( pArea->sqlarea.lpdbPendingRel )
                  SELF_FORCEREL( &pArea->sqlarea.area );

               if( ! pArea->sqlarea.fPositioned ||
                   ( pTag->pForItem &&
                     ! hb_cdxEvalCond( pArea, pTag->pForItem, HB_TRUE ) ) )
                  pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
               else
               {
                  LPCDXKEY pKey;
                  hb_cdxIndexLockWrite( pTag->pIndex );
                  if( pOrderInfo->itmNewVal && ! HB_IS_NIL( pOrderInfo->itmNewVal ) )
                     pKey = hb_cdxKeyPutItem( NULL, pOrderInfo->itmNewVal, pArea->sqlarea.ulRecNo, pTag, HB_TRUE, HB_TRUE );
                  else
                     pKey = hb_cdxKeyEval( NULL, pTag );
                  pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult,
                                                       hb_cdxTagKeyAdd( pTag, pKey ) );
                  hb_cdxIndexUnLockWrite( pTag->pIndex );
                  hb_cdxKeyFree( pKey );
               }
            }
            else
               sqlmixErrorRT( pArea, 0, 1052, NULL, 0, 0 );
         }
         break;

      case DBOI_KEYDELETE:
         if( ! pTag )
            pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
         else
         {
            if( pTag->Custom )
            {
               if( pArea->sqlarea.lpdbPendingRel )
                  SELF_FORCEREL( &pArea->sqlarea.area );

               if( ! pArea->sqlarea.fPositioned ||
                   ( pTag->pForItem &&
                     ! hb_cdxEvalCond( pArea, pTag->pForItem, HB_TRUE ) ) )
                  pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
               else
               {
                  LPCDXKEY pKey;
                  hb_cdxIndexLockWrite( pTag->pIndex );
                  if( pOrderInfo->itmNewVal && ! HB_IS_NIL( pOrderInfo->itmNewVal ) )
                     pKey = hb_cdxKeyPutItem( NULL, pOrderInfo->itmNewVal, pArea->sqlarea.ulRecNo, pTag, HB_TRUE, HB_TRUE );
                  else
                     pKey = hb_cdxKeyEval( NULL, pTag );
                  pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult,
                                                       hb_cdxTagKeyDel( pTag, pKey ) );
                  hb_cdxIndexUnLockWrite( pTag->pIndex );
                  hb_cdxKeyFree( pKey );
               }
            }
            else
               sqlmixErrorRT( pArea, 0, 1052, NULL, 0, 0 );
         }
         break;
#endif

      case DBOI_SHARED:
      case DBOI_ISREADONLY:
         pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, HB_FALSE );
         break;

      default:
         return SUPER_ORDINFO( &pArea->sqlarea.area, uiIndex, pOrderInfo );

   }
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixInit( LPRDDNODE pRDD )
{
   /* This empty method is used to avoid duplicated sqlbase init call */
   HB_SYMBOL_UNUSED( pRDD );
   return HB_SUCCESS;
}


static HB_ERRCODE sqlmixExit( LPRDDNODE pRDD )
{
   /* This empty method is used to avoid duplicated sqlbase exit call */
   HB_SYMBOL_UNUSED( pRDD );
   return HB_SUCCESS;
}


static RDDFUNCS sqlmixTable =
{ ( DBENTRYP_BP ) NULL,                 /* sqlmixBof */
  ( DBENTRYP_BP ) NULL,                 /* sqlmixEof */
  ( DBENTRYP_BP ) NULL,                 /* sqlmixFound */
  ( DBENTRYP_V ) sqlmixGoBottom,
  ( DBENTRYP_UL ) NULL,                 /* sqlmixGoTo */
  ( DBENTRYP_I ) NULL,                  /* sqlmixGoToId */
  ( DBENTRYP_V ) sqlmixGoTop,
  ( DBENTRYP_BIB ) sqlmixSeek,
  ( DBENTRYP_L ) NULL,                  /* sqlmixSkip */
  ( DBENTRYP_L ) NULL,                  /* sqlmixSkipFilter */
  ( DBENTRYP_L ) sqlmixSkipRaw,
  ( DBENTRYP_VF ) NULL,                 /* sqlmixAddField */
  ( DBENTRYP_B ) NULL,                  /* sqlmixAppend */
  ( DBENTRYP_I ) NULL,                  /* sqlmixCreateFields */
  ( DBENTRYP_V ) NULL,                  /* sqlmixDeleteRec */
  ( DBENTRYP_BP ) NULL,                 /* sqlmixDeleted */
  ( DBENTRYP_SP ) NULL,                 /* sqlmixFieldCount */
  ( DBENTRYP_VF ) NULL,                 /* sqlmixFieldDisplay */
  ( DBENTRYP_SSI ) NULL,                /* sqlmixFieldInfo */
  ( DBENTRYP_SCP ) NULL,                /* sqlmixFieldName */
  ( DBENTRYP_V ) NULL,                  /* sqlmixFlush */
  ( DBENTRYP_PP ) NULL,                 /* sqlmixGetRec */
  ( DBENTRYP_SI ) NULL,                 /* sqlmixGetValue */
  ( DBENTRYP_SVL ) NULL,                /* sqlmixGetVarLen */
  ( DBENTRYP_V ) sqlmixGoCold,
  ( DBENTRYP_V ) sqlmixGoHot,
  ( DBENTRYP_P ) NULL,                  /* sqlmixPutRec */
  ( DBENTRYP_SI ) NULL,                 /* sqlmixPutValue */
  ( DBENTRYP_V ) NULL,                  /* sqlmixRecall */
  ( DBENTRYP_ULP ) NULL,                /* sqlmixRecCount */
  ( DBENTRYP_ISI ) NULL,                /* sqlmixRecInfo */
  ( DBENTRYP_ULP ) NULL,                /* sqlmixRecNo */
  ( DBENTRYP_I ) NULL,                  /* sqlmixRecId */
  ( DBENTRYP_S ) NULL,                  /* sqlmixSetFieldExtent */
  ( DBENTRYP_CP ) NULL,                 /* sqlmixAlias */
  ( DBENTRYP_V ) sqlmixClose,
  ( DBENTRYP_VO ) NULL,                 /* sqlmixCreate */
  ( DBENTRYP_SI ) NULL,                 /* sqlmixInfo */
  ( DBENTRYP_V ) NULL,                  /* sqlmixNewArea */
  ( DBENTRYP_VO ) NULL,                 /* sqlmixOpen */
  ( DBENTRYP_V ) NULL,                  /* sqlmixRelease */
  ( DBENTRYP_SP ) sqlmixStructSize,
  ( DBENTRYP_CP ) NULL,                 /* sqlmixSysName */
  ( DBENTRYP_VEI ) NULL,                /* sqlmixEval */
  ( DBENTRYP_V ) NULL,                  /* sqlmixPack */
  ( DBENTRYP_LSP ) NULL,                /* sqlmixPackRec */
  ( DBENTRYP_VS ) NULL,                 /* sqlmixSort */
  ( DBENTRYP_VT ) NULL,                 /* sqlmixTrans */
  ( DBENTRYP_VT ) NULL,                 /* sqlmixTransRec */
  ( DBENTRYP_V ) sqlmixZap,
  ( DBENTRYP_VR ) NULL,                 /* sqlmixChildEnd */
  ( DBENTRYP_VR ) NULL,                 /* sqlmixChildStart */
  ( DBENTRYP_VR ) NULL,                 /* sqlmixChildSync */
  ( DBENTRYP_V ) NULL,                  /* sqlmixSyncChildren */
  ( DBENTRYP_V ) NULL,                  /* sqlmixClearRel */
  ( DBENTRYP_V ) NULL,                  /* sqlmixForceRel */
  ( DBENTRYP_SSP ) NULL,                /* sqlmixRelArea */
  ( DBENTRYP_VR ) NULL,                 /* sqlmixRelEval */
  ( DBENTRYP_SI ) NULL,                 /* sqlmixRelText */
  ( DBENTRYP_VR ) NULL,                 /* sqlmixSetRel */
  ( DBENTRYP_VOI ) NULL,                /* sqlmixOrderListAdd */
  ( DBENTRYP_V ) sqlmixOrderListClear,
  ( DBENTRYP_VOI ) NULL,                /* sqlmixOrderListDelete */
  ( DBENTRYP_VOI ) sqlmixOrderListFocus,
  ( DBENTRYP_V ) NULL,                  /* sqlmixOrderListRebuild */
  ( DBENTRYP_VOO ) NULL,                /* sqlmixOrderCondition */
  ( DBENTRYP_VOC ) sqlmixOrderCreate,
  ( DBENTRYP_VOI ) NULL,                /* sqlmixOrderDestroy */
  ( DBENTRYP_SVOI ) sqlmixOrderInfo,
  ( DBENTRYP_V ) NULL,                  /* sqlmixClearFilter */
  ( DBENTRYP_V ) NULL,                  /* sqlmixClearLocate */
  ( DBENTRYP_V ) NULL,                  /* sqlmixClearScope */
  ( DBENTRYP_VPLP ) NULL,               /* sqlmixCountScope */
  ( DBENTRYP_I ) NULL,                  /* sqlmixFilterText */
  ( DBENTRYP_SI ) NULL,                 /* sqlmixScopeInfo */
  ( DBENTRYP_VFI ) NULL,                /* sqlmixSetFilter */
  ( DBENTRYP_VLO ) NULL,                /* sqlmixSetLocate */
  ( DBENTRYP_VOS ) NULL,                /* sqlmixSetScope */
  ( DBENTRYP_VPL ) NULL,                /* sqlmixSkipScope */
  ( DBENTRYP_B ) NULL,                  /* sqlmixLocate */
  ( DBENTRYP_CC ) NULL,                 /* sqlmixCompile */
  ( DBENTRYP_I ) NULL,                  /* sqlmixError */
  ( DBENTRYP_I ) NULL,                  /* sqlmixEvalBlock */
  ( DBENTRYP_VSP ) NULL,                /* sqlmixRawLock */
  ( DBENTRYP_VL ) NULL,                 /* sqlmixLock */
  ( DBENTRYP_I ) NULL,                  /* sqlmixUnLock */
  ( DBENTRYP_V ) NULL,                  /* sqlmixCloseMemFile */
  ( DBENTRYP_VO ) NULL,                 /* sqlmixCreateMemFile */
  ( DBENTRYP_SCCS ) NULL,               /* sqlmixGetValueFile */
  ( DBENTRYP_VO ) NULL,                 /* sqlmixOpenMemFile */
  ( DBENTRYP_SCCS ) NULL,               /* sqlmixPutValueFile */
  ( DBENTRYP_V ) NULL,                  /* sqlmixReadDBHeader */
  ( DBENTRYP_V ) NULL,                  /* sqlmixWriteDBHeader */
  ( DBENTRYP_R ) sqlmixInit,
  ( DBENTRYP_R ) sqlmixExit,
  ( DBENTRYP_RVVL ) NULL,               /* sqlmixDrop */
  ( DBENTRYP_RVVL ) NULL,               /* sqlmixExists */
  ( DBENTRYP_RVVVL ) NULL,              /* sqlmixRename */
  ( DBENTRYP_RSLV ) NULL,               /* sqlmixRddInfo */
  ( DBENTRYP_SVP ) NULL,                /* sqlmixWhoCares */
};

/* force SQLBASE linking */
HB_FUNC_TRANSLATE( SQLMIX, SQLBASE )

HB_FUNC_STATIC( SQLMIX_GETFUNCTABLE )
{
   RDDFUNCS *  pTable;
   HB_USHORT * puiCount, * puiSuperRddId, uiRddId;

   puiCount      = ( HB_USHORT * ) hb_parptr( 1 );
   pTable        = ( RDDFUNCS * ) hb_parptr( 2 );
   uiRddId       = ( HB_USHORT ) hb_parni( 4 );
   puiSuperRddId = ( HB_USHORT * ) hb_parptr( 5 );

   if( pTable )
   {
      HB_ERRCODE errCode;

      if( puiCount )
         *puiCount = RDDFUNCSCOUNT;

      errCode = hb_rddInheritEx( pTable, &sqlmixTable, &sqlmixSuper, "SQLBASE", puiSuperRddId );
      if( errCode == HB_SUCCESS )
         s_uiRddIdSQLMIX = uiRddId;
      hb_retni( errCode );
   }
   else
      hb_retni( HB_FAILURE );
}

static void hb_sqlmixRddInit( void * cargo )
{
   HB_SYMBOL_UNUSED( cargo );

   if( hb_rddRegister( "SQLBASE", RDT_FULL ) > 1 ||
       hb_rddRegister( "SQLMIX", RDT_FULL ) > 1 )
      hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL );
}

HB_INIT_SYMBOLS_BEGIN( sqlmix__InitSymbols )
{
   "SQLMIX", { HB_FS_PUBLIC | HB_FS_LOCAL }, { HB_FUNCNAME( SQLMIX ) }, NULL
},
{ "SQLMIX_GETFUNCTABLE", { HB_FS_PUBLIC | HB_FS_LOCAL }, { HB_FUNCNAME( SQLMIX_GETFUNCTABLE ) }, NULL }
HB_INIT_SYMBOLS_END( sqlmix__InitSymbols )

HB_CALL_ON_STARTUP_BEGIN( _hb_sqlmix_rdd_init_ )
hb_vmAtInit( hb_sqlmixRddInit, NULL );
HB_CALL_ON_STARTUP_END( _hb_sqlmix_rdd_init_ )

#if defined( HB_PRAGMA_STARTUP )
   #pragma startup sqlmix__InitSymbols
   #pragma startup _hb_sqlmix_rdd_init_
#elif defined( HB_DATASEG_STARTUP )
   #define HB_DATASEG_BODY  HB_DATASEG_FUNC( sqlmix__InitSymbols ) \
   HB_DATASEG_FUNC( _hb_sqlmix_rdd_init_ )
   #include "hbiniseg.h"
#endif
