HDO (Harbour Data Objects) entra en escena.

HDO (Harbour Data Objects) entra en escena.

Postby xmanuel » Tue Aug 18, 2015 5:07 pm

Hola a todos...
Bueno ya hace un tiempo comenté que quería hacer una libreria para acceder a bases de datos de cualquier tipo sin tener que cambiar el código...
Pues ya tengo un boceto...
De monento esta en un 75% el RDL para SQLite.

Este es el código de un ejemplo:
Code: Select all  Expand view

//------------------------------------------------------------------------------
// Proyecto: Harbour Data Objects hdo
// Fichero: demo01.prg
// Descripcion: Demo con el RDL SQLite
// Autor: Manu Exposito
// Fecha: 04/05/2015
//------------------------------------------------------------------------------

#include "hdo.ch"

//------------------------------------------------------------------------------
// Funcion principal

procedure main()

    local i, oDrv, oCur, a, e, oHdo, oStmt
    local cCrea := ;
        "CREATE TABLE socios " + ;
            "( clavesocio INTEGER PRIMARY KEY," + ;
            "socio TEXT,"                       + ;
            "direccion TEXT,"                   + ;
            "telefono FLOAT,"                   + ;
            "miblob BLOB,"                        + ;
            "categoria TEXT );"

    local cIns := ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Paula Maria', 'California Baja, 34', 955667788.01, 'campo tipo blob', 'de primera es esta' );" + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Manuel', 'Formentera, 44', 955127756.02, 'blob', 'de segunda' );"        + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Carmen', 'Tinto, 33', 932667778.03, 'blob', 'de tercera' );"             + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Isabel', 'El Cano, 4', 923667745.04, 'blob', 'de cuarta' );"             + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Adrian', 'Octavio Paz, 2', 955333788.05, 'blob', 'de quinta' );"         + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Lorenzo', 'Alvareda, 9', 953245281.06, 'blob?', 'de _' );"           + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Sebastian', 'Formentera, 4', 935332255.07, 'EN blob?', 'de octaba' );"   + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Francisco', 'Chinchilla, 19', 915212218.08, 'EL blob?', 'de novena' );"  + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Miguel', 'San Rafael, 12', 954258218.09, 'ENEL blob?', 'de decima' );"   + ;
        "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria )"                  + ;
            "VALUES ( 'Antonio', 'Argentina, 2', 955222288.10, '', 'de decimoprimera' );"

    cls

    // Todas las sentencias que se envian al servidor deberian ir envueltas
    // en un TRY - CATCH
    TRY
        oDrv := THDORDL():new()

        muestra( oDrv:listRDLs(), "RDLs" )

        oHdo := THDO():new( "sqlite" )

        oHdo:connect( ":memory:" ) // Una de las dos "agenda.db"    ":memory:"

        msg( oHdo:getHost() + ";" + oHdo:getDbName() + ";" + ;
            oHdo:getUser() + ";" + oHdo:getPasswd() + ";" + ;
            oHdo:getDrvName(), "Datos conexion" )

        muestra( oHdo:rdlInfo(), "Datos del RDL" )

        TRY
            oHdo:exec( cCrea )
        CATCH  e
            muestra( e:SubSystem + ";" + padl( e:SubCode, 4 ) + ";" + ;
                e:Operation + ";" + e:Description, "CATCH 2 - Error desde Harbour" )
            muestra( oHdo:errorInfo(), "CATCH 2 - Error desde rdl:errorInfo()" )
        END

        TRY
            msg( if( oHdo:inTransaction(), "1 Esta ", "1 No esta " ) + "en una trasaccion" )

            oHdo:beginTransaction()

            msg( if( oHdo:inTransaction(), "2 Esta ", "2 No esta " ) + "en una trasaccion" )

            i := oHdo:exec( cIns )

            msg( AllTrim( Str( i ) ) + " - " + AllTrim( Str( oHdo:lastInsertId() ) ), "Columnas afectadas y lastInsertId" )

            oHdo:commit()

        CATCH e
            muestra( e:SubSystem + ";" + padl( e:SubCode, 4 ) + ";" + ;
                e:Operation + ";" + e:Description, "CATCH 2 - Error desde Harbour" )
            muestra( oHdo:errorInfo(), "CATCH 3 - Datos del error" )
            oHdo:rollBack()
        END

        TRY
            oHdo:exec( "SELECT * FROM socios WHERE clavesocio = 1" )
            muestra( oHdo:errorInfo(), "Datos del error" ) // Ejemplo de errorInfo sin errores
        CATCH
            muestra( oHdo:errorInfo(), "CATCH 4 - Datos del error" )
        END

        msg( oHdo:escapeStr( "Manuel's kely \todo mi\o" ) )

    CATCH e
        muestra( e:SubSystem + ";" + padl( e:SubCode, 4 ) + ";" + ;
                e:Operation + ";" + e:Description, "CATCH 1 - Error desde Harbour" )
        muestra( oHdo:errorInfo(), "CATCH 1 - Error desde rdl:errorInfo()" )
    FINALLY
            //--- Pruebas con prepare ------------------------------------------

            msg( "Pruebas con prepare" )
            // Con comandos
            oStmt := oHdo:prepare( "INSERT INTO socios ( socio, direccion, telefono, miblob, categoria ) " + ;
                                                "VALUES ( ?, ?, ?, :miblob, ? );" )
            oStmt:bindValue( 1, 'Maria' )
            oStmt:bindValue( 2, 'Sevilla, 76' )
            oStmt:bindValue( 3, 999777666.11 )
            oStmt:bindValue( ':miblob', 'Este es el blob' )  // Se puede hacer asi tambien
            oStmt:bindValue( 5, 'de primerisima' )

            oStmt:execute()
            oStmt:close()

            // Con el metodo query (hace un prepare y execute a la vez)
            oStmt := oHdo:query( "SELECT * FROM socios" )

            msg( oStmt:getQueryStr() + ";;Tiene " + AllTrim( str( oStmt:columnCount() ) ) + ;
                 " colunas;;" + "oStmt es de tipo: " + ;
                 oStmt:className(), "Prueba de PREPARE" )

            // Mientras fetch devuelva un array muestra el registro
            while !empty( a := oStmt:fetch() )
                muestra( a )
                ? oStmt:fetchColumn( 1 ), oStmt:fetchColumn( 2 ), oStmt:fetchColumn( 3 ), oStmt:fetchColumn( 4 ), oStmt:fetchColumn( 5 ), oStmt:fetchColumn( 6 )
            end while

            oStmt:close()

            // Egemplo de fetchAll por defecto devuelve el resultado en un array de arrays
            oStmt := oHdo:query( "SELECT socio, direccion, telefono FROM socios" )

            a := oStmt:fetchAll() // o oStmt:fetchAll( FETCH_ARRAY )

            for i := 1 to Len( a )
                AEval( a[ i ], { | x | QOut( x ) } )
                ? "----------------------------------------------"
            next

            oStmt:close()

            oStmt := oHdo:prepare( "SELECT * FROM socios" )
            oStmt:execute()
            a := oStmt:fetchAll( FETCH_HASH )
            oStmt:close()

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

        oHdo:disconnect()
    END

    if msgSN( "Pruebas con el cursor" )

        oCur := THashCursor():new()
        oCur:setCursor( a )

        msg( oCur:fieldCount(), "fieldCount" )
        msg( oCur:fieldGet( 1 ), "fieldGet" )

        oCur:goTo( 2 )

        msg( oCur:recNo(), "recNo" )

        muestra( oCur:asArray(), "asArray" )

        msg( oCur:fieldName( 2 ) + ": " + ;
             oCur:getByName( "socio" ) + " - " + ;
             oCur:fieldName( 3 ) + ": " + oCur:fieldGet( 3 ) )

        msg( oCur:recCount(), "recCount" )

        msg( oCur:skipper( 100 ), "skipper 100" )

        miBrw( oCur )

        oCur:close()

    endif

    msg( ";;;;ESTO ES TODO!!!;;;;;" )

return

//------------------------------------------------------------------------------
// Browse para el objeto HbHashCursor

procedure miBrw( o )

    local i, oBrowse := TBrowseNew( 5, 5, 16, 74 )

    o:GoTop()

    oBrowse:colorSpec     := "W+/B, N/BG"
    oBrowse:ColSep        := hb_UTF8ToStrBox( "│" )
    oBrowse:HeadSep       := hb_UTF8ToStrBox( "┼─" )
    oBrowse:FootSep       := hb_UTF8ToStrBox( "┴─" )
    oBrowse:GoTopBlock    := { || o:goTop() }
    oBrowse:GoBottomBlock := { || o:goBottom() }
    oBrowse:SkipBlock     := { | nSkip | o:skipper( nSkip ) }

    for i := 1 to o:fieldCount()
        oBrowse:AddColumn( TBColumnNew( o:FieldName( i ), genCB( o, i ) ) )
    next

    hb_DispBox( 4, 4, 17, 75, hb_UTF8ToStrBox( "┌─┐│┘─└│ " ) )

    oBrowse:forceStable()

    while oBrowse:applyKey( inkey( 0 ) ) != -1
        oBrowse:forceStable()
    enddo

return

//------------------------------------------------------------------------------
// Genera el codeblock para las columnas del Browuse

function genCB( o, i ) ; return( { || o:fieldget( i ) } )

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

 


Pronto crearé un grupo en yahoo donde os podreis bajar la LIB y los ejemplos.

Recordad que una de la novedad es que la LIB y los RDL (como los RDD pero para bases de datos como SQLite o MySQL) estan 100% en lenguaje C (Clases hechas en C saltandose así la maquina virtual necesaria cuando se hacen en PRG).
Si alguien está interesado en ser betatester que me lo diga. :roll:

Salu2 :mrgreen:
Last edited by xmanuel on Wed Aug 19, 2015 9:45 am, edited 2 times in total.
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: HDO entra en escena

Postby D.Fernandez » Tue Aug 18, 2015 5:53 pm

Manuel: Se ve muy bueno.

Saludos

Ruben Fernandez
Dario Fernandez
FWH 22.12, Harbour, MVS2022 Community, BCC, MySql & MariaDB, Dbf/Cdx VSCode.
Maldonado - Uruguay
D.Fernandez
 
Posts: 455
Joined: Wed Jul 31, 2013 1:14 pm
Location: Maldonado - Uruguay

Re: HDO entra en escena

Postby albeiroval » Wed Aug 19, 2015 4:22 am

Se ve bien.
Saludos,
Regards,

Albeiro Valencia
www.avcsistemas.com
User avatar
albeiroval
 
Posts: 359
Joined: Tue Oct 16, 2007 5:51 pm
Location: Barquisimeto - Venezuela

Re: HDO entra en escena

Postby hmpaquito » Wed Aug 19, 2015 7:16 am

hola manuel,

hdo. se ve muy bien.
nunca he entendido. en conexiones sql para que hace falta el try.
sí todos los métodos devolvieran. un objeto que tuviera una data oerror con datos caso de error se podrá preguntar por empty(ocon:oerror) .

lo he visto en vario .motores pero siempre. me ha parecido que se puede hacer de otra forma.

un saludo
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: HDO entra en escena

Postby xmanuel » Wed Aug 19, 2015 9:34 am

De una manera fácil, te explico lo TRY , CATCH y FINALLY.

Es una práctica que deberíamos adoptar todos para nuestros programas, no sólo para los que usan bases de datos.

En mi HDO yo envío mensajes al gestor de errores de harbour que no siempre tienen que ser por errores de sintaxis u otros típicos de harbour.
Por ejemplo se puede ejecutar una sentencia de SQL que no tenga fallos pero que el gestor de la bases de datos me devuelva un aviso como por ejemplo que la tabla esta bloqueada o no exista... de eso harbour ni se entera ya que para él es un mensaje más. HDO puede enviar ese aviso al gestor de errores de Harbour. En algunos entorno de bases de datos esos "errores" se llaman "excepciones" y es muy importante que el usuario sepa que se ha producido.
Pero lo más importante para nosotros es, quizás lo que te comento ahora:
La estructura de este método de tratar las excepciones es la siguiente:

TRY
...
sentencias que se deberían de ejecutar, por ejemplo abrir conexión, select, insert, deletes, updates, etc.
...
CATCH
...
sentecias que se ejecutará si se produce algún error de harbour o excepciones de la base de datos, puede ser desde un mensaje informativo o hacer algo como intentar de nuevo algo o restablecer una transacción con un "rollBack" para garantizar las reglas de coherencia y estabilidad, como por ejemplo que si no se puede salvar el encabezado de una factura o el detalle que no se salve nada.
...
FINALLY
---
tal vez esto es super importante ya que harbour garantiza que tanto si hay errores o excepciones o no, este grupo de sentencias se van a ejecutar. Aquí se debería poner el cierre de las consultas o el cierre de la conexión o de la bases de datos. O la liberación de la memoria ocupada.
...
END

Ademas evita que tengas que preguntar por el error después de cada sentencia, ya sabes el
if oStmt:lError
...
else
...
end

Es fácil no? ;-)

Te recuerdo que clipper y por tanto harbour, cuenta con la construcción:

BEGIN SEQUENCE WITH {| oError | Break( oError ) }
RECOVER USING oError
ALWAYS

que es muy parecida a lo explicado.

Espero haberte ayudado a entender :lol:
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: HDO (Harbour Data Objects) entra en escena.

Postby hmpaquito » Thu Aug 27, 2015 8:47 am

Manuel,

Agradezco tu respuesta.

A mi entender el uso de TRY deberia ser para aquellas operaciones en que Harbour puede devolver un error: division por cero, etc.

Frente a:

Code: Select all  Expand view
TRY
   oHdo:= ConexionHdo()
CATCH e

FINALLY

END


El codigo:

Code: Select all  Expand view
oHDo:= ConexionHDo()
IF !Empty(oHDo:oErr)
   ...
   MsgInfo("El error es: "+ oHDo:oErr:Description() )
ELSE
   ...  
ENDIF


Es mas xBase. No veo razon para tratar las excepciones SQL como errores. IMHO esos sucesos podrian ser atrapados por HDO.

Ademas, hay dos cosas a tener en cuenta: 1) cuando se abre un TRY, se desactiva el error handler por defecto, de modo que en el try los errores producidos pueden ser derivados del codigo Harbour o de las excepciones sql, convertidas en error por hdo, del sql. 2) Al usar un TRY algunos comandos xBase pueden estar limitados o no pueden ser usados dentro de la estructura TRY. Ejemplos: EXIT, LOOP, etc...


En todo caso, es verdad que el metodo TRY es el mas habitual, es muy comun su uso en otros lenguajes, etc... pero...

Pero sigo sin verlo, HDO puede suavizar el exabrupto de un error sql, de hecho ya lo hace pero convirtiendolo en error, y devolverlo mediante la data oErr.

Es solo una opinion personal. Agradezco tu atencion
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: HDO (Harbour Data Objects) entra en escena.

Postby xmanuel » Thu Aug 27, 2015 10:34 pm

Claro que sí, me parece bien...
Efectivamente en al CATCH van a ir a parar todo tipo de errores.
Por lo que puede ser un poco más complejo el controlarlos. Ya se me ocurrirá algo ;-)
Tambié quiero decirte que uno de los atributos que va a tener HDO es el hacer que las excepciones se traten como errores o que sea el usuario el que pregunte por ellas cuando crea que es necesario, por ejemplo al hacer el open o compilar una sentencia.

Por ejemplo:
Code: Select all  Expand view

oHDO:setAtribute( ERR_EXECEPTION, .f. ) // Silencia las excepciones como errores de harbour
// y ahora

oHDO:open()
if oHDO:errorCode() != 0 // Hay una excepcion
   for i := 1 to len( oHDO:errorInfo() )
        ?  oHDO:errorInfo()[ i ]
   next
endif
 

PD: Ya esta creado el grupo en yahoo: https://es.groups.yahoo.com/neo/groups/HDO_Harbour/info
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: HDO (Harbour Data Objects) entra en escena.

Postby cnavarro » Fri Aug 28, 2015 2:20 pm

Preguntonta: ¿Hay que tener una cuenta de yahoo?
Cristobal Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
User avatar
cnavarro
 
Posts: 6500
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: HDO (Harbour Data Objects) entra en escena.

Postby xmanuel » Fri Aug 28, 2015 2:56 pm

Creo que no hace falta...
Únicamente tengo que aprobar la inscripción al grupo. Lo hago así para que no entren los spam...
:D
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: HDO (Harbour Data Objects) entra en escena.

Postby hmpaquito » Fri Aug 28, 2015 8:14 pm

Pero quiza si sea necesaria cuenta Yahoo para postear. IMHO por estas latitudes es infrecuente tener cuenta yahoo. Puede ser mejor opción google group o similar.

Sólo son mis dos centimos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: HDO (Harbour Data Objects) entra en escena.

Postby xmanuel » Fri Aug 28, 2015 9:04 pm

Parece que voy a tener que cambiar...
Lo de yahoo es simplemente por inercia, ahí estaban las cuentas de Eagle1, Condor1, TDbf pro, etc...

La creo y ya aviso ;-) así aprovecho y reestructuro todo un poco 8)
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: HDO (Harbour Data Objects) entra en escena.

Postby xmanuel » Fri Aug 28, 2015 9:08 pm

Cambiando de tema...
A ver si alguien hace un pequeño programita con ADORDD que ataque a una base de datos SQLite y hacemos una carrera con HDO con el RDL de SQLite :D :twisted: :roll:

Si pudiera hacerlo Antonio (AHF) mejor que mejor...

PD: Antonio la semana que viene estaré por Sintra. Por si te apetece una birra :)
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla


Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 81 guests