Una Clase TDataBase mucho más rápida !!!

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Una Clase TDataBase mucho más rápida !!!

Post by Antonio Linares »

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 :-)

Code: Select all

#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
User avatar
joseluisysturiz
Posts: 2024
Joined: Fri Jan 06, 2006 9:28 pm
Location: Guatire - Caracas - Venezuela
Contact:

Post by joseluisysturiz »

Muchas gracias Antonio y a el tocayo Jose Luis, empiezo a probarla...saludos... :shock:
Dios no está muerto...

Gracias a mi Dios ante todo!
User avatar
tosko
Posts: 28
Joined: Sat Oct 29, 2005 12:01 am
Location: Puerto Vallarta MX
Contact:

Re: Una Clase TDataBase mucho más rápida !!!

Post by tosko »

Esta Classe funciona con Xharbour ?
Gracias de antemano
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Una Clase TDataBase mucho más rápida !!!

Post by Antonio Linares »

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:

Code: Select all

   ...
         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
User avatar
ruben Dario
Posts: 986
Joined: Thu Sep 27, 2007 3:47 pm
Location: Colombia

Re: Una Clase TDataBase mucho más rápida !!!

Post by ruben Dario »

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
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Una Clase TDataBase mucho más rápida !!!

Post by Antonio Linares »

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
Post Reply