ADO RDD xHarbour

AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Rick,

Adordd uses seek in the following way (also with relations).
It works just like dbfs any changes needed in app code. See the code

We will have another function adoseek(cKey) to build a recordset with the ckey expresssion as where clause
and adoseek() to revert to the previous recordset.

Its faster than the ado_seek() but you will need to insert these changes into your app code.

Code: Select all

STATIC FUNCTION ADO_SEEK( nWA, lSoftSeek, cKey, lFindLast )
   LOCAL oRecordSet := USRRDD_AREADATA( nWA )[ WA_RECORDSET ]
   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL aSeek,cSql,nRecno,oRs := TempRecordSet()
   
   HB_SYMBOL_UNUSED( nWA )
   HB_SYMBOL_UNUSED( lSoftSeek )
   HB_SYMBOL_UNUSED( cKey )
   HB_SYMBOL_UNUSED( lFindLast )
   
   DEFAULT lFindLast TO .F.
   DEFAULT lSoftSeek TO .F.
   
   IF ADOEMPTYSET(oRecordSet)
      aWAData[ WA_FOUND ] := ! oRecordSet:EOF
      aWAData[ WA_EOF ] := oRecordSet:EOF
      RETURN HB_SUCCESS
   ENDIF
   
   IF aWAData[WA_INDEXACTIVE] = 0
      MSGALERT("No Index active seek not allowed!") //SHOULD RAISE ERROR
      RETURN HB_FAILURE
   ENDIF
   
   aSeek := ADOPseudoSeek(nWA,cKey,aWAData,lSoftSeek)

   //this tell us if we are in a subset of records from previous seek we need to reset to defaut to have another seek
   IF aWAData[WA_ISITSUBSET] .AND. aSeek[3] //ONLY IF ITS FIND IF SEEK NEW RECORDSET WILL BE ALWAYS CREATED

      oRecordSet:Close()
      cSql := IndexBuildExp(nWA,aWAData[WA_INDEXACTIVE],aWAData)
      oRecordSet:Open( cSql,aWAData[ WA_CONNECTION ] )

   ENDIF

   IF aSeek[3] //no more than one field in the expression we can use find
      
      //eof control doesnt matter in seek that are really selects THATS WHY ITS HERE
      //for finds lets place in  row 1
      IF oRecordSet:EOF .OR. oRecordSet:BOF .OR.  aWAData[ WA_EOF ] .OR. aWAData[ WA_BOF ]
         oRecordSet:MoveFirst()
         aWAData[ WA_EOF ] := oRecordSet:EOF
         aWAData[ WA_BOF ] := oRecordSet:BOF
      ENDIF

      IF lSoftSeek 
      
         oRecordSet:Find( aSeek[1]) //SEE PSEUDOSEEK
         
      ELSE
      
         IF lFindLast
            oRecordSet:MoveLast()
            oRecordSet:Find( aSeek[1],,adSearchBackward)
         ELSE
            oRecordSet:MoveFirst()
            oRecordSet:Find( aSeek[1])
         ENDIF  
      ENDIF
      
   ELSE

      //attention multiple fields in cseek expression cannot emulate behaviour of lSoftSeek 
      //more than one field in the seek expression has to be select
//MSGINFO(aSeek[2])
      cSql := IndexBuildExp(nWA,aWAData[WA_INDEXACTIVE],aWAData,.F.,aSeek[2])
//MSGINFO(CSQL)
      oRs:Open(cSql,aWAData[ WA_CONNECTION ] )  //new temp recordset
      
      IF !ADOEMPTYSET(oRs) //FOUNDED!
      
         IF lFindLast 
            oRS:MoveLast()
         ENDIF  
         
         IF !EMPTY(aWAData[WA_FIELDRECNO])  // 100% SUPPORTED AND SAFE
         
            nRecno := oRS:Fields(aWAData[WA_FIELDRECNO]):Value //field autoinc
            
         ELSE

            IF oRS:Supports(adBookmark) 
            
               /* Although the Supports method may return True for a given functionality, it does not guarantee that 
               the provider can make the feature available under all circumstances. 
               The Supports method simply returns whether the provider can support the specified functionality,
               assuming certain conditions are met. For example, the Supports method may indicate that a 
               Recordset object supports updates even though the cursor is based on a multiple table join, 
               some columns of which are not updatable*/
               IF oRS:Eof() .or. oRS:Bof()
                  nRecno := 0
               ELSE   
                  nRecno := oRS:BookMark
               ENDIF
            ELSE        
               //ATTENTION NOT WORKING CORRECTLY WITH DELETED ROWS!2    
               nRecno := IF( oRS:AbsolutePosition == adPosEOF, oRS:RecordCount() + 1, oRS:AbsolutePosition )
               //MUST TAKE OUT THE DELETED ROWS! OTHERWISE WRONG NRECNO 
               //TODO nRecno := nRecno-nDeletedRows 
            ENDIF   
         ENDIF
         
         ADO_GOTO(nWA,nRecno)
         
      ELSE  //NOT FOUND!

         // lSoftSeek DONT KNOW HOWTO!
        
      ENDIF
  
      //TO CHECK NEXT CALLS IF WE ARE IN A SUBSSET TO REVERT TO DEFAULT SET
      aWAData[WA_ISITSUBSET] := .F.
      
      oRs:close()
      
   ENDIF      
   
   aWAData[ WA_FOUND ] := ! oRecordSet:EOF
   aWAData[ WA_EOF ] := oRecordSet:EOF
   
   RETURN HB_SUCCESS
 
Regards
Antonio H Ferreira
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Antonio, Enrico, Lucas,

Do you have any experience use SELECT * FROM cTable with adUseServer and adUserClient ?

Does ADO load all rows? Is there a way to avoid that? CacheSize? MaxRecords?

I have now a new adordd version imitating 100% dbf behaviour, however Im afraid that during trials this will be slow because of the use of SELECT as explained above and the use of find to locate record number.
With adUseClient we can use ADO index property for field recno but I dont have any trials on this.

Besides this, with new version all browses in the app work without any changes.

The tester in our client says that with Oracle, although much slower than ADSrdd its ok.
In ADSoledb its slow!
In Mysql didnt tried yet.

The table average size of this application is 100.000 Recs some with > 700.000.

Can you share some of your experience?

Here you have alternatives:

100% compatible dbf behavior

Code: Select all

STATIC FUNCTION ADOSEEKSQLFIND( nWA, lSoftSeek, cKey, lFindLast )
   LOCAL oRecordSet := USRRDD_AREADATA( nWA )[ WA_RECORDSET ]
   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL aSeek,cSql,nRecno,oRs := TempRecordSet(),E
   
   HB_SYMBOL_UNUSED( nWA )
   HB_SYMBOL_UNUSED( lSoftSeek )
   HB_SYMBOL_UNUSED( cKey )
   HB_SYMBOL_UNUSED( lFindLast )
   
   DEFAULT lFindLast TO .F.
   DEFAULT lSoftSeek TO .F.

   IF ADOEMPTYSET(oRecordSet)
      aWAData[ WA_FOUND ] := ! oRecordSet:EOF
      aWAData[ WA_EOF ] := oRecordSet:EOF
      RETURN HB_SUCCESS
   ENDIF
   
   IF aWAData[WA_INDEXACTIVE] = 0
      MSGALERT("No Index active seek not allowed!") //SHOULD RAISE ERROR
      RETURN HB_FAILURE
   ENDIF
   
   aSeek := ADOPseudoSeek(nWA,cKey,aWAData,lSoftSeek)

   cSql := IndexBuildExp(nWA,aWAData[WA_INDEXACTIVE],aWAData,.F.,IF(aSeek[3],aseek[1],aSeek[2] ))
   
   oRs:Open(cSql,aWAData[ WA_CONNECTION ] )
  
   IF lSoftSeek .AND. ADOEMPTYSET(oRs)  // DIDNT FIND SFOTSEEK ON LOOK FOR THE NEXT KEY where field > key
      cSql := STRTRAN(cSql,"SELECT ","SELECT TOP 1 ") //first following record SOFTSEEK
      oRs:Close()
      oRs:Open(cSql,aWAData[ WA_CONNECTION ] )
   ENDIF
  
   IF !ADOEMPTYSET(oRs) //FOUNDED!
  
      IF lFindLast
         oRs:MoveLast()
      ELSE
         oRs:MoveFirst()
      ENDIF   
     
      IF !EMPTY(aWAData[WA_FIELDRECNO])  // 100% SUPPORTED AND SAFE
     
         nRecno := oRS:Fields(aWAData[WA_FIELDRECNO]):Value
        
      ELSE

         IF oRS:Supports(adBookmark) 
        
            //Although the Supports method may return True for a given functionality, it does not guarantee that 
            //the provider can make the feature available under all circumstances. 
            //The Supports method simply returns whether the provider can support the specified functionality,
            //assuming certain conditions are met. For example, the Supports method may indicate that a 
            //Recordset object supports updates even though the cursor is based on a multiple table join, 
            //some columns of which are not updatable
            IF oRS:Eof() .or. oRS:Bof()
               nRecno := 0
            ELSE      
               nRecno := oRS:BookMark
            ENDIF
           
         ELSE       
        
            //ATTENTION NOT WORKING CORRECTLY WITH DELETED ROWS!2   
            nRecno := IF( oRS:AbsolutePosition == adPosEOF, oRS:RecordCount() + 1, oRS:AbsolutePosition )
            //MUST TAKE OUT THE DELETED ROWS! OTHERWISE WRONG NRECNO 
            //TODO nRecno := nRecno-nDeletedRows 
           
         ENDIF  
        
      ENDIF
     
      ADO_GOTO(nWA,nRecno)
     
   ELSE  //NOT FOUND!

      oRecordSet:MoveLast()
      oRecordSet:Move(1)  // eof()
      
   ENDIF

   oRs:close()
      
   aWAData[ WA_FOUND ] := ! oRecordSet:EOF
   aWAData[ WA_EOF ] := oRecordSet:EOF
   
   //TO CHECK NEXT CALLS IF WE ARE IN A SUBSSET TO REVERT TO DEFAULT SET
   aWAData[WA_ISITSUBSET] := .F.

   IF !EMPTY(aWAData[WA_PENDINGREL]) .AND. PROCNAME(1) <> "ADO_RELEVAL" //ENFORCE REL CHILDS BUT NOT IN A ENDLESS LOOP!
      ADO_FORCEREL( nWA ) 
   ENDIF
 
 
   RETURN HB_SUCCESS

 

Fast but not 100% compatible dbf behavior

Code: Select all

STATIC FUNCTION ADOSEEKSQL( nWA, lSoftSeek, cKey, lFindLast )
   LOCAL oRecordSet := USRRDD_AREADATA( nWA )[ WA_RECORDSET ]
   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL aSeek,cSql
   
   HB_SYMBOL_UNUSED( nWA )
   HB_SYMBOL_UNUSED( lSoftSeek )
   HB_SYMBOL_UNUSED( cKey )
   HB_SYMBOL_UNUSED( lFindLast )
   
   DEFAULT lFindLast TO .F.
   DEFAULT lSoftSeek TO .F.

   IF aWAData[WA_INDEXACTIVE] = 0
      MSGALERT("No Index active seek not allowed!") //SHOULD RAISE ERROR
      RETURN HB_FAILURE
   ENDIF
   
   aSeek := ADOPseudoSeek(nWA,cKey,aWAData,lSoftSeek)
   
   cSql := IndexBuildExp(nWA,aWAData[WA_INDEXACTIVE],aWAData,.F.,IF(aSeek[3],aseek[1],aSeek[2] ))
   oRecordSet:Close() 
   oRecordSet:Open(cSql,aWAData[ WA_CONNECTION ] )
   
   //TO CHECK NEXT CALLS IF WE ARE IN A SUBSSET TO REVERT TO DEFAULT SET
   aWAData[WA_ISITSUBSET] := .T.

   IF !ADOEMPTYSET(oRecordSet) //FOUND!
      
      IF lFindLast
         oRecordSet:MoveLast()
      ELSE
         oRecordSet:MoveFirst()
      ENDIF   
         
   ENDIF

   aWAData[ WA_FOUND ] := ! oRecordSet:EOF
   aWAData[ WA_EOF ] := oRecordSet:EOF

   IF !EMPTY(aWAData[WA_PENDINGREL]) .AND. PROCNAME(1) <> "ADO_RELEVAL" //ENFORCE REL CHILDS BUT NOT IN A ENDLESS LOOP!
      ADO_FORCEREL( nWA ) 
   ENDIF
 
 
   RETURN HB_SUCCESS
 
Regards
Antonio H Ferreira
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Antonio,

Dbeval calls UR_EVALBLOCK that calls ADO_EVALBLOCK.

There is something missing because the block its not properly evaluated in SUM

Can you help me finding out whats the problem?

Is the RETURN correct?

Code: Select all

STATIC FUNCTION ADO_EVALBLOCK( nArea, bBlock, uResult )

   LOCAL nCurrArea

   nCurrArea := Select()
   IF nCurrArea != nArea
      dbSelectArea( nArea )
   ELSE
      nCurrArea := 0
   ENDIF

   uResult := Eval( bBlock )

   IF nCurrArea > 0
      dbSelectArea( nCurrArea )
   ENDIF

   RETURN HB_SUCCESS
 
Regards
Antonio H Ferreira
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: ADO RDD xHarbour

Post by Antonio Linares »

Antonio,

Please show me how you use it from your PRG
regards, saludos

Antonio Linares
www.fivetechsoft.com
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Antonio,

I solev the problem but dont know if the most correct way.

App code (does not evaluate nothing)

Code: Select all

sum quantidade to wreservas for codigoprod+armazem==codigo .and.;
            len(alltrim(nrfactur))=0 while (wmain():setmsg("Verifcando totais reservas"),.t.)
 
New ADO_EVALBLOCK (it evaluates like ths)

Code: Select all

STATIC FUNCTION ADO_EVALBLOCK( nArea, bBlock, uResult )

   LOCAL nCurrArea

   nCurrArea := Select()
   IF nCurrArea != nArea
      dbSelectArea( nArea )
   ELSE
      nCurrArea := 0
   ENDIF
   
   IF PROCNAME(1) <> "ADO_RELEVAL"
      // DONT KNOW WHY BUT DBEVAL ONLY WORK LIKE THIS
      UR_SUPER_EVALBLOCK( nArea, bBlock, uResult )
   ELSE   
      uResult := Eval( bBlock )
   ENDIF
   
   IF nCurrArea > 0
      dbSelectArea( nCurrArea )
   ENDIF
   
   
   RETURN HB_SUCCESS
 
Regards
Antonio H Ferreira
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: ADO RDD xHarbour

Post by Antonio Linares »

Isn't uResult provided by reference ?

If not, then this value will be lost:

uResult := Eval( bBlock )

not sure if it will be also applicable to:

UR_SUPER_EVALBLOCK( nArea, bBlock, uResult )
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: ADO RDD xHarbour

Post by Antonio Linares »

UR_SUPER_EVALBLOCK( nArea, bBlock, @uResult ) maybe ?
regards, saludos

Antonio Linares
www.fivetechsoft.com
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Antonio,

I dont know if uResult it is provided by reference because its called from dbeval().

Dont know why but UR_SUPER_EVALBLOCK( nArea, bBlock, uResult ) works! (without @)

Adordd its almost ready ! Now adordd its a true Rdd!
There are big differences from previous posted version.
Its much slower than DBF or ADS with few users but completely workable and the difference begin to decrease as more users are getting in.
Ill report after the trials.

The converted app is running quite ok with zero changes in the logic of the code.
The only code changes we had to make were for locates and some index creation expressions, nothing else!

Now we are testing every app routine and Im sure that we will still find some small bugs.

We are still with some minor problems like the display of numeric fields on the browses (right just instead of left just) to be solved.

Ill post the last version soon!
Regards
Antonio H Ferreira
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: ADO RDD xHarbour

Post by Antonio Linares »

Antonio,

You are doing a great work, congratulations! :-)
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
lucasdebeltran
Posts: 1303
Joined: Tue Jul 21, 2009 8:12 am
Contact:

Re: ADO RDD xHarbour

Post by lucasdebeltran »

Antonio,

I await for last release, as current is buggy and can´t be used.

Thank you.
Muchas gracias. Many thanks.

Un saludo, Best regards,

Harbour 3.2.0dev, Borland C++ 5.82 y FWH 13.06 [producción]

Implementando MSVC 2010, FWH64 y ADO.

Abandonando uso xHarbour y SQLRDD.
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Regards
Antonio H Ferreira
User avatar
lucasdebeltran
Posts: 1303
Joined: Tue Jul 21, 2009 8:12 am
Contact:

Re: ADO RDD xHarbour

Post by lucasdebeltran »

Antonio,

Some erros when compiling:

STATIC FUNCTION ADO_OPEN( nWA, aOpenInfo )

IF oRecordSet:CursorLocation = adUseClient
THIS LINE oRecordSet:Fields( aWAData[WA_FIELDRECNO] ):Properties("Optimize") := 1
ENDIF


FUNCTION Filter2Sql(nArea,cTableName,cFiltro)


THIS LINE; IN if substr(cfiltro,at(alltrim(upper((cAliasCorr)->(fieldname(n)))),cfiltro)-1,1) in " (),.##%$=<>"



Next, SET ADO DEFAULT DATABASE command does not work with Access.

Next, in this code I get error at USE command:
Error description: Error DBCMD/1005 Argument error: DBUSEAREA

Stack Calls
===========
Called from: => DBUSEAREA( 0 )
Called from: lucas.prg => MAIN( 43 )


Test sample:

Code: Select all


#include "fivewin.ch"

#include "adordd\adordd.ch"
#include "adordd\adordd.prg"



REQUEST ADORDD

function Main()

   local aArray := {}


   RDDSETDEFAULT(   "ADORDD"   )


   //SET ADO DEFAULT DATABASE TO "test2.mbd"  ENGINE TO "ACCESS"

   ADODEFAULTS( "test2.mbd", "", "ACCESS", "", "", .f. )    //FUNCTION ADODEFAULTS( cDB, cServer, cEngine, cUser, cPass,lGetThem )

   SET ADO DEFAULT RECNO FIELD TO "HBRECNO"

   SET AUTOPEN OFF


   IF !FILE(   "test2.mdb"   )

      DbCreate( "test2.mdb;table1", { { "FIRST",   "C", 30, 0 },;
                                      { "LAST",    "C", 30, 0 },;
                                      { "AGE",     "N",  8, 0 } }, "ADORDD" )
   ENDIF



   USE TABLE1 NEW ALIAS "TEST2"
   //SET INDEX TO TEST1

   XBROWSER FASTEDIT


   APPEND BLANK
   test2->First   := "HOMER si no Homer"
   test2->Last    := "Simpson"
   test2->Age     := 45

   APPEND BLANK
   test2->First   := "aaa Lara"
   test2->Last    := "Croft si no"
   test2->Age     := 32


   GO TOP

   xBrowse()
   DbCloseAll()




return nil

 
Muchas gracias. Many thanks.

Un saludo, Best regards,

Harbour 3.2.0dev, Borland C++ 5.82 y FWH 13.06 [producción]

Implementando MSVC 2010, FWH64 y ADO.

Abandonando uso xHarbour y SQLRDD.
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Lucas,

Sorry my mistake with the version posted.

Here is the correct one https://github.com/AHFERREIRA/adordd.git.

You should ow be able to run tryadordd.
Regards
Antonio H Ferreira
AHF
Posts: 837
Joined: Fri Feb 10, 2006 12:14 pm

Re: ADO RDD xHarbour

Post by AHF »

Antonio, Mr Rao,

Im having a problem with Numeric fields Len and Dec.

Ive found this in adofuncs.prg:
Max( 19, oField:Precision + 2 )

What does it mean?

NumrericScale = Nr of decimal places
Precision = Total field len

numfield N 11 4

Precision = 11
NumericScale = 4

Len = 11
Dec = 4

Making numeric fields for calculations it round always decimals to 0 although field len and dec are correct.

What can we do?
Regards
Antonio H Ferreira
User avatar
lucasdebeltran
Posts: 1303
Joined: Tue Jul 21, 2009 8:12 am
Contact:

Re: ADO RDD xHarbour

Post by lucasdebeltran »

Antonio,

I don´t understand the change in LOCATES:

IF RDDSNAME() = "ADORDD"
hb_adoSetLocateFor( "pessoa = "+"'"+(MeuNome())+"'")
locate for "pessoa = "+"'"+(MeuNome())+"'"
ELSE
...

We have to keep LOCATE FOR as DBFCDX, so not to use hb_adoSetLocateFor.

Or provide it for complex locating, but not for ordinary use.

If we have to start changing code, there is no RDD compilant.

Thank you.
Muchas gracias. Many thanks.

Un saludo, Best regards,

Harbour 3.2.0dev, Borland C++ 5.82 y FWH 13.06 [producción]

Implementando MSVC 2010, FWH64 y ADO.

Abandonando uso xHarbour y SQLRDD.
Post Reply