Está en la página 1de 32

INTRODUCCIÓN A EAGLE1.LIB llamada antes MSCLASS.

LIB

MySql es la base de datos gratuita mas popular del mundo por tanto es la alternativa
ideal para los usuarios de Fivewin para migrar sus aplicaciones al entorno cliente
servidor. La velocidad y eficiencia de MySql y la inmensa comunidad de usuarios que
tiene lo hacen ver como la opción mas estable que existe y lo único que faltaba era la clase
de Manuel Expósito EAGLE1.LIB para manejara desde nuestro código FW con el driver
nativo LIBMYSQL.DLL

Antes de que nazca EAGLE1.LIB era posible acceder a MYSQL a través de OBDC pero
el rendimiento de las aplicaciones decaía en velocidad con respecto a nuestras programas
Clipper lo cual disgusta mucho a un cliente quien no conoce las otras ventajas de la
arquitectura cliente servidor.

Nuestros GURUS nos aconsejaban que hay que sacrificar velocidad por seguridad, pues
eso señores no lo entienden nuestros clientes.

Con la fantástica clase de Manuel Expósito esa lentitud desaparece se abren tablas
enormes y se pueden mostrar en TWBROWSE o TSBROWSE al instante en LAN como
están acostumbrados a ver nuestros clientes.

Es obvio que para migrar a MYSQL primero hay que conocer MYSQL pero les digo con
sinceridad que solo esta RDBMS es la más simple y sencilla que conozco, así como
garantizo que la pueden aprender en un fin de semana.

Miren lo simple que es, nuestros dbfs se llaman tablas y estas deben estar contenidas en
una base de datos que no es otra cosa mas que un directorio dentro de la carpeta
mysql\data. Es decir si tenia una dbf llamada Asientos.Dbf debo crear una base de datos
CONTABLE a la que pertenece quedando así en el disco duro

Mysql
Data
Contable
Asientos.frm que tiene la estructura de la tabla
Asientos.myd que tiene la data
Asientos.myi que tiene el índice

Ven lo fácil que es ahora si quiero copiar mi data a otro lado solo zipeo la carpeta data y la
puede remplazar en un nuevo árbol de instalación de MySql.

Ahora para manejar los datos hay solo cuatro sentencias básicas que deben aprender
SELECT para consultar, INSERT para agregar, UPDATE para actualizar y DELETE para
borrar, es poquísima la curva de aprendizaje.

Ahora lo que si hay que aceptar es que nosotros tenemos miles de líneas de código armado
bajo el modo de manejo de base de datos con áreas de trabajo y alias y esto rompe esos
esquemas. Eso es cierto pero las distintas clases de manejo de ODBC se preocuparon por
ello y la solución que encontraron fue traer un Query de la base de datos MYSQL e
importarlo como un DBF. Pero esto hace que decaiga el rendimiento porque la dbf entera
o un pedazo de ella se debe escribir de nuevo en el disco local cada vez que se usan.
Con la clase de Manuel no se escribe nada en el disco el Query esta en nuestra RAM lista
para acceder a ella como si se tratara de una dbf. , esto se logro a través de una capa de
funciones que explicare mas adelante.

Al momento de redactar este Manual os explicare todo lo que esta funcional en la clase ya
que Manuel trabaja en varios métodos aun y me ha permitido también poder agregar
varios métodos mas en los cuales aun trabajo.

El Manual lo he organizado en las siguientes capítulos

1. Que se necesita para trabajar con EAGLE1.LIB


2. Conexión con el servidor y manejo de bases de datos
3. Como se manejan las tablas
4. Que es la capa de funciones
5. Como imprimo
6. Como ejecuto los procesos
7.
CAPITULO 1. Que se necesita para trabajar con EAGLE1.LIB

a. Se necesita HARBOUR 42 que lo pueden descargar de


www.fivetechsoft.com o XHARBOUR 0.82 o 0.81 que lo pueden descargar
de www.xharbour.org .
b. Si desean correr los ejemplos en Windows se requiere de FIVEWIN para
HARBOUR o XHARBOUR por que no corre la clase a 16 bits.
c. El servidor MySql que lo pueden descargar de www.mysql.com
d. La clase Eagle1.Lib para Harbour o XEagle1.Lib para XHabour
e. El archivo LibMysql.lib
f. El archivo LibMysql.dll

Inserte en su make o bat de preparación de su exe las rutas para enlazar las librerías
Eagle1.lib y LibMysql.lib .Si no tiene una debajo adjuntamos un bat simple donde los
árboles de Harbour y BCC están en una sola carpeta X. Luego de generar el exe en la
carpeta donde lo ejecutara copie el archivo LibMysql.dll

c:\X\bin\harbour \CPSCON\program\p06 /n /ic:\X\include /p /oc:\CPSCON\obj32\p06.c


c:\X\bin\bcc32 -M -c -O2 -Ic:\X\include -tW -oC:\CPSCON\OBJ32\p06.obj C:\CPSCON\OBJ32\p06.c

ECHO ****************************************
ECHO *** GENERANDO PRUEBA 6 ***
ECHO ****************************************
echo c0w32.obj + > b32.bc
echo C:\CPSCON\OBJ32\p06.OBJ, + >> b32.bc
echo P06.exe, + >> b32.bc
echo P06.map, + >> b32.bc
echo c:\X\lib\FiveH.lib + >> b32.bc
echo c:\X\lib\FiveHC.lib + >> b32.bc
echo c:\X\LIB\ODBC32.lib + >> b32.bc

echo c:\X\lib\rtl.lib + >> b32.bc


echo c:\X\lib\vm.lib + >> b32.bc
echo c:\X\lib\gtwin.lib + >> b32.bc
echo c:\X\lib\lang.lib + >> b32.bc
echo c:\X\lib\macro.lib + >> b32.bc
echo c:\X\lib\rdd.lib + >> b32.bc
echo c:\X\lib\dbfntx.lib + >> b32.bc
echo c:\X\lib\dbfcdx.lib + >> b32.bc
echo c:\X\lib\debug.lib + >> b32.bc
echo c:\X\lib\common.lib + >> b32.bc
echo c:\X\lib\pp.lib + >> b32.bc

rem Uncomment these two lines to use lib de Mysql


echo c:\X\lib\eagle1.lib + >> b32.bc
echo c:\X\lib\libmysql.lib + >> b32.bc
echo c:\X\lib\cw32.lib + >> b32.bc
echo c:\X\lib\import32.lib, >> b32.bc

ECHO ****************************************
ECHO *** linkeamos *.obj y Generamos *.exe ***
ECHO ****************************************

wait

c:\X\bin\ilink32 -Gn -aa -Tpe -s -x @b32.bc

P06
CAPITULO 2. Conexión con el servidor y manejo de bases de datos

Se supone que antes de correr este primer programa usted debe ya haber cargado el
servidor de MySql si no lo sabe hacer he aquí algunas líneas para ayudarlo.

Cuando instala MySql se crea una carpeta mysql y dentro de ella la carpeta bin donde
están los exes del servidor y programas clientes de MySql y la carpeta data donde creara
una carpeta por cada base de datos que cree y dentro de ellas se guardaran sus tablas.

Entonces vaya al DOS y digite


Cd \mysql\bin
Luego cargué el servidor que le llaman “mysql demonio” o “mysqld”
Mysqld-opt en el caso de estar en un sistema operativo Windows 9X
Mysqld-nt --standalone en el caso de Windows NT-XP o 2000

Listo ya podemos empezar las pruebas de conexión ahí va nuestro primer programa

//----------------------------------------------------------------------------//
// P00 Ejemplo de connexion a MySQL
//----------------------------------------------------------------------------//

function main()

local oMySql, oDb

MsgInfo( "Empiezo..." )

oMySql := TMSConnect():New()

if oMySql:lInit
MsgInfo( "Inicializado...", "Hola" )
endif

if oMySql:Connect()
MsgInfo( "Conectado", "Hola" )
oMySql:CreateDB( "MiPrueba" )

MsgInfo( MsUser( oMySql:hConnect ), "1 USER" )


MsgInfo( MsHost( oMySql:hConnect ), "2 Host" )
MsgInfo( MsDb( oMySql:hConnect ), "3 Db" )

MsgInfo( MsPasswd( oMySql:hConnect ), "4 Passwd" )


MsgInfo( str( MsPort( oMySql:hConnect ) ), "5 Port" )
MsgInfo( MsServerVersion( oMySql:hConnect ), "6 Version" )

MsgInfo( MSUNIXSOCKET( oMySql:hConnect ), "7 Unix Socket" )


MsgInfo( MSHOSTINFO( oMySql:hConnect ), "8 Host Info" )
MsgInfo( MSINFO( oMySql:hConnect ), "9 Info" )
oDb := TMSDataBase():Create( oMySql, "MiPrueba" )
oMySql:Close()
else
MsgInfo( "No hay conexion", "Hola" )
endif

return( nil )
//----------------------------------------------------------------------------//

oMySql := TMSConnect():New()
Aquí asignamos a la variable oMySql el objeto de conexión este objeto contiene un puntero que
nos devuelve el servidor para conectarnos y un dato lInit que nos indica si MySql nos dios el
puntero pata conectarnos.

if oMySql:lInit

Una vez que tenemos el puntero nos conectamos con

if oMySql:Connect()
MsgInfo( "Conectado", "Hola" )

Luego de ello podemos crear nuestra primera base de datos

oMySql:CreateDB( "MiPrueba" )

y podemos obtener datos de la conexión como el usuario el host la base de datos en curso usando
las siguientes funciones a las que debemos pasar como parámetro el manejador (handle) de la
conexión que esta como un dato de la clase oMySql:hConnect

MsgInfo( MsUser( oMySql:hConnect ), "1 User" )


MsgInfo( MsHost( oMySql:hConnect ), "2 Host" )
MsgInfo( MsDb( oMySql:hConnect ), "3 Db" )

Hay otra forma de crear base de datos y es usando la clase TMSDataBase() a la que debemos de
pasar como parámetro el objeto de conexión oMysql y el nombre de la base de datos para
asignarlo todo a un objeto oDB.

oDb := TMSDataBase():Create( oMySql, "MiPrueba" )

Finalmente cerramos la conexión con

oMySql:Close()

Ahora vamos con un segundo ejemplo


//----------------------------------------------------------------------------//
// P01 Ejemplo de connexion a MySQL
//----------------------------------------------------------------------------//

#define CRLF Chr(13)+Chr(10)


function main()
local oMySq, cTxt := "", n := 0 , i := 0
MsgInfo( "Empiezo..." )
oMySql := TMSConnect():New()
if oMySql:lInit
MsgInfo( "Inicializado...", "Información" )
endif

// Se puede usar para el Host la direccion IP o el nombre de la conexion:


// if oMySql:Connect( "127.0.0.1", "root", "lolo" )
if oMySql:Connect( "localhost" , "root" )
MsgInfo( "Conectado", "Atención" )
Muestra( oMySql:aDataBases )
oMySql:CreateDB( "ManuExpo" )
Muestra( oMySql:aDataBases )
oMySql:DestroyDB( "ManuExpo" )
Muestra( oMySql:aDataBases )
oMySql:DestroyDB( "ManuExpo" ) // Un error sabido ;-)
oMySql:Close()
else
MsgInfo( "No hay conexion", "Información" )
endif
return( nil )
En este segundo ejemplo vemos como podemos definir los datos de la conexión en el siguiente
orden Host , usuario y password
// if oMySql:Connect( "127.0.0.1", "root", "lolo" )
if oMySql:Connect( "localhost" , "root" )

En el caso de correr el ejemplo en una PC Monousuaria debemos ponerle localhost


Para saber todas las bases de datos que existen tenemos el dato aDatabases del objeto de conexión
que no es mas que un arreglo, en el ejemplo lo transferimos a una función Muestra para ver esa
información.
Muestra( oMySql:aDataBases )

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

static function Muestra( a )

local cTxt := ""


local n := len( a )
local i := 0

For i := 1 TO n
cTxt += CRLF + a[ i ]
Next

MsgInfo( cTxt, "DataBases" )

return( nil )

Para borrar una base de datos usamos el método DestroyDB del objeto de conexión oMySql
oMySql:DestroyDB( "ManuExpo" )

Ahora si quisiéramos saber las tablas que contiene una base de datos hay 2 formas la primera es
usando el objeto de conexión directamente y usando la función
MSLISTTABLES( oMySql:hConnect, " MiPrueba" )

O usando la clase TMSDatabase


oDbs := TMSDataBase():New( oMySql, "MiPrueba" )
if oDbs:Use()
Muestra( oDbs:aTables, "Tablas de miprueba" )
else
MsgInfo( "No se pudo activar MiPrueba" )
endif
CAPITULO 3. Como se manejan las tablas

Hay 2 formas de manejar las tablas

Usando la clase TMSTABLE o usando la clase TMSQUERY que es la que se usa para las
consultas también.

La idea de Manuel es crear una clase que sirva exclusivamente para manejar tablas al estilo
XBASE y esta es la clase que sirve para eso (TMSTABLE).

Como si fuera una tabla dbf esta clase trae toda la tabla para tratarla como una dbf en el cursor de
MySql, pero si su tabla es grande tardara en llegar la tabla y ya no se parecerá tanto a su dbf que
abría al instante.

Si su tabla es grande y usted necesita traer solo un pedazo entonces le recomiendo usar la clase
TMSQUERY que si permite seleccionar mediante una condición SQL llamada WHERE los
registros que deseo traer.

Bueno yo particularmente solo uso la TMSQUERY y es en esta clase que he basado todos mis
aportes a las clases de Manuel y claro como la TMSTABLE es hija de la clase TMSQUERY
hereda todos los datos y métodos implementados.

Clase TMSTABLE
Procedemos a explicar la clase con el siguiente ejemplo

#define CRLF Chr(13)+Chr(10)


#include "Common.ch"

function main()

local oMySql, oDb, oTb


local cTable := "ta199"
local aRow
local n := 0

MsgInfo( "Empiezo..." )

oMySql := TMSConnect():New()

if oMySql:Connect()
MsgInfo( "Conectado", "Hola" )

En la siguientes línea se usa el método oDbFrom para crear el objeto de la base de datos ODB y
a continuación se activa con el método oDb:Use datos
oDb := oMySql:ODbFrom( "aduana" ) // TMSDataBase():New( oMySql, "MiPrueba" )
oDb:Use()

Luego se crea el objeto de la tabla con TMStable y se asigna a oTb y a continuación lo abrimos
con oTb:Open
oTb := TMSTable():New( oMySql, cTable )
oTb:Open()

Ahí podemos hacer uso usando datos o métodos del objeto para saber ciertos datos
MsgInfo( "LEN " + str( MYRecLen( oTb:hResult ) ) )
MsgInfo( "Numero de registros: " + str( oTb:RecCount() ) )

MsgInfo( "Se han tratado " + AllTrim( str( oTb:AffectedRows() ) ) + " registros" )

MsgInfo( "Numero de campos: " + str( MSNumFields(oTb:hResult ) ) + str( MSFIELDCOUNT(oTb:hConnect) ) )


Al estilo XBase podemos usar métodos que tienen los mismo nombres de los comandos XBase
para navegar por la tablas eso si
oTb:GoTop()

Eso si cada vez que se muevan deben cargar el registro con el método Load()
aRow := oTb:Load()
MsgInfo( "LEN " + str( MyRecLen( oTb:hResult ) ) )

Muestra( aRow )
MsgInfo( aRow[ 2 ] + " - " + oTb:FldGet( 2 ) )

oTb:Go( 3 )
oTb:SetBookMark()
MsgInfo( "Marca" )
Muestra( oTb:Load(), str( 3 ) + "---" + AllTrim( str( oTb:RecNo() ) ) )

oTb:Go( 100 )
Muestra( oTb:Load(), str( 100 ) + "---" + AllTrim( str( oTb:RecNo() ) ) )

oTb:GoBookMark()
MsgInfo( "Marca" )
Muestra( oTb:Load(), AllTrim( str( oTb:RecNo() ) ) )

oTb:Go( 600 )
Muestra( oTb:Load(), str( 600 ) + "---" + AllTrim( str( oTb:RecNo() ) ) )

oTb:GoBookMark()
aRow := oTb:Load()
MyCommit( oTb:hConnect )
Muestra( aRow, AllTrim( str( oTb:RecNo() ) ) )

oTb:FreeResult()
oMySql:Close()

Aqui va otro ejemplo de la clase

#define CRLF Chr(13)+Chr(10)

function main()
local oMySql, oTb
local n := 0

cls

MsgInfo( "Empiezo..." )

oMySql := TMSConnect():New()

if oMySql:lInit
MsgInfo( "Inicializado...", "Hola" )
endif

if oMySql:Connect()
MsgInfo( "Conectado", "Hola" )
oMySql:SelectDb( "MiPrueba" )

oTb := TMSTable():New( oMySql, "alumno" )


oTb:Open()

Como pueden ver se recorre la tabla como si se tratase de una base de datos solo que hay que
poner Load() antes de leer el registro.
while !oTb:EOF()

oTb:Load()

? oTb:nRow, oTb:FldGet( 1 ), oTb:FldGet( 2 )

if ++n / 24 == 1
MsgInfo( "Presiona el botón para seguir..." )
n := 0
endif

oTb:skip()
end

while !oTb:BOF()

oTb:Load()

? oTb:nRow, oTb:FldGet( 1 ), oTb:FldGet( 2 )

if ++n / 24 == 1
MsgInfo( "Presiona el botón para seguir..." )
n := 0
endif

oTb:skip(-1)
end

MsgInfo( "Voy al registro 5" )

oTb:GoTo( 5 ):Load()
MsgInfo( "RecNo: " + str( oTb:RecNo() ) + CRLF + "Campo uno: " + oTb:FldGet( 1 ) + CRLF + "Campo dos: " +
oTb:FldGet( 2 ) )

MsgInfo( "Se termino..." )

oTb:Close()
oMySql:Close()
else
MsgInfo( "No hay conexion", "Hola" )
endif

return( nil )

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

Como podrán ver los métodos son parecidos a las funciones XBase y se heredan de TMSQUERY

METHOD FldName( n ) devuelve el nombe del campo segun el numero (n)


METHOD FldType( n ) devuelve el tipo del campo segun el numero(n)
METHOD FldLength( n ) devuelve la longitud del campo segun el numero(n)
METHOD FldDec( n ) devuelve los decimales según el numero(n)
METHOD GoTo( nRec ) te lleva el registro nRec
METHOD GoTop() te lleva al primer registro
METHOD GoBottom() te lleva la ultimo registro
METHOD Skip( n ) salta n registros
METHOD RecNo() devuelve el registro actual
METHOD EOF() devuelve .t. si es fin de archivo
METHOD BOF() devuelve .t. si es inicio de archivo
METHOD Load() carga el registro activo
METHOD FldGet( nFld ) devuelve el valor del campo según el numero nFld x
METHOD FieldCount() devuelve el Numero de campos de la tabla
METHOD RecCount() devuelve el numero de registros de la tabla
METHOD Close() libera el cursor de memoria

La clase TMSTABLE es una clase hija de la clase TMSQUERY y usa el método Open por el
momento para traer la tabla y por el momento es de solo lectura.
El orden de los registros de la tabla que esta en el cursor que maneja TMSTABLE corresponden a
los registros de la tabla en MySql por que se esta trayendo la tabla entera pero en la próxima clase
que veremos TMSQUERY el método Open también puede traer un pedazo de la base de datos en
base a la cláusula WHERE y ahí el numero de registro que manejaba TMSTABLE no
necesariamente va a ser el orden en que están en MySql.

Y es lógico, imaginen que tengo una tabla de asientos y me traigo solo los registros que
correspondan al diario 01 y asiento 002

Cursor de OPEN TABLA EM MYSQL

Reg Diario asiento cuenta Reg Diario Asiento Cuenta


1 01 002 10410 1 01 001 621012
2 01 002 121420 2 01 001 421020

3 01 002 10410
4 01 002 121420

Como pueden ver los registros en la tabla en MySql son 3 y 4 y en el cursor del método OPEN es 1
y2

Y es que la verdad es que en MYSQL no existe el numero de registro aquí se trabaja con claves
primarias. Una clave primaria es un campo que contiene un dato único en la tabla que no se puede
duplicar y que permite identificar al registro.

Muy bien, esa parece ser la solución pero en el ejemplo de arriba que dato voy a usar si el diario el
asiento y la cuenta se pueden repetir.

Esa idea solo funciona para las tablas de códigos donde si un código de cuenta, cliente no se debe
repetir jamás, pero en archivos de transacciones como se hace.

La solución me la dio el Dbf2Mysql un utilitario Ruso para migrar dbfs a MySql, este crea un
primer campo llamado ROW_ID en la tabla y te genera el número de registro que tanto necesitas.
Este número de registro es básico para poder sincronizar con el servidor en las altas bajas y
cambios y es obligatorio su uso en el caso de trabajar con el método cursor o usar la capa de
funciones para la migración rápida de código XBase.
Clase TMSQUERY
Esta clase sirve para hacer Querys al servidor MySql hasta ahora es la mas extensa e importante de
todas las clases que contiene EAGLE1.lib.

Por el momento existen tres métodos para hacer Querys.

ExecSQL( cStatement ) que hace querys y no devuelve datos ideal para hacer UPDATE , INSERT
o DELETE o cualquier otra sentencia SQL

Open( cStatement , lUseResult ) ejecuta querys que devuelven datos en un cursor nativo de
MySql que de momento es de solo lectura ideal para consultas y reportes.

Cursor( cStatement ,lUseResult ) ejecuta querys que devuelven datos en un arreglo de 2


dimensiones que permite hacer altas bajas y cambio en sincronía con en el servidor , ideal para
ingreso de datos en browses. Sobre este último tipo de cursor es que están basadas las capas de
funciones para migración rápida que explicare mas adelante.

MÉTODO OPEN

El siguiente ejemplo muestra como hacer uso de este método.

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

function p05
local oMySql, oTb, oDlg
local n := 0,inicio, objsql, tiempo

MsgInfo( "Prueba de cargado de cursor en browse 20 veces mas rápido que ODBC usando driver nativo mysql","Demo de
MSCLASS de Manuel Exposito" )

oMySql := TMSConnect():New()

if oMySql:lInit
MsgInfo( "Inicializado...", "Hola" )
endif

if oMySql:Connect()
MsgInfo( "Conectado", "Hola" )
// Selecciona la base de datos
oMySql:SelectDb( "ADUANA" )

Lo primero que hay que hacer para ejecutar un query es crear un objeto query
// Crea el objeto Query
objsql := TMSQuery():New( oMySql )
// Ejecuta el SQL
inicio:=seconds();tiempo:=time() // tomamos el tiempo

Y luego recién se puede usar cualquiera de los 3 métodos de querys existentes.

En este caso usamos el método open y pasamos como parámetro al sentencia SQL que nos permite
traer toda la tabla ta199 con todos sus campos.
objsql:Open( "SELECT * FROM ta199" )
?"resultados Empezo ",tiempo ," termino ",time(),"muchisisimo mas rapido que con ODBC hay que reconocer..."
?"No campos",objsql:nFieldCount,"No registros",objsql:nRowCount

DEFINE DIALOG oDlg FROM 3, 3 TO 30, 81 TITLE "Browse directo del cursor MYSQL "
@ 0, 1 SAY " Nada de Arrays ni dbf temporales" OF oDlg
Luego cuando hacemos el listbox para mostrarlo ponemos los fields para definirlos con el evento
bline

@ 1, 1 LISTBOX oLbx ;
FIELDS " "," "," " ;
HEADERS "RECNO","Partida", "Descripcion" ;
SIZES 30,80,800 SIZE 300,180 OF oDlg

Aca le pasamos los campos con el metodo FLDGET


oLbx:bLine:= {|| { objsql:FldGet(1),objsql:FldGet(2),objsql:FldGet(3) } }

oLbx:aJustify = { .f., .f. , .f. }

Y acá seteamos el browse para adecuar el skipper del browse


objsql:SetBrowse( oLbx , .t. )

ACTIVATE DIALOG oDlg


/* Ahora si quieren listarlo harian algo asi
While !objsql:lEof
objsql:Load()
?objsql:FldGet(1),objsql:FldGet(2),objsql:FldGet(3)
objsql:skip(1)
Enddo
*/
else
MsgInfo( "No hay conexion", "Hola" )
endif

return( nil )

Como ve es sumamente fácil, por el momento aun no esta disponible usar el nombre del Campo
como por ejemplo objsql:Partida pero se que Manuel Trabaja en eso y me preciso que va a
funcionar así.

Los métodos expuesto son los mismos que los de clase TMSTABLE ya que esta es la clase padre.

Este método es ideal para hacer consultas o reportes es tremendamente rápido de 20 a 60 veces
mas rápido que odbc. Pero actualmente tiene una limitación y es que no se puede escribir en él ni
tampoco puede agregar o eliminar, si hace un browse con el y modifica o agrega en MySql vía
sentencias sql tendría que traer el cursor nuevamente después de cada operación.

METODO CURSOR

En vista de la limitación del método anterior que solo se puede leer el cursor existe este otro
método que carga todo el cursor a un arreglo de 2 dimensiones y lo gestiona como si fuera una
base de datos, es decir que si agregan, modifica o elimina un registro al arreglo, ocurre lo mismo
automáticamente en la tabla de MySql . Esta precisión se logra a través de un campo fijo que debe
existir en todas sus tablas y que se llama ROW_ID que deber ser definido como una clave
primaria .Usted ni se preocupe cuando migra su dbf con Dbf2Mysql ese utilitario se lo crea
automáticamente.

El dato mas importante de este meto es claro el arreglo que contiene el cursor.

Se llama aDbf y su numero de registro se controla a través de una variable llama nAt y como es
lógico tiene métodos parecidos para su manejo al cursor nativo.

// Metodos de navegacion del arreglo


METHOD _Go( nRec ) va al registro nRec igual que pones obejto:nAt:=nRec
METHOD _GoTop() va al registro 1 igual que poner objeto:nAt:=1
METHOD _GoBottom() va al registro final igual que poner objeto:nAt:=len(::aDbf)
METHOD _Skip( n ) va al siguiente registro igual a poner objeto:nAt+=n
METHOD _RecNo() devuelve el valor del registro en curso igual que poner objeto:nAt
METHOD _EOF() es igual que poner objeto:nAt > len(objeto:aDbf) )
METHOD _BOF() es igual que poner objeto:nAt < 1
METHOD _RecCount() es igual que poner len(objeto:aDbf)

Veamos como funciona


//----------------------------------------------------------------------------//
// Ejemplo de connexion con TMSQUERY
// compilando con xharbour 0.82 Free
//----------------------------------------------------------------------------//

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

Static inicio

function p05
local oMySql, oTb, oDlg
local n := 0,objsql,tiempo
local oget1,oget2,oget3,oget4,oget6
local cIp:= "localhost"
local cuser:= "jose "
local cpassw:=""
local cbase:= "ADUANA"
local ctabla:="TA199"
Local cWhere:=" "
oMySql := TMSConnect():New()
if oMySql:lInit
MsgInfo( "Inicializado p06...", "Hola" )
endif

DEFINE DIALOG oDlg FROM 3, 3 TO 25, 58 TITLE "Xharbour Prueba de ocursor con autofill "
@ 0, 2 SAY "Server IP:" OF oDlg
@ 0, 6 GET oGet1 VAR cIp OF oDlg SIZE 40, 10
@ 1, 2 SAY "User:" OF oDlg
@ 1, 6 GET oGet2 VAR cuser OF oDlg SIZE 40, 10
@ 2, 2 SAY "Password:" OF oDlg
@ 2, 6 GET oGet3 VAR cpassw OF oDlg SIZE 40, 10
@ 3, 2 SAY "Base Datos:" OF oDlg
@ 3, 6 GET oGet4 VAR cbase OF oDlg SIZE 40, 10
@ 4, 2 SAY "tabla:" OF oDlg
@ 4, 6 GET oGet5 VAR cTabla OF oDlg SIZE 40, 10
@ 5, 2 SAY "Where:" OF oDlg
@ 5, 6 GET oGet6 VAR cwhere OF oDlg SIZE 40, 10

@ 6, 7 BUTTON "&Ok" OF oDlg SIZE 30, 12 ACTION vamos(oDlg,oMySql,cip,cuser,cpassw,cbase,cwhere,ctabla)


@ 6, 16 BUTTON "&Cancel" SIZE 30, 12 OF oDlg ACTION oDlg:End() CANCEL
ACTIVATE DIALOG oDlg CENTERED
retu .t.

Func vamos( oDlg,oMySql,cip,cuser,cpassw,cbase,cwhere,ctabla )


Local objsql
if oMySql:Connect( alltrim(cip), alltrim(cuser) , cpassw )
oMySql:SelectDb( ALLTRIM(cbase) )
// Crea el objeto Query
objsql := TMSQuery():New( oMySql )
// Ejecuta el SQL

En esta parte es idéntico a otros métodos, se pasa como parámetro la sentencia SQL
if empty(cwhere)
objsql:Cursor( "SELECT * FROM "+alltrim(ctabla) )
else
objsql:cursor( "SELECT * FROM "+alltrim(ctabla)+" WHERE "+alltrim(cWhere) )
endif

DEFINE DIALOG oDlg FROM 3, 3 TO 30, 81 TITLE "Xharbour 0.82 Browse directo del cursor MYSQL "
@ 0, 1 SAY " Con Arrays" OF oDlg
@ 1, 1 LISTBOX oLbx ;
FIELDS " "," "," "," " ;
HEADERS "nRow","Campo 1","Campo 2","Campo 3" ;
SIZES 30,30,500,80 SIZE 300,180 OF oDlg

Aquí es donde varia, hay que mostrar el arreglo aDbf contenido en el objeto objsql
oLbx:bLine:= {|| {
str(objsql:aDbf[oLbx:nAt][1]),objsql:aDbf[oLbx:nAt][2],objsql:aDbf[oLbx:nAt][3],objsql:aDbf[oLbx:nAt][4] } }
oLbx:aJustify = { .f. , .f., .f. , .f. }

y luego setearlo en le browse como cualquier arreglo mas


oLbx:SetArray( objsql:aDbf )
ACTIVATE DIALOG oDlg
else
endif
else
MsgInfo( "No hay conexión", "Hola" )
endif
return( nil )

Ahora bien eso es para mostrar el cursor y la pregunta es como se hacen las altas, bajas y cambios

METHOD AddNew() para agregar un registro


METHOD Edit() para iniciar el modo de edición
METHOD FieldGet( nField ) para leer un dato
METHOD FieldPut( nField, uValue ) para grabar un dato conociendo su numero de campo
METHOD FieldPos( cField ) para saber el nombre de un campo en base a su posición
METHOD Update sirve catalizar los datos a la tabla MySql
METHOD Delete() para borrar un registro

Yo soy usuario de la clase TDBODBCIRECT la compre en Junio del 2002 a OZS cuando estaba
aun con FiveTech y he trabajado la migración de 2 sistemas con esa clase pero para ello he tenido
que implementar los métodos para manejo de cursores en arreglos por que la original trabaja solo
con bases temporales y en la clase TMSQUERY he hecho lo mismo le tuve que poner los mismos
nombres que los de la clase de Ignacio para evitar modificar los métodos en todos los fuentes del
sistema.

De esa manera logre que mi sistema corra con las 2 clases es por ello que en el ejemplo contable
que les deje uno puede elegir entre conexión nativa MySql o conexión ODBC que es la que usa la
clase de Ignacio.

Estos métodos los implemente con una lógica de funcionamiento parecida a la de la clase que uso,
como les repito para poder hacer funcionar mis programas con ambas clases.

La lógica es esta, cuando se agrega un registro se usa el método AddNew e inmediatamente


después se proceder a poner los valores de los campos en este caso yo lo hago con el método
FieldPut, y finalmente cuando se aplica el método UPDATE la clase verifica que se adiciono un
registro mas en el arreglo y procede a ejecutar una sentencia SQL INSERT con la colección de
campos que se registraron en la clase de acuerdo a cuantos FielPut halla puesto antes del método
UPDATE .
Ejemplo

NEWROWID:=20 // le digo que el nuevo registro va a ser el 20


objsql:AddNew() // agrego el nuevo registro, solo agrega en el arreglo aDbf
objsql:FIELDPUT(1,NEWROWID) // le pone el NewRowId al registro agregado en el arreglo
objsql:Update() //aca recien se inserta el registro en la tabla de MySql

Para modificar datos la lógica parecida, primero hay que activar el modo de edición

Con el método Edit y luego con el fielput hacer los cambios para finalmente poner usar el método
Update para actualizar la tabla en MySql

Por ejemplo en el caso anterior hemos poder modificar en ese nuevo registro los campos 2, 3 y 4
primero campos
Obsql:Edit()
Obsql:FielPut(2,”01” ) // Nuevo diario
Obsql:FielPut(3,”001” ) // Nuevo asiento
Obsql:FielPut(4,”121002” ) // Nueva cuenta
objsql:Update() // Aca recién se actualiza el registro en la tabla de MySql

Para borrar es mas simple se ubica en el registro que desean borrar y luego aplican el método
Delete.

objsql:Delete() // y listo borra el registro tanto de MySql como del arreglo


CAPITULO 4. QUE ES LA CAPA DE FUNCIONES

La capa de funciones es una galería de código con la que yo he podido migar rápido mi código y
que ofrecí cederla a la comunidad .Este capa la utilizo con el método cursor y la uso para hacer
altas bajas y cambios con la tablas en general . Esta capa me ha permitido adaptar todo mis
aplicaciones velozmente a la clase TMSQUERY ya que mi código usa esta capa y no los métodos
directos de las clases TDBODCDIRECT y TMSQUERY de manera que si quisiera mas adelante
usar OLE NATIVO para acceder a ADO no haría mas que adaptar esta galería y todos mis
aplicaciones accederían a ADO vía OLE nativo sin tocar una solo línea de código.

La razón fundamental de esto es que las bases de datos se trabajan con áreas y alias y los objetos
cursores no, pues la solución es simple asociar los objetos cursores a áreas que se invocarían con
SELECT como siempre.

Así se migraría un programa simplemente remplazando los comando clipper por funciones e
inclusive si modifica el preprocesador no tendrían que ni tocar código.

En clipper Migrado para que trabajo con dbf y mysql

Local nwcod:=”0001” Local nwcod:=”0001”


SETMYSQL()
SELE A SELE A
USE EMPLEADOS SHARED VZUSE_(“EMPLEADOS”)
If ¡dbseek( nwcod ) IF !vzdbseek({nwcod},{“COD”})
APPE BLAN VZAPPEND({nwcod},{“COD”})
REPL CODIGO WITH nwcod
endif ENDIF
if rlock() if VZRLOCK()
REPL FECHA WITH DATE() VZREPL(“FECHA”,DATE() )
REPL HORA WITH TIME() VZREPL(“HORA”,TIME() )
REPL SUELDO WITH 10.00 VZREPL(“SUELDO”,10.00)
REPL ESTADO WITH “ASISITIO” VZREPL(“ESTADO”,”ASISTIO”)
COMMIT VZCOMMIT()
UNLO VZUNLO()
Endif Endif
GO TOP VZGOTOP()
WHILE !EOF() WHILE ¡EOF()
?A->FECHA,A->HORA,A->SUELDO,A->ESTADO ?VZ(“A->FECHA”),VZ(“A->HORA”),VZ(“A->SUELDO”)
IF EMPTY(A->CODIGO) IF EMPTY(VZ(“A->CODIGO”))
DELE RECO RECNO() VZDELE()
ENDIF ENDIF
SKIP VZSKIP()
ENDDO ENDDO

Como ven esto es fácil para migar y lo puedes encargar a otra persona, en mi caso se lo encargué a
una practicante de otro lenguaje e hizo la migración perfectamente por que no hay que cambiar
lógica solo remplazar.
Pero como es que funciona esto muy simple se asocian los objetos a un arreglo que a su vez esta en
paralelo a otro arreglo que guarda todas las áreas con las que se trabaja.

Cuando ponen USE en clipper abre la tabla en cambio VZUSE solo agrega al arreglo que controla
las áreas el nombre de la tabla para que sepa que pertenece a esa área.

Recién cuando se pone vzdbseek se trae el cursor para manejar la tabla a diferencia del dbseek
que ubica el registro en una tabla ya abierta. Por lo tanto si ustedes quieren traer toda la tabla
deben usar vzdbseek() con argumento blanco después del vzuse

En clipper En mysql
USE EMPLEADOS vzUSE(“EMPLEADOS”)
GO BOTTOM vzGOBOTTOM()
WHILE ¡BOF() WHILE ¡BOF()
?A->FECHA,A->HORA,A->SUELDO,A->ESTADO ?VZ(“A->FECHA”),VZ(“A->HORA”),VZ(“A->SUELDO”)
SKIP(-1) vzskip(-1)
ENDDO ENDDO

El vzdbseek trabaja con 2 argumentos que deben ser pasados como arreglos , primero va lo que se
busca y luego el campo donde se busca , claro esta que si se buscan en jun campo compuesto solo
habria que agregar los datos y los campos sobre las que se ha de efectuar la búsqueda.

El vzappend agrega in registro y graba los datos de la clave de búsqueda en el nuevo registro
agregado , si solo desea agregar el registro use vzappend con argumento en blanco.

El vzrlock pone en modo de edición el registro en mysql no lo bloquea , lo que sucede es que este
codigo es híbrido es decir sirve para ejecutar el mismo codigo con una base de datos dbf o con un
tabla mysql Como el codigo esta debajo le recomendamos mejor si no va aser híbrido cambiar el
nombre de esta funcion por otro mas apropiado por ejempo vzedit()

Los vzrepl graban los datos en el arreglo y posteriormente cuando se llega a vzcommit se ejevuta
el metodo Update() que graba todos los vzrepl en la tabla de mysql.

El vzunlo no hace nada en mysql pero en xbase si cumple la funcion de desbloquear.

A continuación procedemos a explicar las funciones de la galeria

static areasql,tablasql,dbfsql,objsql,isql,oOdbc,cDsn,sqlwhere

/////////////////////////////////////////////////////////////////////////////////////
// CAPA DE FUNCIONES PARA MIGRAR A SQL USANDO ENFOQUE XBASE
// TRABAJA CON TDBODBCDIRECT CON ODBC Y TMSQUERY CON DRIVER NATIVO
MYSQL
//Actualizado 13/11/2002 23/03/2003 trabaja sin dbf temporal POR FIN
/
/////////////////////////////////////////////////////////////////////////////////////
// ++NATIVO 02/09

FUNC setmysql()
// ARREGLOS PARA ASOCIAR OBJETOS QUERYS A AREAS
areasql:={};dbfsql:={};tablasql:={};objsql:={};isql:=1;jcs:={};sqlwhere:={}
retu .t.

// Asociar un query a un area


Func vzuse_(dbf,indice,comparte,orden,nuevo,alias,driver,sololee)
Local uisql,j
if empty(cDsn)
if empty(dbf)
use
else
use_(dbf,indice,comparte,orden,nuevo,alias,driver,sololee)
endif
else
if (uISQL:=ascan( areasql,select() ) )=0
if !empty(dbf)
aadd(areasql,select())
aadd(dbfsql,dbf)
aadd(tablasql,solotabla(dbf))
aadd(objsql,"")
aadd(sqlwhere,"")
endif
else
if empty(dbf)
if valtype(objsql[uISQL])="O"
objsql[uISQL]:End()
endif
zadel(areasql,uISQL)
zadel(dbfsql,uISQL)
zadel(tablasql,uISQL)
zadel(objsql,uISQL)
zadel(sqlwhere,uISQL)
else
//?dbf,"el area¨",select()," ya esta siendo usada y esta en la posicion ",uISQL
areasql[uISQL]:=select()
dbfsql[uISQL]:=dbf
tablasql[uISQL]:=solotabla(dbf)
objsql[uISQL]:=""
sqlwhere[uISQL]:=""
//for xxx= 1 to len(AREASQL)
//?xxx,"como queda despues del vzUSE
",AREASQL[xxx],dbfsql[xxx],tablasql[xxx],objsql[xxx]
//next xxx

endif
endif
endif
/*
xx:=""
for j=1 to len(areasql)
xx +="Pos. "+ltrim(str(j))+" Area "+ltrim(str(areasql[j]))+" Tabla "+tablasql[j]+chr(13)
next j
?xx
*/
retu .t.

// ++nativo 02/09
//23/03/2003
// PONER REGISTRO EN MODO DE EDICION
Function vzrlock //Remplazar
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
While !rlock()
if !MsgYesNo('No puedo grabar por que el registro '+ltrim(str(recno()))+' de la dbf '+dbf()+' del
area '+ltrim(str(select()))+', esta bloqueado.Otro usuario esta usando el registro en este instante o
se abrio la dbf con dbu,dbase o Fox.Reintentar',;
'Pulse No para Abandonar el Sistema ')
CLOSE ALL
quit
end
Enddo
ELSE
objsql[uISQL]:Edit()
ENDIF
retu( .t. )
// ++nativo 02/09
// ASIGNAR DATOS A CAMPOS
//23/03/2003
FUNC VZREPL(CAMPO,VALOR)
Local uISQL,n
CAMPO:=UPPE(alltrim(CAMPO))
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
REPL &CAMPO WITH VALOR
ELSE
if (n:=AT("->",campo))>0;campo:=subs(campo,n+2);end
//MSGSTOP(objsql[uISQL]:FieldPos(CAMPO),"VZREPL FIELDPOS DE "+CAMPO)
objsql[uISQL]:FieldPut( mifieldpos(objsql[uISQL],campo) ,VALOR )
ENDIF
RETU .T.

// ++nativo 02/09
// HACE EL UPDATE Y GRABA LOS DATOS
//23/03/2003
FUNC VZCOMMIT // NO HACE NADA EN C/S
Local uISQL
//for j=1 to len(areasql)
//?"EXAMINADO EN VZCOMMIT ",J,areasql[j],tablasql[j],objsql[j],dbfsql[j]
//next j
//?"CUAL ES EL AREA ",SELECT(),ASCAN( AREASQL,Select() ),VZASCAN(
AREASQL,Select() )
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
COMMIT
else
// ?"QUE VALE GenUpdate
UISQL",objsql[uISQL]:GenUpdate(),empty(objsql[uISQL]:GenUpdate())
if !empty(objsql[uISQL]:GenUpdate())
//?"Actualiza"
objsql[uISQL]:Update()
else
//?"NO Actualiza"
objsql[uISQL]:nEditRecord := 0
endif
ENDIF
RETU .T.

// ++nativo 02/09
//23/03/2003
FUNC VZUNLOCK // NO HACE NADA EN C/S
IF ASCAN( AREASQL,Select() )=0 .or. empty(cDsn)
UNLOCK
ENDIF
RETU .T.

func vzunlo
retu(VZUNLOCK())

// ++nativo 02/09
//23/03/2003
// ELIMINACION
FUNC VZDELE() // CADA UNO A SU MODO
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
DELE RECO RECNO()
ELSE
objsql[uISQL]:Delete()
ENDIF
retu .t.

// ++nativo 02/09 OK
//23/03/2003
FUNC VZSKIP(N)
Local uISQL
IF VALTYPE(N)="U"
N:=1
ENDIF
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
DBSKIP(N)
ELSE
if valtype( oMysql )="O"
objsql[uISQL]:_Skip(N)
else
objsql[uISQL]:Skip(N)
endif
ENDIF
retu .t.
// ++nativo 02/09 OK
//23/03/2003
FUNC vzGoTop()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
GO TOP
ELSE
if valtype( oMysql )="O"
//?"LINEA 2260 ",valtype(objsql[uISQL]),objsql[uISQL]:classname()
objsql[uISQL]:_GoTop()
else
objsql[uISQL]:GoTop()
endif
ENDIF
retu .t.
// ++nativo 02/09
//23/03/2003
FUNC vzGoBottom()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
GO BOTTOM
ELSE
if valtype( oMysql )="O"
objsql[uISQL]:_GoBottom()
else
objsql[uISQL]:GoBottom()
endif
objsql[uISQL]:nAt:=len(objsql[uISQL]:aDbf)
// ?"despues del gobottom nAt vale ",objsql[uISQL]:nAt
ENDIF
retu .t.

// nativo ++ 02/09
//23/03/2003
func vzGoto( nRecno )
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
dbgoto( nRecno )
else
if valtype( oMysql )="O"
objsql[uISQL]:_GoTo( nRecno )
else
objsql[uISQL]:GoTo( nRecno )
endif
ENDIF
retu .t.

//nativo ++ 02/09
//23/03/2003
func vzGo( nRecno )
vzGoto( nRecno )
retu .t.

// nativo ++ 02/09
//23/03/2003
func vzRecno()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(Recno())
else
if valtype( oMysql )="O"
RETU( objsql[uISQL]:_Recno() )
else
RETU( objsql[uISQL]:Recno() )
endif
ENDIF

// nativo ++ 02/09 OK
//23/03/2003
func vzEof()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(Eof())
else
if valtype( oMysql )="O"
//?objsql[uISQL]:Classname(),objsql[uISQL]:_Eof(),valtype(objsql[uISQL]:_Eof() )
RETU( objsql[uISQL]:_Eof() )
else
RETU( objsql[uISQL]:Eof2() )
endif
ENDIF

// nativo ++ 02/09
//23/03/2003
func vzBof()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(Bof())
else
if valtype( oMysql )="O"
RETU( objsql[uISQL]:_Bof() )
else
RETU( objsql[uISQL]:Bof() )
endif
ENDIF

//nativo ++ 02/09
//23/03/2003
func vzRecCount()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(RecCount())
else
if valtype( oMysql )="O"
RETU( objsql[uISQL]:_RecCount() )
else
RETU( objsql[uISQL]:RecCount() )
endif
ENDIF

//nativo ++ 02/09
//23/03/2003
func VZFieldName(nField )
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(FieldName(nField))
else
if valtype( oMysql )="O"
RETU( objsql[uISQL]:FldName(nField) )
else
RETU( objsql[uISQL]:FieldName(nField) )
endif
ENDIF

//nativo ++ 02/09
//23/03/2003
FUNC VZ(campo,area)
Local uISQL,buf:=select(),resu,n,resucam,compo:=campo
local hWnd := GetActiveWindow()
campo:=uppe(alltrim(campo))
if empty(area);area:=select();end
if (n:=AT("->",CAMPO) )>0 ;area:=subs(campo,1,n-1);campo:=subs(campo,n+2)
area:=uppe(area)
//msgstop(area,"cual es el area")
//msgstop(ASC(area),"cual es el ASC(area)")

if (ASC(area)>=65 .and. ASC(area)<=90) .OR. VAL(AREA)>0


IF VAL(AREA)>0 ;area:=val(area)
ELSE
// MSGSTOP( asc(area)-64 ,"asc(area)-64")
area:=asc(area)-64
ENDIF
endif
// msgstop(area ,"al final el area")

//for j=1 to len(areasql)


//?"EXAMINADO EN VZ ",J,areasql[j],tablasql[j],objsql[j],dbfsql[j]
//next j

//vzachoice(0,0,24,79,AREASQL)

IF (uISQL:=ASCAN( AREASQL,area ) )=0 .or. empty(cDsn)


select(area)
//?'cual es el dichoso campo ',campo,' area ',area
resu:=&compo //FieldGet( Fieldpos(uppe(campo)) )
select(buf)
RETU( resu )
else
// ?"bestia no es ",area,"si no areasql[uISQL] ",areasql[uISQL]
select(areasql[uISQL])
//?"campo",campo

//?objsql[uISQL]:fieldname(1),objsql[uISQL]:fieldname(2)//,objsql[uISQL]:fieldname(3),objsql[u
ISQL]:fieldname(4),objsql[uISQL]:fieldname(5),objsql[uISQL]:fieldname(6)

//?objsql[uISQL]:fieldname(1),objsql[uISQL]:fielname(2),objsql[uISQL]:fieldname(3),objsql[uIS
QL]:fielname(4),objsql[uISQL]:fieldname(5),objsql[uISQL]:fielname(6)
//?mifieldpos(objsql[uISQL],campo,area),"posicion ok"

//MSGSTOP(objsql[uISQL]:FieldPos(CAMPO),"VZ FIELDPOS DE "+CAMPO)


mifi:=mifieldpos(objsql[uISQL],campo,area)
//?"CUAL ES LA POSICION CARAJu",mifi

//?objsql[uISQL]:fieldname(1),objsql[uISQL]:fieldname(2)//,objsql[uISQL]:fieldname(3),objsql[u
ISQL]:fieldname(4),objsql[uISQL]:fieldname(5),objsql[uISQL]:fieldname(6)
//?"el valor de :nAt",objsql[uISQL]:nAt
if mifi=0
msgstop("ATENCION YARIS INFORMA LA POSICION ES "+LTRIM(STR(mifi)),campo)
resucam:=""
else
IF LEN( objsql[uISQL]:aDbf )>0 // HAY FILAS
IF LEN(objsql[uISQL]:aDbf[1])>0 // HAY COLUMNAS
resucam:=objsql[uISQL]:aDbf[objsql[uISQL]:nAt,mifi]
ELSE
SetWindowText(hWnd, "ERROR COLUM campo = "+campo+" mifi = "+ltrim(str(mIFI)) )
ENDIF
ELSE
SetWindowText(hWnd, "ERROR FILA campo = "+campo+" mifi = "+ltrim(str(mIFI)) )
ENDIF

endif
//resucam:=objsql[uISQL]:FieldGet( mifieldpos(objsql[uISQL],campo,area) )
////////////// VOTAR ESTO CUANDO LA DBF NO TENGA NIL
if valtype(resucam)="U" .and. valtype(oMysql)="U"
sele 99;use_(dbfsql[uISQL],.F.)
resu:="99->"+solocompo(compo)
resucam:=&RESU
Do case
case valtype(resucam)="D";resu:=ctod(" / / ")
case valtype(resucam)="N";resu:=resucam*0
otherwise
resu:=space(len(resucam))
Endcase
resucam:=resu
use
endif
////////////////////////////////////////////
select(buf)
RETU( resucam )
ENDIF
else
resu:=&compo
RETU( resu )
end

//nativo ++0209

FUNC SOLOCOMPO(compo)
retu( subs(compo,at("->",compo)+2 ) )
// nativo 09/02
//23/03/2003 REVISARLO DEBE TRABAJAR CON BIEN
// debe devolver el fielpos del campo dependiendo de su alias
// tres funciones de apoyo para el mostrado del browse
// estas 2 primeras sirven para formar el codebloc con los datos
func danumcampo(campo)
Local uISQL,buf:=select(),resu,n,alias:=ltrim(str(select())),area:=select()
Local resucam
n:=AT("->",CAMPO)
area:=subs(campo,1,n-1);alias:=area;campo:=subs(campo,n+2)
area:=uppe(area)
if ASC(area)>=65 .and. ASC(area)<=90 ;area:=asc(area)-64
else ;area:=val(area)
endif
select(area)
uISQL:=ASCAN( AREASQL,area )
//?"en danumcampo area,uISQL",area,uISQL
//MSGSTOP(objsql[uISQL]:FieldPos(CAMPO),"DAnumcampo FIELDPOS DE "+CAMPO)
resucam := alltrim(str( mifieldpos(objsql[uISQL],campo) ))
select(buf)
RETU(resucam )
// nativo 09/02
Func daarea(campo)
Local n:=AT("->",CAMPO) ,area
area:=subs(campo,1,n-1);campo:=subs(campo,n+2)
area:=uppe(area)
if ASC(area)>=65 .and. ASC(area)<=90 ;area:=asc(area)-64
else ;area:=val(area)
endif
retu( ltrim(str(area)) )
//nativo++ 09/02
// para que muybien muestre finalmente el browse
func muybien(menun16,Folder,FoldPag,col,area)
Local olbx:=dameobj(menun16,Folder,FoldPag),dato:="",_bmulti
Local uISQL:=ASCAN( AREASQL,area ),mascara:=""
//local hWnd := GetActiveWindow()
// _bmulti:=olbx:aMulti; mascara:=_bmulti[col,3]
//SetWindowText(hWnd, "valtype(olbx)="+valtype(olbx)+" uISQL>0 "+ltrim(str(uISQL)) )

if valtype(olbx)="O" .and. uISQL>0


IF LEN(objsql[uISQL]:aDbf)>=oLbx:nAt // La Fila existe
IF LEN(objsql[uISQL]:aDbf[1])>=col // La columna existe
dato:=objsql[uISQL]:aDbf[oLbx:nAt][col]
ENDIF
ENDIF //transform(dato,'99,999,999.99')
//msgStop(mascara,ncolAct)
// SetWindowText(hWnd, "mascara="+mascara+" ncolAct "+ltrim(str(olbx:ncolAct)) )

// if empty(mascara)
//if valtype(dato)="N" ; retu( transform(dato,'99,999,999.99') )
if valtype(dato)="D" ; retu( dtoc(dato) )
elseif valtype(nativo())="O" .and. valtype(dato)="N"
if objsql[uISQL]:FldType(col)=0
retu( transform(dato,replic('9',objsql[uISQL]:FldLength(col)-2
)+"."+replic('9',objsql[uISQL]:FldDec(col) ) ) )
else ;retu( transform(dato,'99,999,999.99') )
endif
elseif valtype(nativo())!="O" .and. valtype(dato)="N"
retu( transform(dato,'99,999,999.99') ) // odbc
else ; retu( dato )
endif
// else
// retu( transform( dato , mascara ) )
// endif
else
retu ""
endif

//nativo++ 02/09
//23/03/2003
func vzFieldGet(nField )
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
RETU(FieldGet(nField))
else
RETU( objsql[uISQL]:FieldGet(nField) )
ENDIF
//nativo ++ 02/09
//23/03/2003
func vzFieldPut(nField, uValue)
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
FieldPut(nField, uValue)
else
objsql[uISQL]:FieldPut(nField, uValue)
ENDIF
retu .t.

// CAMBIO DE INDICE
func VZDBSETORDER(n) // CADA UNO A SU MODO
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
DBSETORDER(n)
ENDIF
retu .t.
//nativo ++ 09/02
//////////////////////////////////////////////////////////////////////////////////
//23/03/2003
FUNC
VZDBSEEK(aValores,aCampos,lhaydbf,fields,OBY,xStruLen,xStruDec,SUMARIO,NLIKE,LIM
ITE )
Local
uISQL,oc,j,cWHERE:="",cORDERBY:="",BUSCA:="",aStru,xstru,aStruLen:={11},aStruDec:={
0}
Local buf:=select(),objdir
Local DISTINCT:=""
Local vLIKE:=" = "
Local LIMI:="",oTabla
// 13/05/2003 caso de evert
if !empty(aCampos) // el campo esta lleno
if empty(aValores) // pero los valores no y sale error
retu .f.
endif
endif
IF VALTYPE(LIMITE)="N"
LIMI:=" LIMIT "+ALLTRIM(STR(LIMITE))
ENDIF
IF VALTYPE(lhaydbf)="U";lhaydbf:=0;end
// lhaydbf 0 ahora no crea base de datos el 0 y 2 son iguales
// lhaydbf 1 no crea dbf y no chanca objeto sql usa objdir aparte
// lhaydbf 2 no crea base de datos y chanca usa objsql

if valtype(NLIKE)!="U"
if NLIKE=1 ; vLIKE:=" LIKE "
else ; vLIKE:=" NOT LIKE "
endif
end
if valtype(SUMARIO)!="U";DISTINCT:="DISTINCT ";end
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
if valtype(aValores)="U";go top
else
For j=1 to len(aValores);BUSCA+=aValores[j];next j
RETU( DBSEEK( BUSCA ) )
endif
else
if lhaydbf=1 ; aStruLen:=NIL ;aStruDec:=NIL
else //;use_(dbfsql[uISQL],.f.) ;aStru:=dbstruct()
If valtype(OBY)="N"
OBY:=LTRIM(STR(OBY+1))
endif
/* //por fin la erradique
if valtype(xStruLen)="U"
for xStru=1 to len(aStru)
aadd(aStruLen,aStru[xStru][3] ) ; aadd(aStruDec,aStru[xStru][4] )
next xStru
else ; aStruLen:=xStruLen;aStruDec:=xStruDec
endif
*/
//solucion facil aca//IF VALTYPE(aCampos)="U";acampos:={fieldname(1)};ENDIF
IF VALTYPE(aCampos)="U"
if valtype(nativo())="O"
oTabla:=vzquery("SELECT * FROM "+tablasql[uISQL]+" LIMIT 1" )
oTabla:Load() ;acampos:={ oTabla:FldName(2) }
else
oTabla:=OBJvzquery("SELECT * FROM "+tablasql[uISQL]+" LIMIT 1" )
acampos:={ oTabla:FieldName(2) }
endif
ENDIF
//use // NO A LA DEPENDENCIA DBF
endif
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
IF valtype(aValores)="U"
IF lhaydbf=1
msgstop("caso especial no se guarda objeto","Ajaaaaaaaaa")
objdir:=OdbcQuery( "SELECT ROW_ID FROM " +tablasql[uISQL] )
else
IF EMPTY(OBY) ;cORDERBY:=""
ELSE ;cORDERBY:=" ORDER BY "+OBY
ENDIF
IF VALTYPE(FIELDS)="U";objsql[uISQL]=OdbcQuery( "SELECT "+DISTINCT+"* FROM
" +tablasql[uISQL]+cORDERBY+LIMI,aStruLen,aStruDec )
ELSE ;objsql[uISQL]=OdbcQuery( "SELECT "+DISTINCT+FIELDS+" FROM "
+tablasql[uISQL]+cORDERBY+LIMI,aStruLen,aStruDec )
ENDIF
endif
ELSE ///////////////////////////////////////////////
For j=1 to len(aValores)
if valtype(aValores[j])=="C" ;aValores[j]:=CHR(34)+aValores[j]+CHR(34)
elseIF valtype(aValores[j])=="N" ;aValores[j]:=LTRIM(STR(aValores[j]))
elseIF valtype(aValores[j])=="D" ;aValores[j]:=dtoc(aValores[j])
else ;aValores[j]:=""
endif
If j>1;vLIKE:=" = ";end
IF EMPTY(aValores[j])
//MSGSTOP("ESTA CREANDO WHERE CON CAMPO SIN VALOR ","EVERT
CUIDADO")
RETU .F.
ELSE
if j=len(aValores); cWHERE+=aCampos[j]+vLIKE+aValores[j] ; cORDERBY+=aCampos[j]
else ;cWHERE+=aCampos[j]+vLIKE+aValores[j]+" AND ";cORDERBY+=aCampos[j]+","
endif
ENDIF
next j
IF !EMPTY(OBY) ;cORDERBY:=OBY; ENDIF
IF lhaydbf=1 ; objdir:=OdbcQuery( "SELECT ROW_ID FROM " +tablasql[uISQL]+"
WHERE "+cWHERE+" ORDER BY "+cORDERBY )
else
IF VALTYPE(FIELDS)="U"
objsql[uISQL]=OdbcQuery( "SELECT "+DISTINCT+"* FROM " +tablasql[uISQL]+"
WHERE "+cWHERE+" ORDER BY "+cORDERBY+LIMI ,aStruLen,aStruDec)
else
objsql[uISQL]=OdbcQuery( "SELECT "+DISTINCT+FIELDS+" FROM "
+tablasql[uISQL]+" WHERE "+cWHERE+" ORDER BY "+cORDERBY+LIMI
,aStruLen,aStruDec)
endif
endif //ROW_ID
ENDIF
sqlwhere[uISQL]:=cWhere
/////////////////////////////////////////////////////////
if valtype( oMysql )="O"
IF lhaydbf=1
if objdir:nRowCount=0; retu(.f.)
else ; retu(.t.)
endif
else
if objsql[uISQL]:nRowCount=0 ; retu(.f.)
else ; retu(.t.)
endif
endif
else
IF oOdbc:IsError()
oOdbc:ShowErrorList();oOdbc:aErrors := {};oDbf:End()
RETURN .F.
ENDIF
IF lhaydbf=1
objdir:lhaydbf := .f. ; objdir:lDateAsStr := .f.
objdir:Open() ; objdir:Complete() ; objdir:Gotop()
IF len(objdir:adbf)=0 ;retu(.f.)
else ;retu(.t.)
endif
else
objsql[uISQL]:lhaydbf := .f. ; objsql[uISQL]:lDateAsStr := .f.
objsql[uISQL]:Open() ; objsql[uISQL]:Complete() ; objsql[uISQL]:Gotop()
IF len(objsql[uISQL]:adbf)=0 ;retu(.f.)
else ;retu(.t.)
endif
endif
endif

Endif
select(buf)
retu .t.
//nativo
//23/03/2003
// ORSCOPE NO HACE NADA EN CLIENTE SERVIDOR
FUNC VZORDSCOPE(N,VALOR) //
// SUPUESTAMENTE ESTO SE APLICA SOBRE UN AREA EN CURSO CON VZELECT
IF ASCAN( AREASQL,Select() ) =0 .or. empty(cDsn)
ORDSCOPE(N,VALOR)
ENDIF
RETU .T.
//23/03/2003 //nativo
FUNC VZindexord() //
// SUPUESTAMENTE ESTO SE APLICA SOBRE UN AREA EN CURSO CON VZELECT
IF ASCAN( AREASQL,Select() ) =0 .or. empty(cDsn)
indexord()
ENDIF
RETU .T.
//23/03/2003
func cdsn
retu cdsn
//23/03/2003 //nativo 02/09
func vzFCount()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
retu( FCount() )
else
if valtype( nativo() )="O"
retu( objsql[uISQL]:nFieldCount )
else
retu( len(objsql[uISQL]:aFields) )
endif
ENDIF
////////////////////////////////////////////////////////////////////////////////////
//23/03/2003 // nativo
func dameobjsql()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
retu( 0 )
else
retu( objsql[uISQL] )
ENDIF

//23/03/2003 //nativo 09/02


Function vzappend(aValores,aCampos,flag)
//El Flag se usa para definir donde poner registro despues de agregar
// 1 Poner registro al final ideal para bloques
// 2 no interesa donde pone el regsitro refresh
// 3 Ubicar reg buscando en primer campo

Local uISQL,oOdbc:=oOdbc(),name:="",valor:="",j,dato,arrini:={},ii
Local buf:=select(),limii,pasaflag:=.t.,limite:=if(valtype(aValores)="U",0,len(aValores) )
Local nlen,NEWROWID,anat,MYROWID,nwrowid
if valtype(flag)="U";flag:=1;end
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn);appe blan
While NETERR()
if MsgYesNo('No se puede agregar registros en la dbf '+dbf()+' del area '+ltrim(str(select()))+',
posiblemente otro usuario abrio esta dbf en modo exclusivo o con dbu,dbase o Fox .Desea
reintentar agregar ','Pulse No para Abandonar el Sistema');appe blan
else ;CLOSE ALL ;quit
end
Enddo
COMMIT
else
if valtype(oMysql)="O"
oOdbc:= TMSQuery():New( oMysql )
oOdbc:Open( "SELECT MAX(ROW_ID) FROM "+tablasql[uISQL] )
oOdbc:Load()
if oOdbc:nRowCount=0;NEWROWID:=1
else ; NEWROWID:=oOdbc:FldGet(1)+1
endif
else
NEWROWID:=oOdbc:QueryRow( "SELECT MAX(ROW_ID) FROM "+tablasql[uISQL] )
IF VALTYPE(NEWROWID[1])="U" ;NEWROWID:=1
ELSE;NEWROWID:=NEWROWID[1]+ 1
ENDIF
endif

//?"NEWROWID",VALTYPE(NEWROWID),NEWROWID,LEN(NEWROWID),NEWROWID[
1],VALTYPE(NEWROWID[1])
// MSGSTOP(NEWROWID,"EL NUEVO REGISTRO ")
// POR FIN LA AGREGACION COMO LA PLANTEA EL FABRICANTE
objsql[uISQL]:AddNew()
//objsql[uISQL]:Edit()
//objsql[uISQL]:ROW_ID:=NEWROWID // LA CLAVE PRIMARIA
objsql[uISQL]:FIELDPUT(1,NEWROWID)
if limite>0
For j=1 to limite
//MSGSTOP(objsql[uISQL]:FieldPos(aCampos[j]),"VZAPPEND FIELDPOS DE
"+aCampos[j])
objsql[uISQL]:FieldPut( mifieldpos(objsql[uISQL],acampos[j]),aValores[j] )
Next j
endif
objsql[uISQL]:Update()
Myrowid:=objsql[uISQL]:FIELDGET(1)
//msgstop(Myrowid,"QUE VALE MYROW_ID")
//msgstop(objsql[uISQL]:nAt,"QUE VALE nAt")

if valtype( nativo() )="O"


else
objsql[uISQL]:Refresh()
endif
nwrowid:=objsql[uISQL]:FIELDGET(1)
// msgstop("nuevo rowid",nwrowid)
if valtype( nativo() )="O"
else
if Myrowid==nwrowid
// ?" donde esta quedando nAt ",objsql[uISQL]:nAt
else
anat:=1
While anat<=len(objsql[uISQL]:aDbf)
//?anat,objsql[uISQL]:aDbf[anat,1]==Myrowid,objsql[uISQL]:aDbf[anat,1],Myrowid
if objsql[uISQL]:aDbf[anat,1]==Myrowid
objsql[uISQL]:nAt:=anat;exit
end
anat++
Enddo
endif
endif
endif
retu nil

//23/03/2003 //nativo 09/02


func vzRefresh()
Local uISQL
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
else
objsql[uISQL]:Refresh()
ENDIF
retu .t.
//23/03/2003
// borrar bloques // nativo 02/09
FUNC VZDelete(aValores,aCampos )
Local uISQL,j,cWHERE:="",cORDERBY:=""
IF (uISQL:=ASCAN( AREASQL,Select() ) )=0 .or. empty(cDsn)
else
For j=1 to len(aValores)
if valtype(aValores[j])="C" ;aValores[j]:=CHR(34)+aValores[j]+CHR(34)
else ;aValores[j]:=LTRIM(STR(aValores[j]))
endif
if j=len(aValores)
cWHERE+=aCampos[j]+"="+aValores[j]
cORDERBY+=aCampos[j]
else
cWHERE+=aCampos[j]+"="+aValores[j]+" AND "
cORDERBY+=aCampos[j]+","
endif
next j
if valtype(oMysql)="O"
oOdbc:= TMSQuery():New( oMysql )
oOdbc:ExecSQL( "DELETE FROM " +tablasql[uISQL]+" WHERE "+cWHERE)//+" ORDER
BY "+cORDERBY )
else
oOdbc:Execute( "DELETE FROM " +tablasql[uISQL]+" WHERE "+cWHERE)//+" ORDER
BY "+cORDERBY )
endif
/* esto no tiene nada qu ver con el cursor
if valtype(objsql[uISQL])="O"
objsql[uISQL]:Refresh()
endif
*/
endif
retu .t.

//23/03/2003
// La ultima cierra todas las areas y vacea arrays del sql
Func vzcloseall
dbcloseall()
if !empty(cDsn)
areasql:={};tablasql:={};dbfsql:={};objsql:={};sqlwhere:={}
endif
retu .t.
//////////////////////////////////////////////////////////////////////////////////
// nativo ++ 02/09/2003
func mifieldpos(objsql,campo,area)
Local buf:=select()
Local fin,resu:=0,x:=1
if empty(area);area:=select();end
Select(area)
if valtype( oMysql )="O"
fin:=objsql:nFieldCount
else
fin:=len(objsql:aFields)//objsql:Fcount()
endif
while x<=fin
//?"por favor funciona
",alltrim(uppe(objsql:Fieldname(x))),alltrim(uppe(campo)),alltrim(uppe(objsql:Fieldname(x)))=allt
rim(uppe(campo))
if valtype( oMysql )="O"
if alltrim(uppe(objsql:Fldname(x)))==alltrim(uppe(campo))
resu:=x ;exit
endif
else
if alltrim(uppe(objsql:Fieldname(x)))==alltrim(uppe(campo))
resu:=x ;exit
endif
endif
x++
Enddo
Select(buf)
retu(resu)

También podría gustarte