You are on page 1of 11

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.

prg

Martes, 02 de Julio del 2013 02:45 a.m.

** Ejemplo-VFP-accesando-SQLite.prg ** Jun-2013 ** jhernancanom-at-hotmail-dot-com *-- Author: Jess Hernn Cano Martnez *-- Este es un ejemplo de acceso a una base de datos SQLite utilizando el concepto * de motor embebido, que ofrece este motor. * La metodologa parece ser similar a ADODB.RecordSet. * *-- Nota: Es el primero de varios documentos para accesar tanto este motor como otros * *-- Parmetros: Este ejemplo no usa parmetros * *-- Ejemplo de llamada: desde la ventana de comandos de VFP (o IDE compatible), * o desde un .EXE ya compilado, ejecuta lo sgte: * do "Ejemplo-VFP-accesando-SQLite.prg" * (puede cambiar el nombre a este ejemplo si gusta) * *-- Requirimientos: * Este ejemplo consta de varios archivos, todos incluidos en el .zip que usted descarg. * Esos archivos son: * 1. Ejemplo-VFP-accesando-SQLite.prg * 2. NewObjectsPack1.dll * 3. SQLITE3COMUTF8.dll * 4. VB6STKIT.DLL * 5. northwind.sqlite * Ver el archivo con la descripcin de ellos. * *-- Recomendacin: * Con el fin de probar adecuadamente este procedimiento, y cualq otro que quieras escribir, * te recomiendo que abras la base de datos con un administrador de bases de datos. * En mi caso tengo estos siete (la mayora portales): * * SQLite Studio: muy bien!!! seleccionado y recomendado de mi parte a ustedes (pero puedes usar el que uses, si te acostumbraste a otro) * * SQLite Admin : muy bien!!! (pero ya me est fallando: no me volvi a abrir NorthWind.sqlite) * ste tiene el logo original de SQLite * SQLiteMan : bien "Describe table" [PortableApps] * * Descart los sgtes: * *x SQLite Database Browser: no muestra la estructura completa [PortableApps] *x Database Browser: requiere ODBC [PortableApps] *x HeidiSQL: requiere ODBC [PortableApps] *x EasySQLite2: parece que es para SQLite2. * * El hecho de descartarlos por requerir de ODBC es slo por probar primero la * tecnologa "motor embebido", es decir con el mnimo de recursos. * El sgte paso ser (2) conocer un "motor portable" como MariaDB (y probablemente MySQL) * y luego (3) ODBC, pero haciendo la instalacin del ODBC del motor desde VFP, para de * esta forma requerir menos recursos (con "recursos" me refiero a los conceptos de * embebido y portabilidad, es decir no necesitar instalar nada ms que la app)
-1-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

* * * * * *

Pero claro antes de ello ser optimizar y socializar la info que este ejemplo nos permita descubrir sobre SQLite y VFP. Nos vemos en el foro!!!

************************************************************************************************ ****************** PROCEDURE MAIN ************************************************************** ************************************************************************************************ ** Primera parte: Registrando el motor if .not.Registrar() = MessageBox ( "No se pudo registrar + "No puedo continuar" ) return endif CLEAR

NewObjectsPack1.dll " +chr(13)+chr(13);

** Segunda parte: Inicializando el motor SQLite embebido, SQLite3 COM *' Using the embedded SQL database engine. *' You use the SQLite COM object to work with it: local db as "newObjectsPack1.sqlite.dbutf8" local oR as "adodb.recordset" local oObj as "adodb.recordset" ** db= CreateObject("newObjectsPack1.sqlite.dbutf8") --> as est en la doc, pero no me funciona db = CreateObject("newObjects.sqlite3.dbutf8") && busqu en el registro de Windows y encontr newObjects.sqlite3.dbutf8 y otros && selecciono ste pues corresponde a la versin 3 M.cCad = chr(13) + "Version de SQLite: "+transform(db.SQLiteVersion) ; + chr(13) + "db.TypeInfoLevel : "+transform(db.TypeInfoLevel) ** TypeInfoLevel &&---> 0 -zero= MessageBox ( M.cCad, 0, '--', 5000) db.NoErrorMode = .T. &&' True - no error mode, False(defaults) - error mode && Si se deja el standard (db.NoErrorMode = .f.), cuando se presente un && error en la ejecucin del motor SQLite, se mostrar un error de VFP && al estilo del comando ERROR && Si hacemos db.NoErrorMode = .t., cuando se presente un error en la && ejecucin del motor SQLite, podemos usar propiedades internas de && SQLite COM (motor embebido) para deteccin y manejo de errores ** Indicamos cul es la base de datos que queremos abrir local M.cDB M.cDB = addbs(curdir())+"northwind.sqlite"

** Tercera parte: Abrimos la base de datos


-2-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

*' Opens or creates a SQLite3 database If db.Open(M.cDB) Then ** Cuarta parte: Pedimos la estructura de uno de loa rchivos de datos (una de la tablas) ** ============================================================================================ ** De forma didctica veamos cmo capturamos la estructura de una de las tablas ** local M.cSQL M.cSQL = "PRAGMA table_info('Orders');" * Executes one SQL statement (or several statements separated by ;) oObj = db.Execute(M.cSQL) do case case !empty(db.LastError) or isnull(oObj) *' Perform some error handling *' An error has occured - deal with it = MessageBox ( "Error occured on Execute: " +chr(13)+ db.LastError +chr(13)+ M.cSQL; +chr(13)+ M.cDB ) case oObj.Count<1 = MessageBox ( "No hay datos luego de Execute: " +chr(13)+ db.LastError +chr(13)+ M.cSQL; +chr(13)+ M.cDB ) ** es decir que no hay registros ** la cuestin es que el recordset --aparte hablo de sto-- no puede mostrar ni siquiera ** su estructura cuando no hay registros (o sea si no hay registros, tampoco hay campos) otherwise = MessageBox( 'RecCount: '+tran(oObj.Count)+chr(13)+'ColCount: '+; tran(oObj.Item(1).Count), 0, '--db.Execute('+M.cSQL+')--' ) ** Quinta parte: Procedemos a mostrar la estructura de ese archivo de datos ** Creemos un cursor temporal para recibir los datos que capturemos de SQLite3 (smil VFP) create cursor '_Orders_' ( ; FIELD_NUMBER C( 3),; FIELD_NAME C(50),; FIELD_TYPE C(20),; FIELD_LEN C( 7),; FIELD_NULL C( 5),; FIELD_DEFA C(50),; FIELD_AUTOINC C(10),; PRIMARY_KEY C( 5),; TABLE_NAME C(50),; TABLE_RULE M( 4) ) cCad = '' for K=1 to oObj.Count ** la vble cCad es para mostrar los campos en un MessageBox ** esta vble cCad se puede obviar, y usar slo el cursor _Orders_
-3-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

for L=1 to oObj.Item(K).Count cCad = cCad + chr(13)+tran(K) + ' - ' + tran(L) + ' - ' + ; TRANSFORM(oObj.Item(K).Item(L)) next ** agreguemos un registro al cursor de la estructura append blank ** oObj.Item(K).Item(1) contiene el nmero del campo (zero-based, o sea en la ** forma extraa que maneja el lenguaje C y variantes: la ** posicin del primer elemento de un vector es el nmero cero) ** claro que Item() no es basado en cero (!!!???) ** oObj.Item(K).Item(2) contiene el nombre del campo ** oObj.Item(K).Item(3) contiene el tipo de campo (INTEGER, NUMERIC, TEXT, ** REAL, BLOB, CHAR, VARCHAR y quiz otros), adems si es VAR o ** VARCHAR capturo la logitud, pues est facilita de agarrar ** oObj.Item(K).Item(4) cuando no admite nulos, dice 99. ** oObj.Item(K).Item(5) contiene el valor por defecto; en caso contrario .null. ** oObj.Item(K).Item(6) cuando es clave primaria, contiene un 1. ** ** En las ayudas est el tema "How to obtain more useful information?" ** en SQLite - Version 3 - Object Reference - Execute (cerca del final) ** ** Which will return a SELECT-like result with the following columns: ** cid - the field id ** name - the field name ** type - the type specified ** notnull - 0/1 - 1 means the field must not be null ** dflt_value - the default value for the field (if present) ** pk - 0/1 - 1 means the field is primary key ** ** replace FIELD_NUMBER with transform(oObj.Item(K).Item(1)) replace FIELD_NAME with transform(oObj.Item(K).Item(2)) replace FIELD_TYPE with transform(oObj.Item(K).Item(3)) if '('$oObj.Item(K).Item(3) cNum = STREXTRACT(oObj.Item(K).Item(3),"(",")") if len(alltrim(cNum))=1 cNum = '0'+cNum endif replace FIELD_LEN with cNum endif if (oObj.Item(K).Item(4)=99) replace FIELD_NULL with 'No' else replace FIELD_NULL with 'Yes' endif if transform(oObj.Item(K).Item(5))='.NULL.' replace FIELD_DEFA with '' else replace FIELD_DEFA with transform(oObj.Item(K).Item(5)) endif if (oObj.Item(K).Item(6)=1)
-4-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

replace PRIMARY_KEY with 'PK' else replace PRIMARY_KEY with '' endif replace TABLE_NAME with alias() **

&& 'Orders'

cCad = cCad + CHR(13) if mod(K,6)=0 = MessageBox ( cCad,0,'CAMPOS DE '+alias() ) cCad = '' endif next if !empty(cCad) = MessageBox ( cCad,0,'CAMPOS DE '+alias() ) endif cCad = '' if .f. browse nomo else ** una forma ms elegante de hacer un BROWSE ** (lstima que dependa de la vble lBrowseSetup) private oBrowse, lBrowseSetup lBrowseSetup=.F. wait window [Visualizando datos...] nowait BROWSE NAME oBrowse WHEN BrowseSetup() &&NOWAIT oBrowse = .null. wait clear endif endcase ** ============================================================================================= ** Sexta parte: Procedemos a mostrar los datos de ese archivo de datos ** ============================================================================================= ** vamos a visualizar los datos de una de las tablas ** **local M.cSQL M.cSQL = "SELECT * FROM Orders" oR = db.Execute(M.cSQL) *oR = db.Execute("SELECT * FROM 'Order Details'") do case case !empty(db.LastError) or isnull(oR) *' Perform some error handling *' An error has occured - deal with it = MessageBox ( "Error occured on Execute: " +chr(13)+ M.cDB )

+chr(13)+ db.LastError +chr(13)+ M.cSQL;

case oR.Count<1 = MessageBox ( "No hay datos luego de Execute: " +chr(13)+ db.LastError +chr(13)+ M.cSQL;
-5-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

+chr(13)+ M.cDB ) ** es decir que no hay registros otherwise *= MessageBox ( oR.ReadOnly ) && no existe *db.TypeInfoLevel = 4 *messagebox ( oR.Item(1).Info ) &&--> object ** Esta es otra forma de visualizar los campos del recordset ** pero la info no es suficiente: (1) con .Key() obtengo el nombre del campo, ** y (2) con .Info() obtengo un nmero que representa el tipo, que debo pasar ** por la UDF GetType() para que me d la descripcin, y (3) con la ** funcin LEN() de VFP obtengo la longitud de los campos TEXT ** Esta forma es demasiado simple y sin suficiente info (para dar), as que ** podemos descartarla For FieldN = 1 To oR.Item(1).Count && Nmero de Campos (columnas) ** se muestra campo a campo messagebox ( 'Num.Campo: '+ tran(FieldN) +chr(13); +'Nom.Campo: '+ oR.Item(1).Key (FieldN) +chr(13); +'nTipo : '+ tran(oR.Item(1).Info(FieldN)) +chr(13); +'Desc.Tipo: '+ GetType(oR.Item(1).Info(FieldN)) +chr(13); +'LongCampo: '+ iif(oR.Item(1).Info(FieldN)=8,; TRAN(LEN(oR.Item(1).Item(FieldN))),'.'), ; 0, '--CAMPOS---'+transform(oR.Item(1).Count) ) && , 2000 Next *' Display the results for example *For I = 1 To oR.Count * = MessageBox ( oR(I).Name ) *Next ** Sets the level of type info reported by the Execute methods db.TypeInfoLevel = 1 ** El manual indica que hay cinco valores posibles (de 0 a 4), para tres formas ** diferentes en que el motor me devuelve los datos: ** 0 - (default) Type info is returned as type constants ** 1 or 3 Type info is returned as a single string containing semicolon ";" ** delimited list of type names for each column in the result. ** 2 or 4 - The type info is returned as a collection of type names. ** Me interesa de la segunda forma: lista con valores separados por punto y coma ** pero siempre me devuelve una coleccin, sea cual sea el valor que ponga ** Ser que debo inicializar esta propiedad antes de ejecutar el mtodo Execute()? ** (supona que era entes de "pedir los datos" --antes de ejecutar .Info()--) ** *' Cycle through all the rows For I = 1 To oR.Count ** ** ** ** oR.Count oR.Item(1).Count oR.Item(I).Key(#) oR.Item(I).Item(#) && && && && Nmero Nmero Nombre Valor de Registros de Campos (columnas) del Campo del Campo
-6-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

** Tenemos dos formas de pedir el dato de un campo (el valor, lo que se alamcena) ** 1. Utilizando oR.Item(I).Item("OrderID") ** donde OrderID es el nombre del campo que necesite ** 2. Utilizando oR.Item(I).Item(14) ** donde 14 es el nmero del campo que necesite ** Utilizando oR.Item(I).Key(14) le pido al sistema el nombre del campo ** Ntese: oR es el recordset ** .Item(I) I es el nmero del registro dentro del recordset ** .Key(14) me devuelve el nombre del campo nmero 14 dentro del recordset ** Vamos a visualizar slo los primeros cinco campos de cada registro M.cCad = chr(13) + "OrderID : "+transform(oR.Item(I).Item("OrderID" )) ; + chr(13) + "CustomerID : "+transform(oR.Item(I).Item("CustomerID" )) ; + chr(13) + "EmployeeID : "+transform(oR.Item(I).Item("EmployeeID" )) ; + chr(13) + "OrderDate : "+transform(oR.Item(I).Item("OrderDate" )) ; + chr(13) + "RequiredDate: "+transform(oR.Item(I).Item("RequiredDate")) ; + chr(13) + "Freight : "+transform(oR.Item(I).Item("Freight" )) ; + chr(13) + "ShipCity : "+transform(oR.Item(I).Item("ShipCity" )) ; + chr(13) + "ShipRegion : "+transform(oR.Item(I).Item("ShipRegion" )) ; + chr(13) ; + chr(13) + padr(oR.Item(I).Key(01),20)+": "+transform(oR.Item(I).Item(01)) + chr(13) + padr(oR.Item(I).Key(02),20)+": "+transform(oR.Item(I).Item(02)) + chr(13) + padr(oR.Item(I).Key(03),20)+": "+transform(oR.Item(I).Item(03)) + chr(13) + padr(oR.Item(I).Key(04),20)+": "+transform(oR.Item(I).Item(04)) + chr(13) + padr(oR.Item(I).Key(05),20)+": "+transform(oR.Item(I).Item(05)) + chr(13) + padr(oR.Item(I).Key(08),20)+": "+transform(oR.Item(I).Item(08)) + chr(13) + padr(oR.Item(I).Key(11),20)+": "+transform(oR.Item(I).Item(11)) + chr(13) + padr(oR.Item(I).Key(12),20)+": "+transform(oR.Item(I).Item(12)) + chr(13) wait window left(M.cCad,255) nowait noclear = MessageBox ( M.cCad, 0, transform(I), 5000 )

; ; ; ; ; ; ; ;

** Como estamos viendo los datos en MessageBoxes, paremos en 30 por si nos aburrimos... if I>30 exit endif ** si deseas ver los datos en un Browse y/o guardarlos en un cursor o en un DBF, puedes ** hacer append blnk/replace o insert por aqu... ** alguien nos apoya con algo como un ToDBF o ToCursor o FromRS2DBF, o similar, ms o ** menos genrico? Next endcase *' Finish everything db.Close Else *' Deal with the error further = MessageBox ( "Error occured on Open: " +chr(13)+ db.LastError +chr(13)+ M.cDB ) EndIf
-7-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

** Sptima parte: Limpiamos el sistema operativo, des-registrando las DLL que registramos ** nosotros mismos wait window 'Desregistrando...' timeout 2&&nowait noclear && if !Registrar('U') = MessageBox ( "No se pudo des-registrar NewObjectsPack1.dll " +chr(13)+chr(13) +; "Pero podemos continuar..." ) endif wait clear ************************************************************************************************ ****************** ENDPROC MAIN **************************************************************** ************************************************************************************************

** De acuerdo a las ayudas --imagen 5-- necesitamos registrar slo dos DLLs: NewObjectsPack1.dll ** y SQLITE3COMUTF8.dll para activar el motor embabido de SQLite en nustras app ** (as nos evitamos tener que hacer una instalacin adicional --la del motor--) ** Para ello utilizamos la DLL genrica para registrar que es Vb6stkit.DLL ** Veamos... function Registrar(M.pDes) if pcount()=0 declare integer DLLSelfRegister in [Vb6stkit.DLL] string lpDllName local M.lOk M.lOk = .t. M.lOk = M.lOk and ( DLLSelfRegister([NewObjectsPack1.dll]) = 0 ) M.lOk = M.lOk and ( DLLSelfRegister([SQLITE3COMUTF8.dll] ) = 0 ) return ( M.lOk ) endif if pcount()=1 and M.pDes=='U' wait window 'Desregistrando' nowait noclear local M.lOkN, M.lOkS store .t. to M.lOkN, M.lOkS if !UnregisterControl([NewObjectsPack1.dll]) M.lOkN = .f. = MessageBox ( "No se pudo des-registrar NewObjectsPack1.dll " +chr(13)+chr(13) +; "Continuemos..." ) endif if !UnregisterControl([SQLITE3COMUTF8.dll] ) M.lOkS = .f. = MessageBox ( "No se pudo des-registrar SQLITE3COMUTF8.dll " +chr(13)+chr(13) +; "Continuemos..." ) endif ** el "des-registro" se hace al finalizar la app; si alguno de los dos no se puede ** des-registrar, realmente no hay problema... cierto? return ( .t. ) endif wait window 'Qu pas?' &&nowait noclear ** si lleg hasta aqu, s hay problema... slo se ejecuta sin parmetro (para registrar) ** o con el parmetro U (para des-registrar)... return .f.

-8-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

FUNCTION UnregisterControl *-- Author: Paul Vlad Tatavu *-- This function unregisters an OCX/ActiveX control * or set of OCX/ActiveX controls based on the name of file. *-- Note: It works for OLE servers too. *-- Parameters: * tcFileName = the name of the file that contains * the control(s), including the path. *-- Returns: * Logical TRUE if successful, FALSE otherwise. * Also returns FALSE if the file doesn't exist. *-- Call sample: * llUnregistered = UnregisterControl("c:\windows\system\comctl32.ocx") LPARAMETERS tcFileName LOCAL llSuccess IF FILE(tcFileName) DECLARE INTEGER DllUnregisterServer IN (tcFileName) AS __DllUnregisterServer__ *-- This function returns 0 if successful llSuccess = ( __DllUnregisterServer__() = 0 ) local lnError lnError = 0 if !llSuccess DECLARE INTEGER GETLASTERROR IN WIN32API lnError = GetLastError() endif *MessageBox ( "Mirando"+chr(13)+"UnregisterControl("+tcFileName+")" + chr(13)+; transform(llSuccess) + chr(13)+transform(lnError) ) ELSE llSuccess = .F. *MessageBox ( "No existe"+chr(13)+"UnregisterControl("+tcFileName+")" ) ENDIF RETURN llSuccess ENDFUNC

** Escrib esta funcin para mostrar en "letras" el tipo de campo, ya que SQLite3COM slo me ** entrega un nmero cuando utilizo oR.Item(1).Info(FieldN), pero recordemos que con PRAGMA ** obtenemos mejor info function GetType(M.pType) M.cRet = "" do case case pcount()=0 or empty(M.pType) M.cRet = "ERROR" case M.pType = 1 M.cRet = "NULL - vbNull" case M.pType = 3
-9-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

M.cRet = "INTEGER - vbLong" case M.pType = 5 M.cRet = "NUMERIC - vbDouble" && Real case M.pType = 8 M.cRet = "TEXT - vbString" && DateTime, Date *case M.pType = 0 * M.cRet = "REAL - vbDouble" case M.pType = 8209 M.cRet = "BLOB - binary (vbArray Or vbByte)" && Picture *case M.pType = 0 * M.cRet = "??" otherwise M.cRet = "UNKNOWN < "+transform(M.pType)+" >" && Desconocido endcase return M.cRet

*---------------------------------------** Una idea interesante para visualizar datos, encontrada en Internet proc Browse_Trick_v2 ** Browse_Trick_v2.PRG =MessageBox([No me gusta, pues cae a la ventana de comandos]+chr(13); +[Uno tiene que dar click dentro del browse para volverlo activo]+chr(13); +[],0,[--],4000) RELEASE oBr private oBr use AUXILIAR shared noupdate wait window [Visualizando datos...] nowait BROWSE LAST NOWAIT NAME oBr with oBr .ReadOnly = .T. .Columns(1).Header1.Caption = "User Name" .Columns(3).Header1.Caption = "File ID" .Columns(5).Header1.Caption = "File Name" .Columns(6).Header1.Caption = "Server File Name" .ZOrder (0) .AutoFit() *wait wind 'no funcionaba .SetFocus() cuando hice pruebas' .SetFocus() endwith *----------------------------------------

*---------------------------------------** Es una idea interesante para visualizar datos y no me aguant presentrselas ** obtenida de Internet, pero no recuerdo la fuente (la buscar) PROCEDURE BrowseSetup IF NOT lBrowseSetup WITH oBrowse AS GRID .LEFT = 50 .TOP = 50
-10-

D:\Utiles\Motores\SQLite\PRUEBAS\ejemplo-vfp-accesando-sqlite.prg

Martes, 02 de Julio del 2013 02:45 a.m.

**** Row coloring -----------------------------* Only works good with no index: *.SETALL("DynamicBackColor","IIF(RECNO() % 2 = 0, 16777215, 16777088)","Column") * This one works much better .SETALL("DynamicBackColor","IIF(oBrowse.ActiveRow%2=0,16777215,16777088)","Column") *** Fix the first column ----.LOCKCOLUMNS = 1 .Columns(1).DynamicBackColor= "" .Columns(1).BackColor = RGB(255,255,168) .AUTOFIT() .HIGHLIGHTSTYLE= 2 .ALLOWCELLSELECTION = .F. .HIGHLIGHTBACKCOLOR = RGB(255,128,64) .HIGHLIGHTFORECOLOR = RGB(0,0,0) .Refresh ENDWITH lBrowseSetup = .T. ENDIF *---------------------------------------return ENDPROC **

-11-

You might also like