Class to manager MYSQL for Harbour/xHarbour
Custom Search

sábado, 31 de julio de 2010

Actuazalicion / Update

> tdolpqry.PRG
> function.c
Nuevas funnciones y metodos para localizar registros en una consulta
New functions and Method to locate record
METHOD Find( aValues, aFields, nStart, nEnd, lRefresh )
aValues = Array de datas a buscar / data array to seek
aField = Array de campos que filtrara la busqueda / Field array to filter seek
nStart = Registro  comienzo para buscar / start record to seek
nEnd = Registro final a bsucar / end record to seek
Este metodo no tiene ninguna restriccion, buscara de forma secuencial dentro de la consulta activa, devolvera el primer registro que satisfaga la condicion de busqueda
this method dont have any restriction,  will seek sequentally inside active query, return the first record that, will return the first record that satisfies the search condition

METHOD Locate( aValues, aFields, nStart, nEnd, lRefresh )
igual al metodo Find, la diferencia es, este metodo es mas rapido y para obtener los resultados deseados, debera estar la consulta ordenada de igual forma a los campos a buscar
ejemplo.
si desea buscar por field1, field2 y field3 la consulta debera estar ordenada ORDER BY field1, field2, field3

same Method Find, the difference is: this method is more fast, to get better result, the query should be order same  to field seek
ie.
if you want seek  field1, field2 and field3 the query should be order like ORDER BY field1, field2, field3


sample:

oQry = oServer:Query( "SELECT * FROM clientes ORDER BY first, last " )

oQry:Find( { "Vincent", "Brook" }, {"first", "last" } )
oQry:Locate( { "Vincent", "Brook" }, {"first", "last" } )




viernes, 30 de julio de 2010

Rapido!!!! / Fast!!!!

He realizado una nueva funcion SEEK para busquedas que IMPRESINANTEMENTE RAPIDA
I did a new function SEEK to seek, is INCREDIBLE  FAST

el ejemplo consta de 50.000 resgistros y el resultado buscando la "Z" con la rutina actual fue:
the sample is 50.000 record and result seeking "Z", with current seek method was:

Download Test (new method)



Nuevo metodo Seek
New Seek Method

Paginacion / Pagination

Dolphin maneja la paginacion automaticamente, explicare las datas y  metodo que usa
Dolphin manage automatically pagination, i will explain what datas and method used

DATAS

nCurrentPage     Pagina activa / Current page
nTotalRows        Total filas en la consulta / Total row without limits
nPageStep           Total filas por pagina / total rows for page
nMaxPages         Cantidad maxima de paginas en la consulta / Max pages avalaible in query
nCurrentLimit      Limite activop / Current limit value
bOnChangePage Code Block que se evealua cada vez que se cambia una pagina / codeblock to evaluate when change page


METHOD
SetPages( nLimit ) Activa la paginacion y configura la cantidad de lineas por paginas nLimit, Activate pagination and Set total rows by page nLimit
NextPage( nSkip ) Va a la siguiente pagina disponible o avanza nSkip paginas / Go to next page avalaible or skip nSkip pages
PrevPage( nSkip ) Va a la  pagina anterior disponible o retrocede nSkip paginas / Go to previous page avalaible or back nSkip pages
FirstPage() Va a la primera pagina / Go to first page
LastPage() Va a la ultima pagina / Go to last page
GotoPage( nPage )   Va a la pagina espcifica por nPage / Go to specific nPage Page


El ejemplo esta construido con la version 10.7 de fivewin / This samples was built with fivewin version 10.7 
Ejemplo / Sample

Download Here

//Build
   oQry = oServer:Query( "SELECT * FROM clientes ORDER BY last limit 100" 

   oQry:SetPages( 100 )
   oQry:bOnChangePage = { || oBrw:Refresh(), ChangeTitle( oQry, oDlg ) }




//Button Actions


   @ 10, 10 RBBTN aBtns[ 1 ] PROMPT "&First" OF oDlg SIZE 20, 15 ;
            GROUPBUTTON FIRST  CENTER ;
            ROUND ROUNDSIZE 2;
            ACTION( oQry:FirstPage() ) ;
            WHEN( oQry:nCurrentPage > 1 )                  

   @ 10, 30 RBBTN  aBtns[ 2 ] PROMPT "&Prev" OF oDlg SIZE 20, 15 ;
            GROUPBUTTON  CENTER ;
            ROUND ROUNDSIZE 2;
            ACTION( oQry:PrevPage() ) ;
            WHEN( oQry:nCurrentPage > 1 )

   @ 10, 50 RBBTN aBtns[ 3 ] PROMPT "&Goto" OF oDlg SIZE 20, 15 ;
             GROUPBUTTON  CENTER ;
             ROUND ROUNDSIZE 2;
             ACTION( nPage := oQry:nCurrentPage,;
                     MsgGet( "Select Page:", "Page", @nPage ),;
                     oQry:GoToPage( nPage ) )


   @ 10, 70 RBBTN aBtns[ 4 ] PROMPT "&Next" OF oDlg SIZE 20, 15 ;
             GROUPBUTTON  CENTER ;
             ROUND ROUNDSIZE 2;
             ACTION( oQry:NextPage() ) ;
             WHEN( oQry:nCurrentPage < oQry:nMaxPages )

   @ 10, 90 RBBTN aBtns[ 5 ] PROMPT "&Last" OF oDlg SIZE 20, 15 ;
             GROUPBUTTON END  CENTER ;
             ROUND ROUNDSIZE 2;          
             ACTION( oQry:LastPage() ) ;
             WHEN( oQry:nCurrentPage < oQry:nMaxPages )



XBrowse

Una buena noticia... / Good News

Se ha incluido dentro del xbrowse de fivewin la configuracion para le uso de TDolphin

Was included inside fivewin xbrowse, the TDolphin configuration


http://forums.fivetechsupport.com/viewtopic.php?f=16&t=19450

miércoles, 28 de julio de 2010

Actuazalicion / Update

+ Nuevo: Funcion D_LogicalValue( lOnOff ), activa / Desactiva el uso de valores logicos, permitiendo hacer uso de las variables logicas de [x]Harbour como 1/0, sin argumento retorna el estatus actual sino retorna el estatus anterior,  por omision esta activado

+ New: function D_LogicalValue( lOnOff ), activate / Deactivate Logical values, for use [x]Harbour logical values like 1/0, without argument return current value else return Old Status, default status is Activated

Usar / To Use

D_LogicalValue( .F. ) //Deactivate

Gracias a Willi por la sugerencias / Thanks Willi by suggestion  

martes, 27 de julio de 2010

TDolphin in OSX

TDolphin ahora en Mac (osx), explicare como generar las libreria de MySql y las libreria de Dolphin
TDolphin now in Mac( osx), i will explain how build MySql Lib and Dolphin libs

Herramientas/ Tools
Descargar cMake / Download cMake

Mysql
Descargar fuentes de MySql / Download Mysql Source Code



Dolphin
Descargar dolphin desde el SVN / Download Dolphin from SVN
www.sitasoft.net/dolphinosx
Descargar Directa Dolphin  / Direct Download Dolphin
Esta incluida la libreria dolphin y Mysql, harbour / Include Dolphin and MySql Lib, harbour
estan construidas en Snow Leopard / built in Snow Leopard


Installing...

1- Instalar CMake / Install CMake
2- Descomprimir Codigo fuente de MySql / UnZip MySql Source Code
A la carpeta descomprimida (ej. mysql-connector-c-6.0.2), personalmente, le cambio el nombre para ser mas facil accesarla desde el terminal (ej. connector) / The unzip folder (ie. mysql-connector-c-6.0.2), personally, i change the folder name to easy access from terminal (ie. connector)
3- Abrir el Terminal e ir a la carpeta descomprimida de MySql / Open Terminal and go to MySql unzip folder
4- cmake -G "Unix Makefiles"
5- make
6- Descomprimir archivo de dolphin (Descarga Directa) / Unzip dolphin file (Direct Download)
7- desde el terminal ir a la carpeta descomprimida de Dolphin / from terminal go to dolphin unzip folder
8- make

para constuir los ejemplos con fivemac, deberan cambiar los path dentro de buildfw.sh (si fuese necesario) / Build fivemac samples, should change path inside buildfw.sh (if is necessary) 


viernes, 23 de julio de 2010

Debido al comentario de Charly hice un ajuste al ejemplo TestFile
For Charly comment, i did a little adjust to samples TestFile

"Charly dijo...
Daniel,
A medida que vas añadiendo ficheros, el sistema se vuelve mas lento al hacer el browse(). Seguramente en la lectura te bajas tambien el contenido del blob..."

"Charly said ...
Daniel,
As you add files, the system becomes slower to make the browse (). Probably you're reading the contents of the blob ... "


Gracias Charly por el feedback
Thank Charly by Feedback



jueves, 22 de julio de 2010

Guardando Archivos/Save File

Es necesario actualizar desde el SVN, se hizo un pequeño cambio para construir el ejemplo
Is necessary download from SVN, i did a littlel change to build sample

Guardar archivos en una tabla de MySql, no se diferencia de nada a las tecnicas usadas actualmente, Dolphin se encarga de hacer las conversiones automaticamente
Save file into Mysql table is not different, we use the same way, Dolphin converts automatically the data.

Download Sample

El ejemplo puede demorar un poco porque hay archivos grandes
The sample maybe are slow, because there are big file saved 

Estructura de la tabla / Table Structure


Leer el Archivo / Read File
Dolphin tiene una funcion para leer archivos / Dolphin have a funtion to read file

uData = D_ReadFile( cFile )

Dacer el insert / Do Insert

oServer:Insert( "files", { "filename", "file" }, { GetOnlyName( cFile ), uData } )


Guardar en disco / Save to Disk
para activar el menu en el browse, oprimir click derecho sobre la fila que desea usar
To show menu over browse, Right click over row you want use

Bloquear Registros / Record Lock

Existen funciones de bloqueo, pero a razon de consulta, no existen funciones de bloqueo de registro solo para bloquear tablas ( LOCK TABLES/UNLOCK TABLES ), lo que se hace es simular un bloqueo
There are functions to lock, but at the rate of consult, there is no record locking features to lock tables only LOCK TABLES/UNLOCK TABLES ),  is done is to simulate a lock

GET_LOCK( str, interval )
Intenta obtener un bloqueo con un nombre dado por la cadena str, con intervalo de espera interval en segundos. Retorna 1 si el bloqueo se obtiene con éxito, 0 si el intento ha agotado el tiempo (por ejemplo, porque otro cliente ya ha bloqueado el nombre), o NULL si ocurre un error. Si hay un bloqueo obtenido con GET_LOCK (),  se libera cuando se ejecuta RELEASE_LOCK (). 
Podra ejecutar una nueva GET_LOCK (),  despues de liberarlo o cuando su conexión termina (ya sea normal o anormal). Los bloqueos obtenidos con GET_LOCK () no interactúan con las transacciones. Es decir, confirmar una transacción no libera los bloqueos que haya obtenido en la transacción.
Esta función se puede utilizar para implementar bloqueos de aplicaciones o simular bloqueo de registros. Los nombres se bloquean en el servidor. Si un nombre ha sido bloqueada por un cliente, GET_LOCK () bloquea cualquier petición de otro cliente para bloquear con el mismo nombre. Es recomendable para estas situaciones hacer bloqueos dbname.str
Try to get a lock with a name given by the string str, with timeout interval in seconds. Returns 1 if the lock was  successfully, 0 if the attempt timed out (for example, because another client has already locked the name), or NULL if an error occurs. If there is a lock obtained with GET_LOCK (), is released when running RELEASE_LOCK ().
GET_LOCK() can run again, after release or when your connection is terminated (either normal or abnormal). The locks obtained with GET_LOCK () does not interact with transactions. That is, committing a transaction does not release the locks it has obtained in the transaction.
This feature can be used to implement application locks or to simulate record locks. The names are locked on the server. If a name has been blocked by a client, GET_LOCK () locks any request by another client for a lock with the same name. It is advisable for these situations to lock dbname.str


RELEASE_LOCK( str )
Libera el bloqueo asignado a la cadena str obtenida con  GET_LOCK (). Retorna 1 si el bloqueo se libera, 0 si el bloqueo no fue establecido por este hilo (en cuyo caso el bloqueo no se libera), y NULL si el nombre de bloqueo no existiera.
Releases the lock assigned to the string obtained GET_LOCK() str. Returns 1 if the lock was released, 0 if the lock was not established by this thread (in which case the lock is not released) and NULL if the lock name does not exist

Ejemplo/Sample:
si queremos bloquear un registro en la tabla customer,  customer.ID = 4
if we want lock a record in customer table, customer.ID = 4
SELECT GET_LOCK( 'customer.4', 120 )

usando en Dolphin / using Dolphin
oQry = oServer:Query( "SELECT GET_LOCK( 'customer.4', 120 ) AS locked" )
? oQry:locked
oQry = oServer:Query( "SELECT RELEASE_LOCK( 'customer.4' ) AS unlocked" )
? oQry:unlocked

IS_FREE_LOCK( str )
Comprueba si el nombre de bloqueo str está libre para uso. Retorna 1 si el bloqueo está libre (nadie lo  esta usando), 0 si el bloqueo está en uso, y NULL si se produce un error (como argumentos incorrectos).
Checks whether the lock named str is free to use. Returns 1 if the lock is free (no one is using the lock), 0 if the lock is in use, and NULL if an error occurs (such as an incorrect argument).

IS_USED_LOCK( str )
Comprueba si el nombre de bloqueo str está en uso. Si es así, devuelve el identificador de conexión del cliente que tiene el bloqueo.De lo contrario, devuelve NULL.
Checks whether the lock named str is in use. If so, it returns the connection identifier of the client that holds the lock. Otherwise, it returns NULL.

miércoles, 21 de julio de 2010

Save Vs Update

En el post pasado expuse varias formas de hacer una Actualizacion a una tabla
Last post i showed various ways to do a Update

  • Method Update
  • Build Sentence
  • Method Save
  • Sentence UPDATE

El Method Save fue el mas lento,  para actualizacione masivas no es recomendable, porque hace validaciones internas, transforma los valore de clipper a MySql (como sabemos son diferentes los manejos de datas), construye la sentencia Update, verifica valores cambiados del registro actual.
Por tal razon la idea del post anterior fue mostrar las vias de hacer una Actualizacion y  demostrar que tecnicas usar dependiendo de las necesidades del modulo a construir

The Save Method was the slowest for bulk update is not recommended, because it makes internal validations, transforms the values of clipper to MySql (as we know are different handlings of datas), builds sentence Update, check the current record changed values.
For this reason the idea of the previous post was to show ways to make an update and demonstrate techniques to use depending on the needs of the module to build


Download Sample

ahora muestro un simple ejemplo de como usar el Method save / Now show a simple test to use Method Save

definimos los Get / define Gets

   REDEFINE GET oData:last_name ID 4008 OF oDlg   UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:first_name ID 4009 OF oDlg  UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:suffix ID 4010 OF oDlg      UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:city ID 4011 OF oDlg        UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:state ID 4012 OF oDlg       UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:birth ID 4013 OF oDlg       UPDATE WHEN lNew .OR. lMod
   REDEFINE GET oData:death ID 4014 OF oDlg       UPDATE WHEN lNew .OR. lMod

Cuando cambiemos un valor solo basta hacer un SAVE para guardar dicho valor
When we change a value with a simple SAVE we can save the current value changed

oData:Save()



de otra forma tendriamos que crear la sentencia o hacer uso del Method Update y llenar los valores que requiere ese metodo
otherwise the sentence would have to create or use the Update Method and fill the values that this method requires


   cQry += "UPDATE president SET "
   cQry += "last_name=" + ClipValue2SQL( oData:last_name ) + ","
   cQry += "first_name=" + ClipValue2SQL( oData:first_name ) + ","
   cQry += "suffix=" + ClipValue2SQL( oData:suffix ) + ","
   cQry += "city=" + ClipValue2SQL( oData:city ) + ","
   cQry += "state=" + ClipValue2SQL( oData:state ) + ","
   cQry += "birth=" + ClipValue2SQL( oData:birth ) + ","
   cQry += "death=" + ClipValue2SQL( oData:death )  + " WHERE "

   cWhere += "last_name" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_last_name' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "first_name" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_first_name' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "suffix" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_suffix' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "state" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_state' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "city" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_city' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "birth" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_birth' ] ) ) == "NULL", " IS ", " = " ) +;
              cData + " AND "
   cWhere += "death" + ;
              If( ( cData := ClipValue2SQL( oData:oQuery:hRow[ '_death' ] ) ) == "NULL", " IS ", " = " ) +;
              cData

   cQry += cWhere
  
   oData:oServer:SqlQuery( cQry )
  
   oData:LoadQuery()



Podemos ver quela diferencia de tiempo no es importante, pero si en la construccion del codigo
we can see, the process time is not important, but yes  building source code

martes, 20 de julio de 2010

Update

Mostrare 4 formas de actualizar una tabla, activando y descactivando los errores internos de dolphin y ver la repercucion en este proceso

I'll show 4 ways to do Update, setting ON/Off dolphin's internal errors

To turn ON/OFF internal error in tdolp.mak change USE_INTERNAL YES/NO

Download with Internal Error ON
Download with Internal Error OFF

1) Method Update
Util para parametrizar la actualizacion, dejando a Dolphin crear la sentencia / Useful to custom Update and let Dolphin build sentence

   aColumns = { "Married", "Age", "salary", "Notes" }

   aValues = { .f., 40, 10, oQry:Notes }
   cWhere = "notes='" + oQry:Notes + "'"


   oServer:Update( "clientes", aColumns, aValues, cWhere )

Internal Error ON

Internal Error OFF


2) Build Sentence

Util para parametrizar la actualizacion, constuyendo la sentencia  / Useful to custom Update  build sentence your self



            cQry = "UPDATE clientes SET "
            FOR EACH cField IN aColumns
#ifdef __XHARBOUR__
               n = HB_EnumIndex()
#else                      
               n = cField:__EnumIndex() 
#endif 
               cValue   = ClipValue2SQL( aValues[ n ] )
               cQry += cField + " = " + cValue + ","
            NEXT             
            //Delete last comma 
            cQry = SubStr( cQry, 1, Len( cQry ) - 1 ) 
            cQry += " WHERE " + cWhere
            oServer:SqlQuery( cQry )       
            EXIT 


Internal Error ON


Internal Error OFF


3) Method Save
Util para guardar la edicion de un registro especifico / Useful to save record in edition mode 

            oQry:married = .T.
            oQry:age = 30
            oQry:salary = 20
            oQry:Save()

Internal Error ON

Internal Error OFF

4) Sentence UPDATE
Crear la sentencia Update / Build Update sentence


            cQry = "update clientes set "
            cQry += "married = 0,"
            cQry += "age = 40,"
            cQry += "salary = 10"
            cQry += " where " + cWhere
            oServer:SqlQuery( cQry )


Internal Error ON


Internal Error OFF


Actuazalicion / Update

* tdolpqry.PRG

* tdolpsrv.PRG
* tdolp.mak
Nuevas opciones para construir: 
Podemos asignar ON/OFF el uso de errores internos, asignando YES/NO a la variable USE_INTERNAL dentro de tdolp.mak
podemos asignar  ON/OFF el Debug,  asignar YES/NO a la USE_DEBUG dentro de tdolp.mak
si DEBUG es ON la DATA bDebug en TDolphinSrv esta habilitada para ser usada, Los argumentos son: cQuery, ProcName( 1 ), ProcLine( 1 )
! Pequeño ajuste al Method Update



+ samples/testupd.PRG



* tdolpqry.PRG
* tdolpsrv.PRG
* tdolp.mak
New build options: 
we can set ON/OFF internalerror, setting YES/NO USE_INTERNAL variable inside tdolp.mak
we can set ON/OFF Debug setting YES/NO USE_DEBUG variable inside tdolp.mak
if DEBUG is ON the DATA bDebug in TDolphinSrv is avalaible to use, Arg cQuery, ProcName( 1 ), ProcLine( 1 )
! minor change in Method Update

+ samples/testupd.PRG

sábado, 17 de julio de 2010

Usar Variables en el servidor/To use variable on Server

Podemos hacer uso de variables en el servidor para mejorar nuestras consultas y permitir que el mismo servidor de MySql ejecuta las operaciones por nosotros
We can use variables on Mysql server to enhanced our queries and let the server work by us

primero debemos declararlas antes de usarlas
despues podemos hacer uso de esas variable dentro de nuestras consultas
First, should declare variables before use it
after we can use inside query


SET @VariableName:=Value, @VariableName:=Value,...


ejemplo/sample

oServer:Execute( "set @balance:=0, @val1:=20, @val2:=100" )
oQry = oServer:Query( "select credit, debit, @balance:=@balance+credit-debit as balance, @val1, @VAL2 from test" )

Download Test
podemos ver come el query retorna en una de sus columnas el saldo acumulado de cada fila
we can see, the query return a total balance by row

Actualizacion/Update

He subido al SVN las versiones de harbour y xHarbour que uso
no las volvere a actualizar al SVN, para ello dejare un link de descarga y avisare cuando sean cambiadas

I uploaded to SVN the Harbour and xharbour versions, used by dolphin, i would not upload to SVN again, but will leave a download link and will notice when be change

viernes, 16 de julio de 2010

ADO Vs Dolphin

La prueba es una serie 500 INSERT a una tabla remota, los resultados son muy similares
The test is a 500 INSERT series to a remote table, the result was similar

   FOR n = 1 TO 500
       
       cQry = "INSERT INTO testman SET " + ;
              " NAME='NAME" + StrZero( n, 4 ) + "'" +;
              ",LAST='LAST" + StrZero( n, 4 ) + "'" +;
              ",BIRTH='" + StrZero( Year( Date() ), 4 ) + "-" + StrZero( Month( Date() ), 2 )  + "-" + StrZero( Day( Date() ), 2 ) +"'" +;
              ",ACTIVE=1" 
       ? cQry       
       oServer:Execute( cQry )
          
   NEXT


ADO

Download TestAdo


Test1




Test2


Test3


Test4


DOLPHIN

Download TestDolphin

Test1

Test2


Test3


Test4


Mi punto de vista es, la gran diferencia  en el manejo de ambos, la idea de dolphin es ser mas "amigable" al usuario

My personal view is, the big difference is handle, my Dolphin idea is be more "friendly" to user


miércoles, 14 de julio de 2010

xBrowse

Les incluyo un ejemplo sencillo de como configurar el xBrowse de fivewin, podran decsargarlo del SVN testfw2.prg, el ejemplo incluye, configuracion de xbrowse, ordenamiento de columnas y busqueda incremental por la columna ordenada

It's a simple test to build fivewin xbrowse, the sample include: xbrowse setup, columns sort, incremental seek by order column.




Configuracion / setup

   @ 0, 0 XBROWSE oBrw
 
   SetDolphin( oBrw, oQry )
    
   oBrw:CreateFromCode()
.....


PROCEDURE SetDolphin( oBrw, oQry, lAddCols )

   LOCAL xField    := NIL
   LOCAL cHeader   := ""
   LOCAL cCol      := ""
   LOCAL aFldNames, oCol
 
   DEFAULT lAddCols := .T.

   WITH OBJECT oBrw
      :bGoTop    := {|| If( oQry:LastRec() > 0, oQry:GoTop(), NIL ) }
      :bGoBottom := {|| If( oQry:LastRec() > 0, oQry:GoBottom(), nil )  }
      :bSkip     := {| n | oQry:Skip( n ) }
      :bBof      := {|| oQry:Bof() }
      :bEof      := {|| oQry:Eof() }
      :bBookMark := {| n | If( n == nil,;
                           If( oQry:LastRec() > 0, oQry:RecNo(), 0 ), ;
                           If( oQry:LastRec() > 0, oQry:goto( n ), 0 ) ) }
      :bKeyNo    := {| n | If( n == nil, ;
                           If( oQry:LastRec() > 0, oQry:RecNo(), 0 ), ;
                           If( oQry:LastRec() > 0, oQry:Goto( n ), 0 ) ) }
      :bKeyCount := {|| oQry:LastRec() }
   END

   oBrw:nDataType         := DATATYPE_USER
   oQry:Cargo = oQry:aStructure[ 1 ][ MYSQL_FS_NAME ]
 


   IF lAddCols

      aFldNames := oQry:aStructure

      FOR EACH xField IN aFldNames
         cCol    := xField[ MYSQL_FS_NAME ]
         cHeader := xField[ MYSQL_FS_NAME ]
         oCol = SetColFromMySQL( cCol, cHeader, oQry, oBrw )
         //set order
         oCol:bLClickHeader = Build_CodeBlock_Order( oQry )
      NEXT

      oBrw:bSeek  := { | c | DolphinSeek( c, oQry ) }

   ENDIF

RETURN




Orden / Order


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

FUNCTION Build_CodeBlock_Order( oQry )
RETURN {| nMRow, nMCol, nFlags, oCol | SetOrderDolphin( oCol, oQry ) }
....


PROCEDURE SetOrderDolphin( oCol, oQry )

   LOCAL aToken
   LOCAL cType, cOrder
    
   aToken := HB_ATokens( oQry:cOrder, " " )

   IF Len( aToken ) == 1
      AAdd( aToken, "ASC" )
   ENDIF

   cOrder = AllTrim( Lower( aToken[ 1 ] ) )
   cType = aToken[ 2 ]
 
   AEval( oCol:oBrw:aCols, {| o | o:cOrder := " " } )
   IF oQry:aStructure[ oCol:nCreationOrder ][ MYSQL_FS_NAME ] == cOrder
      IF Upper( cType ) == "ASC"
         cType = "DESC"
         oCol:cOrder = "D"
      ELSE
         cType = "ASC"
         oCol:cOrder = "A"
      ENDIF
   ELSE
      cOrder = oQry:aStructure[ oCol:nCreationOrder ][ MYSQL_FS_NAME ]
      cType = "ASC"
      oCol:cOrder = "A"
   ENDIF
   oQry:SetOrder( cOrder + " " + cType )
   oCol:oBrw:Refresh()

RETURN

Busqueda Incremental / Incremental seek


FUNCTION DolphinSeek( c, oQry )

   LOCAL nStart
   LOCAL uData, nNum
   LOCAL aToken
  
   STATIC aLastRec := {}

   aToken := HB_ATokens( oQry:cOrder, " " )
 
   IF Len( aLastRec ) < Len( c )
      IF Len( aLastRec ) == 0
         nStart = 1
      ELSE
         nStart = oQry:RecNo()
      ENDIF
      AAdd( aLastRec, nStart )
   ELSE
      ADel( aLastRec, Len( aLastRec ) )
      ASize( aLastRec, Len( aLastRec ) - 1 )
      IF Len( aLastRec ) == 0
         nStart = 1
      ELSE
         nStart = ATail( aLastRec )
      ENDIF
   ENDIF
 
   oQry:Seek( c, aToken[ 1 ], nStart, oQry:LastRec(), .T., .T. )
 
RETURN .T.

domingo, 11 de julio de 2010

Este ejemplo esta construido con la version 10.6 de fivewin
This sample is build with fivewin version 10.6

Download samples

se puede usar los campos directamete como variables
can use field like variables


   REDEFINE GET oGet[ 1 ] VAR oQry:first    ID 4008 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE GET oGet[ 2 ] VAR oQry:last     ID 4009 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE DTPICKER oGet[ 3 ] VAR oQry:hiredate ID 4010 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE GET oGet[ 4 ] VAR oQry:street   ID 4011 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE GET oGet[ 5 ] VAR oQry:city     ID 4012 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE GET oGet[ 6 ] VAR oQry:state    ID 4013 OF oDlg  UPDATE WHEN( lNew .OR. lMod )
   REDEFINE GET oGet[ 7 ] VAR oQry:salary   ID 4014 OF oDlg  UPDATE WHEN( lNew .OR. lMod ) PICTURE "999,999,999.99"

   REDEFINE CHECKBOX oGet[ 8 ] VAR oQry:married ID 4026 PROMPT "Married";
            OF oDlg       UPDATE WHEN( lNew .OR. lMod )



asignando accion a los botones
Buttons Action


    //new                

   REDEFINE RBBTN ID 4019 OF oDlg;
            ACTION ( lNew := ! ( lMod := ! lMod ),;
                     oQry:GetBlankRow( .F. ),;
                     oDlg:Update(),;
                     oGet[ 1 ]:SetFocus() ) ;
            WHEN ! lNew;
            GROUPBUTTON FIRST ROUND ROUNDSIZE 2;
            BITMAP "..\bitmaps\new2.bmp"



....


   //Modify        
   REDEFINE RBBTN  ID 4020 OF oDlg ;
            ACTION ( lMod := ! ( lNew := ! lNew ),;
                     oDlg:Update() );
            WHEN ! lMod;
            GROUPBUTTON;
            BITMAP "..\bitmaps\edit.bmp"

  




   //delete
   REDEFINE RBBTN  ID 4022 OF oDlg;
            ACTION ( If( MsgYesNo( "Do want delete current record" ), ;
                         ( oQry:Delete(), oQry:Refresh(),oDlg:Update() ), ) ) ;
            WHEN ( ! lNew .AND. ! lMod );
            GROUPBUTTON;
            BITMAP "..\bitmaps\delete2.bmp"

sábado, 10 de julio de 2010

Actuazalicion / Update

* Reversada a la revison 68, eliminada la caracteristica Lock/Unlock para ser remplazada por la nueva clase TDolphinRow con la informacion del record seleccionado, este objeto puede ser tratado igual al objeto query al que pertenece
* El method GetBlackRow devuelve un objeto TDolphinRow 
+ METHOD GetRowObj( nRow ), devuelve un objeto TDolphinRow 
* mejorada funcion ClipValue2SQL para el manejo de campos no null

* Reverted to rev 68, eliminated the features Lock / Unlock
to be replaced by the new class TDolphinRow with information of current record selected, this object can be treated the same as the query object to which it belongs
*  method GetBlackRow return  TDolphinRow objeto
+ METHOD GetRowObj( nRow ), return  TDolphinRow objeto
* Improved function ClipValue2SQL for handling "not null" fields

viernes, 9 de julio de 2010

Modificar valor de sentencias / Modify sentences values

Para query "simples" Dolphin separa individualmente el valos de las sentencias y poder asignarlas de manera individual (WHERE,  GROUP, HAVING, LIMIT, ORDER)
Puede dejar que dolphin construya  las sentencias "simples" por ud


For "simple" query, Dolphin individually separated intervals of sentences and to assign them individually (WHERE, GROUP, HAVING, LIMIT, ORDER)
You can let dolphin build "simple" sentences for you



      oQry = TDolphinQry():New( "select student.student_id, student.name, "+;
                                "absence.date from absence right join  student on "+;
                                "student.student_id = absence.student_id "+;
                                "where absence.date is not null group by student.student_id"+;
                                " order by absence.date limit 10", oServer )


    ? "QUERY  => " , oQry:cQuery
    ?
    ? "WHERE  => " , oQry:cWhere  
    ? "GROUP  => " , oQry:cGroup  
    ? "HAVING => ", oQry:cHaving  
    ? "ORDER  => ", oQry:cOrder  
    ? "LIMIT  => ", oQry:cLimit





    ? "Changing sentences values"
    ?
    oQry:SetLimit( 3, .F. ) //no refresh
    oQry:SetWhere( "absence.date is null" )// by default the query is refresh
    ? "Limit changed"
    ? "Where changed"
    ? "LIMIT  => ", oQry:cLimit
    ? "WHERE  => " , oQry:cWhere  
    ?
    ? "QUERY  => " , oQry:cQuery
    ?


jueves, 8 de julio de 2010

Actuazalicion / Update

Español
* tdolpqry.PRG
* tdolpsrv.PRG

Implementado registro de bloqueo (METHOD Lock ( nRec )), ahora podemos bloquear un registro específico o registro actual, para no recargar el valor mientars se navega por el Query, para desbloquear el registro usar MÉTODO UnLock()
se desbloquea  automáticamente el registro en METHOD Save
utilice el método Unlock para desbloquear cualquier registro bloqueado
+ Nueva DATA Cargo para el uso programador, puede establecer cualquier cosas ahí dentro

English
* tdolpqry.PRG
* tdolpsrv.PRG


Implemented Lock record ( METHOD Lock( nRec ) ), now we can lock a specific record or current record to not refresh while skipping inside query, to Unlock record use METHOD UnLock() 
Lock record is automatically Unlock in METHOD Save
use METHOD Unlock to Unlock any record locked
+ New DATA Cargo for programmer user, you can set any things in there

miércoles, 7 de julio de 2010

Actuazalicion / Update

Nuevas actualizaciones / New updates 


rev. 80

* tdolpqry.PRG
! memo filed now is not affect by PadR, thanks Ariel

rev. 81

> tdolpqry.PRG
! fixed bug in fieldput using HASH, nos is fixed... Thanks Alejandro
! fixed bug in VerifyValue is a wrong nPadR value, nos is fine... Thanks Gabriel

rev. 83

* minor fix in GetBlankRow(), reported by Gabriel, thanks

rev.84

* tdolpqry.PRG
- discontinued internal error ERR_NOTHINGTOSAVE 9017, is not functional 
+ METHOD UnDo( cnField ), to revers original value for specific num/field name, if is empty cnField, will affect all field 

martes, 6 de julio de 2010

Actuazalicion / Update

> tdolp.mak
> mtdolph.bat
* minor change to build with harbour

> tdolphin.ch
> function.c
> tdolpqry.PRG
> tdolpsrv.PRG
! the fieldname should be lower case, the case sensitive is over tablename
+ add max PadR (see MAX_BLOCKSIZE ) to internal control character data
+ D_SetPadRight( lOnOff ) to activate PadR in character data
The PadR value es take from Max value between Len Default value, Len Current value

> testqry1.PRG

Resultado de la consulta / Retrieve query

//Español
ya hemos visto lo facil que es crear una consulta (query)

Para generar la consulta podemos usar directamente la clase TDolphinQry
o llamar al Metodo Query desde el objeto servidor,
en ambos casos nos devuelve un objeto TDolphinQry

oQry = TDolphinQry():New( cQuery, oServer )
oQry = oServer:Query( cQuery )

ahora demostraremos como obtener informacion de la consulta

abarcaremos la parte basica de un query WHERE, GROUP, HAVING, ORDER, LIMIT
y podran ser consultados y/o cambiados dinamicamente.
La estructura de los campos e una consulta sera convertida a una estructura
de campos reconocida por [x]Harbour, dicha estructura sera llenada automaticamente
y guardada en la data aStruture

veamos un ejemplo...

//English
we seen how easy it is to create a query
To build the query can be directly used TDolphinQry class
or call Query Method from the object server
in both cases we returns an TDolphinQry object


oQry = TDolphinQry ():New (cQuery, oServer)
oQry = oServer: Query (cQuery)


now show how to obtain information from the query


will cover the basic parts of a query WHERE, GROUP, HAVING, ORDER, LIMIT
and may be consulted and / or changed dynamically.
The query field structure will be converted to a field structure
recognized by [x] Harbour, this structure will be filled automatically
and stored in the data aStruture


para mas detalles revisar MySql field structure en tdolphin.ch
for more detail review MySql field structure in tdolphin.ch

Ejemplo/sample 

Download here

// define query tables
aTables  = { "grade_event", "score", "student" }

//define query columns (from)
aColumns = { "student.student_id", "student.name", "grade_event.date", "score.score", "grade_event.category"}

//define where
cWhere   = "grade_event.date = " + ClipValue2SQL( CToD( '09-23-2008' ) ) + " and "
cWhere  += "grade_event.event_id = score.event_id and score.student_id = student.student_id"

//let Dolphin build query by us, remmenber can build the query your self
cQuery = BuildQuery( aColumns, aTables, cWhere )

//build query object
oQry = TDolphinQry():New( cQuery, oServer )

FOR EACH aRow IN oQry:aStructure
  FOR EACH uItem IN aRow
     ?? If( ValType( uItem ) == "N", PadR( AllTrim( Str( uItem ) ), 10 ), PadR( uItem, 11 ) )
  NEXT
  ?
NEXT


   //Retrieve cWhere
   ? "WHERE" 
   ? oQry:cWhere 


   //Retrieve Info, we can use fieldget( cnField )
   ? 
   ? oQry:student_id, oQry:name, oQry:date, oQry:score, oQry:category
   ? oQry:FieldGet( 1 ), oQry:FieldGet( 2 ), oQry:FieldGet( "date" ), oQry:FieldGet( 4 ), oQry:FieldGet( "category" )
   



   // moving recond pointer
   ? oQry:Goto( 5 ), "GoTo 5"
   ? oQry:GoTop(), "GoTop()"
   ? oQry:GoBottom(), "GoBottom()"
   ? oQry:Skip( - 3 ), "Skip -3"
   ? 





//browsing
   oQry:GoTop()
   ? "student_id  name        date    score    category"
   ? "==================================================="
   do while !oQry:Eof()
      ? oQry:student_id, oQry:name, oQry:date, oQry:score, oQry:category
      oQry:Skip()
   end
   ? "==================================================="








lunes, 5 de julio de 2010

Incluir a la Tabla / Insert table

Para añadir registrios a nuestras tablas usamos la sentecia INSERT INTO
To add rows inside tables we use  INSERT INTO

En Dolphin se puede usar un string con la sentecia INSERT o usar el METHOD Insert de la clase TDolphinSrv

cQuery = "INSERT INTO student VALUES ('Megan','F',NULL),('Joseph','M',NULL),"+;
                 "('Kyle','M',NULL),('Katie','F',NULL),('Abby','F',NULL),('Nathan','M',NULL),"+;
                 "('Liesl','F',NULL),('Ian','M',NULL),('Colin','M',NULL),('Peter','M',NULL),"+;
                 "('Michael','M',NULL),('Thomas','M',NULL),('Devri','F',NULL),('Ben','M',NULL),"+;
                 "('Aubrey','F',NULL),('Rebecca','F',NULL),('Will','M',NULL),('Max','M',NULL),"+;
                 "('Rianne','F',NULL),('Avery','F',NULL),('Lauren','F',NULL),('Becca','F',NULL),"+;
                 "('Gregory','M',NULL),('Sarah','F',NULL),('Robbie','M',NULL),('Keaton','M',NULL),"+;
                 "('Carter','M',NULL),('Teddy','M',NULL),('Gabrielle','F',NULL),('Grace','F',NULL),"+;
                 "('Emily','F',NULL)"

oServer:Execute( cQuery )

Se puede leer la informacion de un archivo usando la funcion D_ReadFile( filename )
we can read query from file using function  D_ReadFile( filename )
cQuery =  D_ReadFile( "insert_member.txt" )
oServer:Execute( cQuery )

METHOD Insert( cTable, aColumns, aValues )
cTable Table name
aColumns Array with field names
aValues Array with values, should contain same total item like aColumns array

No preocuparse pro el formato de la data, Dolphin la convierte automaticamente en formato valido de Mysql
we dont worried about value format, Dolphin convert automatically all data to MySql format
   cTable = "grade_event"

   aColumns = { "date", "category", "event_id" } // 3 items
   aValues = { CToD( '09-03-2008' ), 'Q', NIL }  // 3 values
   oServer:Insert( cTable, aColumns, aValues )

===================================
sample
Download here

screen shoot

domingo, 4 de julio de 2010

Construir Tablas / Create Table

Para crear tablas usamos la sentencia CREATE TABLE 
1) podemos usar un string con toda la sentencia y en enviarla al METHOD Execute
To Create tables we use CREATE TABLE we can use a string with all sentence and send to METHOD Execute


   cQuery = "CREATE TABLE absence( student_id INT UNSIGNED NOT NULL, "
   cQuery += "date       DATE NOT NULL,"
   cQuery += "PRIMARY KEY (student_id, date), "
   cQuery += "FOREIGN KEY (student_id) REFERENCES student (student_id)"
   cQuery += ") ENGINE = InnoDB"

   oServer:Execute( cQuery )


2) O, usar el METHOD CreateTable( cTable, aStruct, [cPrimaryKey], [cUniqueKey], [cAuto], [cExtra], [lIfNotExist],[lVer] )
Or, To use METHOD CreateTable( cTable, aStruct, [cPrimaryKey], [cUniqueKey], [cAuto], [cExtra], [lIfNotExist],[lVer] )
cTable El nombre de la tabla en la Base de Datos
              name table in Database
aStruct Esrtructura parecida a la usada en DBF con el siguiente formato
              Stucture like DBF with this format
{ Name, Type, Length, Decimal, Not Null (logical), Defaul value }

cPrimaryKey Nombre del campo que sera PRIMARY KEY
                         field name PRIMARY KEY
cUniqueKey Nombre del campo que sera UNIQUE
                       field name UNIQUE
cAuto Nombre del campo que sera AUTO INCREMENT
            field name AUTO INCREMENT
cExtra sentencias despues de la definicion de campos
             statements after field definitions


   aMemberSt = { { "member_id" , "N", 10, 0, .T., 0 },;
               { "last_name" , "C", 20, 0, .T., } ,;
               { "first_name", "C", 20, 0, .T., } ,;
               { "suffix"    , "C",  5, 0, .F., } ,;
               { "expiration", "D", 10, 0, .F., } ,;
               { "email"     , "C",100, 0, .F., } ,;
               { "street"    , "C", 50, 0, .F., } ,;
               { "city"      , "C", 50, 0, .F., } ,;
               { "state"     , "C",  2, 0, .F., } ,;
               { "zip"       , "C", 10, 0, .F., } ,;
               { "phone"     , "C", 20, 0, .F., } ,;
               { "interests" , "C",255, 0, .F., } }
  oServer:CreateTable( "member", aMemberSt, "member_id", , "member_id", "ENGINE = InnoDB" )

========================================================
sample...
Download Sample

Actuazalicion / Update

Nueva actuzalcion, algunas mejoras, bug correjidos y nuevo ejemplo
ver seccion ultima actualizacion para mas detalles

New Update, some enhancement, fixed bud and new sample
check last update section for more detail

sábado, 3 de julio de 2010

Construir un Query / Build Query

Existen varias formas de hacer un query, veamos unos ejemplos
There are some way to build a query, see the next samples

Download source


   aTables = { "test", "testman" }
   aColumns = {"ID", "NAME", "LAST" }

   aQuery[ 1 ] = "SELECT * FROM test"
   aQuery[ 2 ] = "SELECT ID, NAME, LAST FROM test"
   aQuery[ 3 ] = "SELECT ID, NAME AS USER, LAST FROM test1"
   aQuery[ 4 ] = "SELECT test.ID, testman.NAME, testman.LAST FROM test, testman WHERE test.ID = testman.ID"
   aQuery[ 5 ] = "SELECT ID, NAME AS USER, LAST FROM test LIMIT 100"
   aQuery[ 6 ] = "SELECT ID, NAME AS USER, LAST FROM test ORDER BY NAME DESC LIMIT 100"


   //BuildQuery( aColumns, aTables, cWhere, cGroup, cHaving, cOrder, cLimit, cExt, lWithRoll )
   //"SELECT * FROM test"
   aQuery[ 7 ] = BuildQuery( {"*"}, {"test"} )

   //"SELECT ID, NAME, LAST FROM test"
   aQuery[ 8 ] = BuildQuery( aColumns, {aTables[ 1 ]} )

   //"SELECT ID, NAME AS USER, LAST FROM test1"
   aQuery[ 9 ] = BuildQuery( { "ID", "NAME AS USER", "LAST" }, {aTables[ 1 ]} )

   //"SELECT test.ID, testman.NAME, testman.LAST FROM test, testman WHERE test.ID = testman.ID"
   aQuery[ 10 ] = BuildQuery( {"test.ID", "testman.NAME", "testman.LAST"}, aTables, "test.ID = testman.ID" )

   //"SELECT ID, NAME, LAST FROM test LIMIT 100"
   aQuery[ 11 ] = BuildQuery( aColumns, {aTables[ 1 ]}, , , , , "100" )

   //"SELECT ID, NAME, LAST FROM test ORDER BY NAME DESC LIMIT 100"
   aQuery[ 12 ] = BuildQuery( aColumns, {aTables[ 1 ]}, , , , "NAME DESC", "100" )


   FOR EACH cQry IN aQuery
      TRY
         // we can use
         // oQry = oServer:Query( cQry )
         oQry = TDolphinQry():New( cQry, oServer )
      CATCH oErr
         ? "[N]", cQry
         ?
         ATail( oServer:aQueries ):End()
         ? oErr:Description
         ?
         lError = .T.      
      END
      IF ! lError
         ? "[Y]", oQry:cQuery
         oQry:End()
      ENDIF
      lError = .F.
   NEXT

Actuazalicion / Update

Nueva Actualizacion

* dolerr.ch
2 nuevos errores internos / 2 new internal errors
+ ERR_INVALIDQUERYARRAY
+ ERR_MULTIQUERYFAULIRE

> tdolpqry.PRG
* Mejorado METHOD BuildDatWhere(), ahora hace la conversion a un string valido para ser usado en una sentencia mysql
enhanced METHOD BuildDataWhere(), now convert to create a legal SQL string that you can use in a SQL statement if is necessary

> tdolpsrv.PRG
- METHOD Insert parameter lTransaction, is not necessary
- METHOD Update parameter lTransaction, is not necessary
+ METHOD MultiQuery( aQuery, lTransaction ), to execute multiples queries, ideal to transactions
+ FUNCTION BuildInsert( cTable, aColumns, aValues ), return a INSERT command with specific data

Actuazalicion / Update

Nueva actualizacion / New update

Creado  PROCEDURE Dolphin_DefError para el  control de errores cuando no se define el code block bOnError, Dolphin_DefError crea un objeto oError

Created PROCEDURE Dolphin_DefError to errors control when  codeblock  bOnError is NIL,  Dolphin_DefError create a oError Object

viernes, 2 de julio de 2010

Actuazalicion / Update

Nueva actualizacion con los cambios antes mensionados

New update with change in last post

Avances

He elaborado una rutina de reconexion sin perder el status de los Querys activos en el servidor
Se esta elaborando el proceso de borrado de registros, gracias a la sugeriencia de Alejandro (TecniSofware ) con 2 metodos nuevos Delete() y Zap
Se han correjido algunos detalles pendientes
Estos cambios seran actualizados muy pronto para su descarga

I developed a routine of reconnection without losing the active querys
It is developing the process of deleting records, thanks to Alejandro (TecniSofware)
two new methods Delete () and Zap()
Correct some bugs
These changes will be updated soon for download

Primera conexion / First connection


Para hacer conexion y manejarla usar la clase TDolphinSrv, esta permitira manejar diferentes opciones con el Host que no involucren consultas directas a tablas

To make conection with Host, use the Class TDolphinSrv, this allow direrent options to handle host that do not involve tables queries

TDolphinSrv():New( cHost, cUser, cPassword, nPort, nFlags, cDBName, bOnError )

cHost 
El valor de cHost puede ser tanto un nombre como una dirección IP
The value of cHost may be either a hostname or an IP address.

cUser
Contiene el identificador de login del usuario MySQL
Contains the user's MySQL login ID

cPassword
contiene el password del usuario
contains the password for user

nPort
Puerto de conexion Host, si no es 0, el valor se usará como número de puerto en la conexión TCP/IP
Port Host conection, if nPost is not 0, the value is used as the port number for the TCP/IP connection

nFlags
es normalmente 0, pero puede usarse una combinación de  flags en circunstancias muy especiales
The value of client_flag is usually 0, but can be set to a combination flags in very special circumstances

cDBName
nombre de la base de datos
is the database name

bOnError ( Self, nError, lInternal )
Codeblock que personaliza el manejo de errores
Codeblock to custom manager error message


Download test



#include "hbcompat.ch"
#define CRLF Chr( 13 ) + Chr( 10 )

PROCEDURE Main()

   LOCAL hIni      := HB_ReadIni( ".\connect.ini" )
   LOCAL oServer   := NIL
   LOCAL cServer   := hIni["mysql"]["host"],;
         cUser     := hIni["mysql"]["user"],;
         cPassword := hIni["mysql"]["psw"],;
         nPort     := val(hIni["mysql"]["port"]), ;
         cDBName   := hIni["mysql"]["dbname"], ;
         nFlags    := val(hIni["mysql"]["flags"])
   LOCAL cText := ""

   oServer = TDolphinSrv():New( cServer, ;
                                cUser, ;
                                cPassword, ;
                                nPort, nFlags, cDBName,;
                                {| oServer, nError, lInternal | GetError( oServer, nError, lInternal  ) } )

   IF ! oServer:lError
      cText += "Connection OK" + CRLF
      cText += "Host: " + oServer:cHost +CRLF
      cText += "Database: " + +oServer:cDBName + CRLF
      ? cText
   ENDIF

   oServer:End()
   inkey(5)

RETURN

PROCEDURE GetError( oServer, nError, lInternal )
   LOCAL cText := ""

   cText += "Error from Custom Error Message" + CRLF
   cText += "================================" + CRLF
   cText += oServer:ErrorTxt() + CRLF
   cText += "ERROR No: " + Str( nError ) + CRLF
   cText += "Internal: " + If( lInternal, "Yes", "No" ) + CRLF

   ? cText + CRLF
   wait "presiona una tecla"

RETURN