FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Una Clase TDataBase mucho más rápida !!!
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Una Clase TDataBase mucho más rápida !!!
Posted: Fri Dec 19, 2008 01:11 PM
Jose Luis Capel de la empresa Aicom ha mejorado enormemente la velocidad de la Clase TDataBase de FiveWin haciéndola unas tres veces más rápida !!!

Como un regalo especial de Santa Claus (gracias Jose Luis!) aqui la teneis para empezar a usarla. Ha sido probada con Harbour en PC y en el Pocket PC. Se agradecen vuestras pruebas y resultados :-)
#include "fivewin.ch"
#include "dbinfo.ch"


function main()

local o, x, nSec, n := 0, a[100], b

REQUEST HB_LANG_ES // Para establecer español para Mensajes, fechas, etc..
REQUEST HB_CODEPAGE_ESMWIN // Para establecer código de página a Español
(Ordenación, etc..)
REQUEST DBFCDX //&&,DBFCDX
REQUEST DBFFPT
RDDSETDEFAULT("DBFCDX")
SET AUTOPEN OFF
SET DELETED ON
SET CENTURY ON
SET EPOCH TO( Year(Date())-50 )
SET DATE BRITISH    // Formato dd-mm-aaaa
SET EXCLUSIVE OFF
SET SOFTSEEK OFF

HB_LangSelect('ES')
HB_SetCodePage("ESMWIN") // Para ordenación (arrays, cadenas, etc..)

msginfo("Iniciamos")
o := xDatabase()      // Nueva clase derivada de tDatabase de fivewin
o:New( "CLI01.dbf")
o:lShared := .F.
o:Open()
o:lBuffer := .f.
nSec := Seconds()
// Test de velocidad en lectura de datos. Ahorro aproximado del 50%

nSec := Seconds()
for x=1 to 1000000
 uFunc( o:Codpro )
next
Msginfo(Seconds()-nSec, "Test de lectura de datos")

// Test de velocidad en lectura con movimientos
nSec := Seconds()

for x=1 to 10000
 o:Gotop()
 Do while !o:Eof()
    uFunc( o:Codpro )
    o:Skip()
 Enddo
next
Msginfo(Seconds()-nSec, "Test de lectura")


// Test de velocidad en Escritura
nSec := Seconds()
for x=1 to 10000
 o:Gotop()
 Do while !o:Eof()
    o:Codpro := "Proba" + Alltrim(Str(x))
    o:Skip()
 Enddo
next
Msginfo(Seconds()-nSec, "Test de escritura")
return NIL

FUNCTION uFunc(u);Return NIL

////////////////////////////////////////////////////////////////////////////
/
CLASS xDatabase FROM tDatabase
    METHOD SetArea()
    METHOD Load()
    METHOD CancelUpdate()   INLINE ::lBuffer := .F.

    MESSAGE FieldGet METHOD _FieldGet( nField )
    MESSAGE FieldPut METHOD _FieldPut( nField, uVal )

    METHOD Blank()
    METHOD Modified()
    METHOD SaveBuff()

    MESSAGE OemToAnsi METHOD _OemToAnsi()

    METHOD HashAddMember()
    ERROR HANDLER ONERROR( uParam1 )

ENDCLASS

METHOD SetArea( nWorkArea ) CLASS xDatabase

  local n, oClass, aDatas := {}, aMethods := {}


  ::nArea     = nWorkArea
  ::cAlias    = Alias( nWorkArea )
  ::cFile     = Alias( nWorkArea )

  if ::Used()
     ::cFile     = ( nWorkArea )->( DbInfo( DBI_FULLPATH ) )
     ::cDriver   = ( nWorkArea )->( RddName() )
     ::lShared   = ( nWorkArea )->( DbInfo( DBI_SHARED ) )

     #ifdef __HARBOUR__
        ::lReadOnly = ( nWorkArea )->( DbInfo( DBI_ISREADONLY ) )
     #else
        DEFAULT ::lReadOnly := .f.
     #endif

     DEFAULT ::lBuffer   := .t.
     DEFAULT ::lOemAnsi  := .f.

     DEFAULT ::bNetError := { || MsgStop( "Record in use", "Please, retry"
) }

     ::aStruct   = ( ::cAlias )->( DbStruct() )
     ::aFldNames = {}
     ::aBuffer := hb_HSetCaseMatch( hb_Hash(), .F. )

     for n = 1 to ( ::cAlias )->( FCount() )
        AAdd( ::aFldNames, ( ::cAlias )->( FieldName( n ) ) )
        ::HashAddMember( {( ::cAlias )->( FieldName( n ) )},;
                          ( ::cAlias )->( FieldType( n ) ),;
                          ( ::cAlias )->( FieldGet( n ) ),;
                          ::aBuffer )
     next
     hb_HSetAutoAdd( ::aBuffer, .f. )
     if ::lOemAnsi
        ::OemToAnsi()
     endif
     #ifdef __XPP__
        if ClassObject( Alias() ) == nil
           ClassCreate( Alias(), { TDataBase() }, aDatas, aMethods )
        // else
        //   ::this = Self
        endif
     #endif
  endif

return Self

METHOD _FieldGet( nPos ) CLASS xDataBase

  if ::lBuffer
     //return ::aBuffer[ nPos ]
     Return HB_HVALUEAT( ::aBuffer, nPos )
  else
     return ( ::nArea )->( FieldGet( nPos ) )
  endif

return nil
//--------------------------------------------------------------------------
-//

METHOD _FieldPut( nPos, uValue ) CLASS xDataBase

  local lLocked  := .f.

  if ::lBuffer
     //::aBuffer[ nPos ] := uValue
     HB_HVALUEAT( ::aBuffer, nPos, uValue )
  else
     if ::lShared
        if ! ::lReadOnly
           if ::IsRecLocked( ::RecNo() ) .or. ( lLocked := ::RecLock(
::RecNo() ) )
              ( ::nArea )->( FieldPut( nPos, uValue ) )
              if lLocked
                 ::Commit()
                 ::RecUnLock( ::RecNo() )
              endif
           else
              if ! Empty( ::bNetError )
                 return Eval( ::bNetError, Self )
              endif
           endif
        endif
     else
        ( ::nArea )->( FieldPut( nPos, uValue ) )
     endif
  endif

return nil

METHOD Load() CLASS xDataBase

  local n

  if ::lBuffer
     for n = 1 to ( ::cAlias )->( FCount() )
        ::aBuffer[ ::aFldNames[n] ] := ( ::cAlias )->( FieldGet( n ) )
     next

     if ::lOemAnsi
        ::OemToAnsi()
     endif
  endif

return nil

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

METHOD Modified() CLASS XDataBase

  local n

  if ::lBuffer
     for n := 1 to Len( ::aFldNames )

        if ! ( ::cAlias )->( FieldGet( n ) ) == ::aBuffer[ ::aFldNames[n] ]
           return .t.
        endif
     next
  endif

return .f.

METHOD Blank() CLASS XDataBase

  LOCAL a := HB_HKEYS( ::aBuffer )
  if ::lBuffer
     AEval( a, { |u,i| HB_HVALUEAT( ::aBuffer, i, uValBlank( u )) } )
  endif

return .f.

METHOD _OemToAnsi() CLASS XDataBase

  local n

  for n = 1 to Len( ::aFldNames )
     if ValType( ::aBuffer[ ::aFldNames[n] ] ) == "C"
        ::aBuffer[ ::aFldNames[n] ] := OemToAnsi( ::aBuffer[ ::aFldNames[n]
] )
     endif
  next

return nil

METHOD SaveBuff() CLASS XDataBase

  local n

  if ::lBuffer
     for n := 1 to Len( ::aFldNames )
        if ::lOemAnsi .and. ValType( ::aBuffer[ ::aFldNames[n] ] ) == "C"
           ( ::nArea )->( FieldPut( n, AnsiToOem( ::aBuffer[ ::aFldNames[n]
] ) ) )
        else
           ( ::nArea )->( FieldPut( n, ::aBuffer[ ::aFldNames[n] ] ) )
        endif
     next
  endif

return nil


****************************************************************************
************************************
* Descripción :
* Parámetros  : Ninguno
* Fecha       : 06/21/06
* Autor       : Equipo de desarrollo de Aicom
****************************************************************************
************************************
  METHOD HashAddMember( aName, cType, uInit, oObj ) CLASS xDataBase
//--------------------------------------------------------------------------
------------------------------------
  local cName

  if !( cType == nil )

     switch Upper( Left( cType, 1 ) )

        case "S" // STRING

             if uInit == nil
                uInit := ""
             endif

             exit

        case "N" // NUMERIC

             if uInit == nil
                uInit := 0
             endif

             exit

        case "L" // LOGICAL

             if uInit == nil
                uInit := .f.
             endif

             exit

        case "D" // DATE

             if uInit == nil
                uInit := CtoD( "" )
             endif

             exit

        case "C" // CODEBLOCK

             if uInit == nil
                uInit := { || nil }
             endif

             exit

        case "A" // ARRAY

             if uInit == nil
                uInit := {}
             endif

             exit

     end switch

  endif

return NIL


#pragma BEGINDUMP

#include "windows.h"
#include "hbapi.h"
#include "hbapierr.h"
#include "hbapiitm.h"
#include "hbapicls.h"
#include "hbvm.h"
#include "hbdate.h"
#include "hboo.ch"
#include "hbapirdd.h"
#include "hbstack.h"
#include "hbapilng.h"

char * AicomGetmessage();

HB_FUNC_STATIC( XDATABASE_ONERROR )
{


       char * cMessage       = AicomGetmessage() ;
       PHB_ITEM pSelf        = hb_stackSelfItem();
       BOOL bBuffer          = hb_itemGetL( hb_objSendMsg(pSelf, "LBUFFER",
0) );
       PHB_ITEM pValue       = hb_param(1,HB_IT_ANY);
       const char *cKey      = ( *cMessage == '_'  ?  (cMessage+1) :
cMessage ) ;


       if( bBuffer)
       {
              PHB_ITEM pHash = hb_objSendMsg(pSelf,"ABUFFER",0);
              PHB_ITEM pKey  = hb_itemPutC( hb_itemNew(NULL), cKey );

               if( *cMessage == '_' )
               {
       // Con esto asignamos un valor al buffer
                       if( pHash && pKey && pValue )
                       {
                               hb_hashAdd( pHash, pKey, pValue );
                               hb_itemRelease( pKey );
                       }
                       else
                           hb_errRT_BASE( EG_ARG, 1123, NULL,
HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
               } else
               {
                       // Esto devuelve el valor del buffer
                       PHB_ITEM pDest = hb_hashGetItemPtr( pHash, pKey,
HB_HASH_AUTOADD_ACCESS );
                       hb_itemRelease( pKey );
                       if(pDest)
                           hb_itemReturn(pDest);
                       else
                           hb_errRT_BASE( EG_BOUND, 1132, NULL,
hb_langDGetErrorDesc( EG_ARRACCESS ), 2, pHash, pValue );
               }
       }
       else
       {
               int iAreaAnt    = hb_rddGetCurrentWorkAreaNumber();
// Area anterior
               int iAreaAct    = hb_itemGetNI( hb_objSendMsg(pSelf,
"NAREA", 0 ) );    // Buscamos actual
               AREAP pArea     = ( AREAP )
hb_rddGetCurrentWorkAreaPointer();              // Necesitamos pArea
               USHORT uiField  = hb_rddFieldIndex( pArea, cKey );
// FieldPos ( cFieldName )
               hb_rddSelectWorkAreaNumber( iAreaAct ) ;
// Seleccionamos area actual

               if(uiField)
               {
                       if( *cMessage == '_' )
                       {
                       // Asignamos el valor
                               if( pValue && !HB_IS_NIL( pValue ) )
                               {
                                       if( SELF_PUTVALUE( pArea, uiField,
pValue ) == SUCCESS )
                                       {
                                               hb_itemReturn( pValue );
                                       }
                               }
                       } else
                       {
                       // Devolvemos el valor del campo
                               PHB_ITEM pItem = hb_itemNew( NULL );
                               if( pArea ) // && uiField )
                               {
                                       SELF_GETVALUE( pArea, uiField, pItem
);
                               }
                               hb_itemReturnRelease( pItem );
                       }
                       hb_rddSelectWorkAreaNumber( iAreaAnt ) ;
// Seleccionamos area anterior
               } else
               {
                       hb_errRT_DBCMD(( *cMessage == '_' ? 1005 : 1004 ),
0, "Field not found", cKey );
               }

       }
}

char * AicomGetmessage()
{
 // Thanks to Przemek
 long lOffset = hb_stackBaseProcOffset( 0 );  
 char * cMessage =  (char *)hb_itemGetSymbol( hb_stackItem( lOffset ) )->szName;  
return cMessage ; 
}
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 2064
Joined: Fri Jan 06, 2006 09:28 PM
Una Clase TDataBase mucho más rápida !!!
Posted: Fri Dec 19, 2008 11:47 PM

Muchas gracias Antonio y a el tocayo Jose Luis, empiezo a probarla...saludos... :shock:

Dios no está muerto...



Gracias a mi Dios ante todo!
Posts: 28
Joined: Sat Oct 29, 2005 12:01 AM
Re: Una Clase TDataBase mucho más rápida !!!
Posted: Thu Jan 08, 2009 04:41 PM

Esta Classe funciona con Xharbour ?
Gracias de antemano

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Una Clase TDataBase mucho más rápida !!!
Posted: Thu Jan 08, 2009 06:59 PM
Por el momento, Jose Luis Capel, solo la ha probado con Harbour.

Nosotros hemos optado, de momento, por modificar la Clase TDataBase de FWH y eliminar el AScan() que se usa en el:
   ...
         if( ( nField := ::FieldPos( SubStr( cMsg, 2 ) ) ) != 0 )
            ::FieldPut( nField, uParam1 )
         else
            _ClsSetError( _GenError( nError, ::ClassName(), SubStr( cMsg, 2 ) ) )
         endif
    ...
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1078
Joined: Thu Sep 27, 2007 03:47 PM
Re: Una Clase TDataBase mucho más rápida !!!
Posted: Thu Jan 15, 2009 10:56 PM

Como descargo esta clase, hay algun link o apartir de que version de fivewin esta incluida..

Ruben Dario Gonzalez
Cali-Colombia
rubendariogd@hotmail.com - rubendariogd@gmail.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Una Clase TDataBase mucho más rápida !!!
Posted: Thu Jan 15, 2009 10:59 PM

Ruben,

En FWH 8.12 lo que hemos hecho ha sido quitar la llamada a AScan() para ganar en velocidad, como hemos explicado en esta conversacion.

regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion