FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Manual de MariaDB
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Manual de MariaDB
Posted: Wed Mar 04, 2026 05:20 PM

MANUAL DE MARIADB

para FiveWin / Harbour

Guia Practica de Conexion, Consultas y Manejo de Datos

Basado en los ejemplos oficiales de FiveWin

Version 2025

Realizado por Carlos Atúncar Hernandez

Contenido

1\. Introduccion

2\. Conexion a MariaDB

3\. Comandos y funciones principales

4\. RowSet: consultas y edicion de datos

5\. Operaciones CRUD

6\. Manejo de tablas

7\. Relaciones Padre-Hijo

8\. Funciones avanzadas

9\. Encriptacion AES

10\. Datos espaciales (GEO)

11\. Unicode y codificaciones

12\. Importar DBF a MariaDB

13\. FWMariaRecord: edicion de registros individuales

14\. Comparacion FWH vs ADO vs Dolphin

15\. Referencia rapida de metodos

1\. Introduccion

FiveWin for Harbour incluye soporte nativo para MariaDB/MySQL a traves de la funcion maria\_Connect() y un conjunto de clases que permiten trabajar con bases de datos relacionales de forma sencilla, sin necesidad de drivers ODBC ni componentes externos.

Este manual cubre el uso practico de MariaDB en FiveWin, basado en los ejemplos oficiales incluidos en la distribucion (maria01.prg a maria16.prg y otros).

Requisitos

  • FiveWin for Harbour 25.x o superior

  • Servidor MariaDB 10.x / MySQL 5.7+ accesible en red o local

  • Incluir fivewin.ch en el programa

  • Habilitar Unicode: FW\_SetUnicode( .t. ) si se usan caracteres especiales

Arquitectura de la conexion

La conexion nativa de FiveWin a MariaDB se realiza directamente sin ODBC, lo que ofrece mayor velocidad y simplicidad. El objeto de conexion (oCn) es el punto central desde el que se realizan todas las operaciones.

2\. Conexion a MariaDB

2.1 Funcion maria\_Connect()

La funcion principal para establecer la conexion es maria\_Connect(). Devuelve un objeto de conexion o NIL si falla.

Sintaxis

oCn := maria\_Connect( cConexion, lMostrarError )

oCn := maria\_Connect( cHost, cDatabase, cUser, cPassword \[, nPort\] )

Parametros \- Forma con cadena de conexion

ParametroTipoDescripcion
cConexionCCadena con formato: host:puerto,base,usuario,password
lMostrarErrorL.t. muestra mensaje si falla la conexion

Parametros \- Forma extendida (via FWCONNECT)

ParametroTipoDescripcion
cHostCNombre o IP del servidor. Puede incluir :puerto (ej: localhost:3306)
cDatabaseCNombre de la base de datos
cUserCUsuario de MariaDB
cPasswordCContrasena
nPortNPuerto (opcional, default 3306\)

Ejemplos de conexion

// Forma 1: cadena compacta

oCn := maria\_Connect( "208.91.198.197:3306,fwhdemo,usuario,password", .t. )

// Forma 2: parametros separados (local)

oCn := maria\_Connect( "localhost", "fwh", "fivetec1\_antonio", "1234" )

// Forma 3: via comando FWCONNECT (fivewin.ch)

FWCONNECT oCn HOST cHost USER cUser PASSWORD cPassword DATABASE cDB

// Forma 4: funcion de demo incluida en FiveWin

oCn := FW\_DemoDB() // conecta al servidor de demo de FiveWin

oCn := FW\_DemoDB( 1 ) // servidor de demo alternativo

oCn := FW\_DemoDB( "ADO" ) // conexion via ADO

oCn := FW\_DemoDB( "DLP" ) // conexion via TDolphin

Verificar conexion y cerrar

if ( oCn := maria\_Connect( ... ) ) \== nil

MsgStop( "No se pudo conectar al servidor" )

return nil

endif

// ... operaciones con la base de datos ...

oCn:Close() // o oCn:End()

2.2 Propiedades del objeto de conexion

PropiedadTipoDescripcion
lLogErrLActiva el registro de errores en archivo .log
lShowErrorsLMuestra errores en pantalla automaticamente
nErrorNCodigo del ultimo error (0 \= sin error)
LockTimeOutNTiempo de espera para bloqueos (segundos)
lUnicodeLModo Unicode activo

oCn:lLogErr := .t. // activa log de errores

oCn:lShowErrors := .t. // muestra errores en pantalla

3\. Comandos y Funciones Principales

3.1 Resumen de metodos del objeto de conexion

MetodoDescripcion
oCn:RowSet( cSQL \[, aParams\] )Ejecuta una consulta y devuelve un RowSet editable
oCn:RecSet( cTabla, nRecs )Lee n registros de una tabla (eficiente para tablas grandes)
oCn:Query( cSQL )Ejecuta consulta (compatible TDolphin)
oCn:Execute( cSQL )Ejecuta sentencia SQL sin resultado (INSERT, UPDATE, DELETE, CREATE...)
oCn:QueryResult( cSQL )Devuelve el valor de la primera celda del resultado
oCn:ListTables()Devuelve array con nombres de tablas de la BD
oCn:TableExists( cTabla )Devuelve .t. si la tabla existe
oCn:CreateTable( cTabla, aEstructura )Crea una tabla nueva
oCn:DropTable( cTabla )Elimina una tabla
oCn:Insert( cTabla, cCampos, aData )Inserta uno o multiples registros
oCn:UpdateSummary( ... )Actualiza totales de tabla maestra desde detalle
oCn:ImportFromDbf( cArchivo )Importa un archivo DBF a MariaDB
oCn:PivotArray( ... )Genera tabla pivot
oCn:Close() / oCn:End()Cierra la conexion
oCn:ShowError()Muestra el ultimo error
oCn:setAutoCommit( lVal )Activa/desactiva auto commit para transacciones
oCn:TableStructure( cTabla )Devuelve estructura de la tabla

3.2 Configuracion inicial recomendada

function Main()

local oCn

SET DATE ITALIAN // formato dd/mm/yyyy

SET CENTURY ON

FW\_SetUnicode( .t. ) // habilitar Unicode

FWNumFormat( "A", .t. ) // formato numerico

oCn := maria\_Connect( "localhost", "mibd", "usuario", "clave" )

if oCn \== nil

  MsgStop( "Error de conexion" )

  return nil

endif

oCn:lShowErrors := .t.

// ... codigo de la aplicacion ...

oCn:Close()

return nil

4\. RowSet: Consultas y Edicion de Datos

El RowSet es el objeto central para trabajar con datos en FiveWin/MariaDB. Representa un conjunto de registros que puede mostrarse en un XBrowse y editarse directamente.

4.1 Crear un RowSet

// Tabla completa

oRs := oCn:RowSet( "clientes" )

// Con SQL personalizado

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE activo \= 1" )

// SQL con parametros (evita SQL injection)

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE estado \= ?", { cEstado } )

// JOIN entre tablas

TEXT INTO cSql

SELECT C.ID, C.FIRST, C.CITY, S.NAME AS STATENAME

FROM customer C

LEFT JOIN states S ON C.STATE \= S.CODE

ENDTEXT

oRs := oCn:RowSet( cSql )

4.2 Mostrar en XBrowse

// Modo simple

XBROWSER oRs FASTEDIT TITLE "Clientes"

// Con opciones avanzadas

XBROWSER oRs FASTEDIT AUTOSORT AUTOFIT SHOW RECID ;

TITLE "Clientes" ;

SETUP ( oBrw:lFastEdit := .f. )

4.3 Metodos del RowSet

MetodoDescripcion
oRs:GoTop()Va al primer registro
oRs:GoBottom()Va al ultimo registro
oRs:Skip(n)Avanza n registros
oRs:FieldGet(n)Obtiene valor del campo n
oRs:FieldGet('nombre')Obtiene valor por nombre de campo
oRs:Fields('campo'):lReadOnly := .t.Pone campo como solo lectura
oRs:Delete()Elimina el registro actual
oRs:ReQuery()Recarga los datos del servidor
oRs:ReQuery( aParams )Recarga con nuevos parametros
oRs:ReSync()Sincroniza el registro actual con el servidor
oRs:Refresh()Refresca los datos
oRs:SetDefault('campo', valor)Establece valor por defecto para nuevos registros
oRs:SetFilter('campo \= ?', {val})Aplica filtro parametrizado
oRs:ReFilter( aParams )Vuelve a filtrar con nuevos parametros
oRs:Close() / oRs:End()Cierra el RowSet
oRs:lAutoAppend := .t.Habilita agregar registros desde el browse
oRs:EditBaseRecord(...)Abre dialogo para editar registro completo

4.4 Acceso a campos como propiedades

Los campos del RowSet pueden accederse directamente como propiedades del objeto:

oRs := oCn:RowSet( "clientes" )

? oRs:nombre // lee el campo "nombre"

? oRs:salario // lee el campo "salario"

oRs:estado := "A" // escribe en el campo

4.5 RecSet para tablas grandes

// Lee solo los primeros 1000 registros, carga el resto bajo demanda

oRs := oCn:RecSet( "custbig", 1000 )

oRs:nLastRec := oCn:QueryResult( "SELECT COUNT(\*) FROM custbig" )

XBROWSER oRs TITLE "Tabla grande" SHOW RECID FASTEDIT

5\. Operaciones CRUD

5.1 INSERT \- Insertar registros

// Un registro

oCn:Insert( "clientes", "nombre,ciudad,salario", ;

{ { "Juan Lopez", "Madrid", 3500 } } )

// Multiples registros de una vez (mas eficiente)

local aData := {}

for i := 1 to 1000

AAdd( aData, { "Nombre" \+ Str(i), "Ciudad", HB\_RandomInt(1000,9999) } )

next

oCn:setAutoCommit( .f. ) // inicio de transaccion

oCn:Insert( "clientes", "nombre,ciudad,salario", aData )

oCn:setAutoCommit( .t. ) // commit

5.2 SELECT \- Consultar datos

// Valor unico

nTotal := oCn:QueryResult( "SELECT COUNT(\*) FROM clientes" )

nSuma := oCn:QueryResult( "SELECT SUM(salario) FROM clientes" )

// Resultado como array

aEstados := oCn:Execute( "SELECT CODE, NAME FROM states" )

// RowSet completo

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE ciudad \= ?", { cCiudad } )

5.3 UPDATE \- Actualizar datos

// Directo via SQL

oCn:Execute( "UPDATE clientes SET salario \= salario \* 1.10 WHERE ciudad \= " \+ ;

         Chr(39) \+ cCiudad \+ Chr(39) )

// A traves del RowSet (edicion directa en browse o codigo)

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE id \= ?", { nId } )

oRs:salario := 4000

oRs:Save() // guarda los cambios

5.4 DELETE \- Eliminar registros

// Via SQL

oCn:Execute( "DELETE FROM clientes WHERE id \= " \+ cValToChar( nId ) )

// Via RowSet

oRs:Delete()

oRs:ReQuery()

5.5 Actualizar tabla maestra con totales del detalle

// Actualiza campos de suma en tabla maestra a partir de tabla de detalle

oCn:UpdateSummary( "facturas", "id", "total,cantidad", ;

               "lineas",    "fid", "importe,qty" )

Util para mantener totales en cabeceras de facturas/pedidos actualizados automaticamente.

6\. Manejo de Tablas

6.1 Crear tabla

// Estructura: { nombre, tipo, longitud, decimales \[, opciones\] }

oCn:CreateTable( "clientes", { ;

{ "nombre", "C", 30, 0 }, ;

{ "ciudad", "C", 20, 0 }, ;

{ "salario", "N", 10, 2, "DEFAULT 0" }, ;

{ "activo", "L", 1, 0 }, ;

{ "fecha", "D", 8, 0 }, ;

{ "notas", "M", 0, 0 } } )

Tipos de datos soportados:

Tipo FiveWinTipo MariaDBDescripcion
CVARCHARCaracter / texto corto
NDECIMALNumerico
DDATEFecha
LTINYINT(1)Logico (booleano)
MTEXT / BLOBMemo / texto largo
\+INT AUTO\_INCREMENTEntero autoincrementable
\=TIMESTAMPFecha/hora automatica
@DATETIMEFecha y hora
^BLOBDatos binarios

6.2 Opciones especiales en columnas

// Auto-increment (clave primaria automatica)

{ "id", "+", 4, 0 }

// Timestamp automatico

{ "modificado", "=", 8, 0 }

// Con comentario para comportamiento especial

{ "codigo", "C", 10, 0, "comment'case:upper'" }, // fuerza mayusculas

{ "nombre", "C", 30, 0, "comment'case:proper'" }, // fuerza primera mayuscula

// Con valor por defecto

{ "cantidad", "N", 6, 3, "DEFAULT 1" }

// Con charset especifico

{ "texto\_utf8", "C", 40, 0, "utf8" }

{ "texto\_latin","C", 40, 0, "latin1" }

// Clave foranea (referencia)

{ "pid", "REFERENCES tabla\_padre( id )" }

6.3 Verificar y eliminar tablas

if oCn:TableExists( "clientes" )

oCn:DropTable( "clientes" )

endif

// Listar todas las tablas

XBROWSER oCn:ListTables() TITLE "Tablas disponibles"

7\. Relaciones Padre-Hijo

7.1 Un padre con un hijo

oStates := oCn:RowSet( "states" )

// Metodo 1: agregar hijo con SQL

oStates:AddChild( "SELECT \* FROM customer WHERE state \= states.code" )

// Metodo 2: agregar hijo con RowSet existente

oCust := oCn:RowSet( "customer" )

oStates:AddChild( oCust, "state \= states.code" )

// En el browse padre, sincronizar el hijo al cambiar de fila:

oBrwParent:bChange := { || oStates:SyncChild(), oBrwChild:GoTop(), oBrwChild:Refresh() }

7.2 Un padre con dos hijos (SetFilter)

oRsState := oCn:RowSet( "SELECT \* FROM states ORDER BY name" )

oRsCity := oCn:RowSet( "SELECT id, state, city FROM customer ORDER BY state, city" )

oRsCust := oCn:RowSet( "SELECT id, state, first FROM customer ORDER BY state" )

// Filtros parametrizados

oRsCity:SetFilter( "STATE \= ?", { oRsState:code } )

oRsCust:SetFilter( "STATE \= ?", { oRsState:code } )

// Actualizar hijos al cambiar de fila en el padre

oBrwState:bChange := {||

oRsCity:ReFilter( { oRsState:code } )

oRsCust:ReFilter( { oRsState:code } )

oBrwCity:Refresh()

oBrwCust:Refresh()

return nil }

7.3 Totales dinamicos con SQL (Running Totals)

TEXT INTO cSql

SELECT id, fecha, descripcion, importe,

( @bal := IF( tipo \= "1", @bal \+ importe, @bal \- importe ) ) AS saldo

FROM ctacte, ( SELECT @bal := 0 ) AS t

WHERE ncli \= ?

ORDER BY fecha

ENDTEXT

oRs := oCn:RowSet( cSql, { nCliente } )

Este patron SQL permite calcular saldos acumulados directamente en el servidor, sin procesar en el cliente.

8\. Funciones Avanzadas

8.1 AutoAppend: agregar registros desde el browse

oRs := oCn:RowSet( "SELECT \* FROM detalle WHERE docid \= ?", { nDocId } )

oRs:lAutoAppend := .t. // habilita agregar con flecha abajo

oRs:SetDefault( "docid", nDocId ) // valor fijo para campo clave

XBROWSER oRs FASTEDIT TITLE "Detalle del documento"

8.2 EditBaseRecord: editar registro completo

// Editar todos los campos aunque el RowSet tenga solo algunos

@ 20,20 BTNBMP PROMPT "Agregar" PIXEL FLAT OF oDlg ;

ACTION oRs:EditBaseRecord( nil, .t., { |oRec| MiDialogoEdicion( oRec ) }, oBrw )

@ 20,130 BTNBMP PROMPT "Editar" PIXEL FLAT OF oDlg ;

ACTION oRs:EditBaseRecord( nil, .f., { |oRec| MiDialogoEdicion( oRec ) }, oBrw, .t. )

// En la funcion de dialogo:

function MiDialogoEdicion( oRec )

local lNuevo := ( oRec:RecNo \== 0 )

// Usar GET oRec:campo, CHECKBOX oRec:campo, etc.

// Guardar: If( oRec:Modified(), oRec:Save(), nil )

return nil

8.3 Tabla Pivot

// aPivot \= array bidimensional con los datos cruzados

aPivot := oCn:PivotArray( "ventas", "region", "producto", "importe", "SUM" )

// Mostrar en XBrowse con posibilidad de invertir ejes

@ 30,10 XBROWSE oBrw SIZE \-10,-10 PIXEL OF oDlg ;

DATASOURCE aPivot AUTOCOLS CELL LINES FOOTERS

// Invertir filas y columnas

oBrw:bRClicked := { || oBrw:InvertPivot() }

8.4 Carga de informacion de zonas horarias

// Cargar archivo timezone\_posix.sql al servidor MariaDB

FWCONNECT oCn HOST cHost USER cUser PASSWORD cPassword DATABASE "mysql"

oCn:lLogErr := .t.

// Leer archivo y ejecutar cada sentencia SQL

oCn:Execute( cSql )

El archivo timezone\_posix.sql se descarga desde dev.mysql.com/downloads/timezones.html

9\. Encriptacion AES

MariaDB incluye funciones nativas de encriptacion AES\_ENCRYPT() y AES\_DECRYPT() que permiten almacenar datos sensibles como contrasenas y credenciales de forma segura.

9.1 Crear tabla con campos encriptados

TEXT INTO cSql

CREATE TABLE usuarios (

id INT AUTO\_INCREMENT PRIMARY KEY,

nombre VARCHAR(20),

login TINYBLOB,

pass VARBINARY(255)

) ENGINE=InnoDB CHARSET=latin1

ENDTEXT

oCn:Execute( cSql )

9.2 Insertar datos encriptados

cSql := "INSERT INTO usuarios (nombre, login, pass) VALUES (" \+ ;

    "'andrew'", AES\_ENCRYPT('Andrew', 'miClave'), " \+ ;

    "AES\_ENCRYPT('Pass123', 'miClave'))"

oCn:Execute( cSql )

9.3 Consultar con desencriptacion

cSql := "SELECT id, nombre, " \+ ;

    "AES\_DECRYPT(login,'miClave') AS login, " \+ ;

    "AES\_DECRYPT(pass, 'miClave') AS pass " \+ ;

    "FROM usuarios"

oRs := oCn:RowSet( cSql )

XBROWSER oRs

La clave de encriptacion nunca se almacena en la base de datos. Guardala de forma segura en tu aplicacion o en un archivo de configuracion externo.

10\. Datos Espaciales (GEO)

MariaDB 5.7.1+ soporta el tipo de dato POINT y funciones geometricas para calcular distancias entre coordenadas geograficas.

10.1 Crear tabla con coordenadas

cSql := "CREATE TABLE ciudades (" \+ ;

    "id INT AUTO\_INCREMENT PRIMARY KEY, " \+ ;

    "ciudad VARCHAR(20), pt POINT)"

oCn:Execute( cSql )

10.2 Insertar coordenadas

TEXT INTO cSql

INSERT INTO ciudades (ciudad, pt) VALUES

("Madrid", POINT( 3.7038, 40.4168 )),

("Paris", POINT( 2.3522, 48.8566 )),

("Londres", POINT( 0.1278, 51.5074 ))

ENDTEXT

oCn:Execute( cSql )

10.3 Funcion de distancia

// Crear funcion SQL para calcular distancia entre dos ciudades

TEXT INTO cSql

CREATE FUNCTION distancia( ciudad1 VARCHAR(20), ciudad2 VARCHAR(20) )

RETURNS DOUBLE

BEGIN

 DECLARE p1 POINT;

 DECLARE p2 POINT;

 SELECT pt INTO p1 FROM ciudades WHERE ciudad \= ciudad1;

 SELECT pt INTO p2 FROM ciudades WHERE ciudad \= ciudad2;

 RETURN ST\_Distance\_Sphere( p1, p2 );

END

ENDTEXT

oCn:Execute( cSql )

// Llamar a la funcion desde FiveWin

? oCn:distancia( "Madrid", "Paris" ) // distancia en metros

11\. Unicode y Codificaciones

11.1 Activar Unicode

FW\_SetUnicode( .t. ) // activar antes de conectar

oCn := maria\_Connect( "localhost", "mibd", "user", "pass" )

11.2 Charset por tabla y por campo

// Tabla con charset utf8

oCn:CreateTable( "textos", { ;

{ "idioma", "C", 15, 0, "latin1" }, ;

{ "contenido","C", 40, 0, "utf8" } }, nil, "utf8" )

11.3 Charset en ImportFromDbf

oCn:ImportFromDbf( "clientes.dbf",,,,,, "utf8\_spanish2\_ci" )

11.4 Triggers para username automatico

TEXT INTO cSql

CREATE TRIGGER tabla\_bi BEFORE INSERT ON mitabla

FOR EACH ROW

BEGIN

SET NEW.username \= SUBSTRING(CONCAT\_WS(",",@os\_user,@pc\_name),1,30);

END

ENDTEXT

oCn:Execute( "DROP TRIGGER IF EXISTS tabla\_bi" )

oCn:Execute( cSql )

12\. Importar DBF a MariaDB

FiveWin permite importar directamente tablas DBF a MariaDB con un solo metodo, preservando la estructura y los datos.

12.1 Importacion basica

oCn := maria\_Connect( "localhost", "fwh", "usuario", "clave" )

oCn:lLogErr := .t. // registrar errores en archivo .log

oCn:ImportFromDbf( "clientes.dbf" )

// Verificar resultado

oRs := oCn:RowSet( "clientes" )

XBROWSER oRs FASTEDIT AUTOSORT

12.2 Con charset especifico

oCn:ImportFromDbf( "clientes.dbf",,,,,, "utf8\_spanish2\_ci" )

12.3 Proceso recomendado de migracion DBF \-\> MariaDB

  • Conectar a MariaDB con oCn

  • Llamar a oCn:ImportFromDbf() para cada tabla DBF

  • Revisar el archivo .log por si hubiera errores de conversion

  • Verificar los datos importados con XBROWSER oCn:RowSet(cTabla)

  • Ajustar charsets si hay problemas con caracteres especiales

Ver el ejemplo yunusm.prg incluido en samples\\misc\\ para una migracion completa de una aplicacion DBF a MariaDB.

13\. FWMariaRecord: Edicion de Registro Individual

La clase FWMariaRecord (mariarec.prg) permite leer, editar y guardar un registro individual de MariaDB, ideal para formularios de alta/edicion.

13.1 Crear un FWMariaRecord

// Leer un registro existente

oRec := FWMariaRecord():New( oCn, "clientes", "id \= " \+ cValToChar( nId ) )

// Registro en blanco (para alta)

oRec := FWMariaRecord():New( oCn, "clientes" )

oRec:Blank()

13.2 Acceder y modificar campos

? oRec:nombre // leer campo

oRec:nombre := "Juan Garcia" // modificar campo

oRec:salario := 3500

13.3 Guardar cambios

if oRec:Modified()

oRec:Save()

endif

13.4 Propiedades principales

Propiedad / MetodoDescripcion
oRec:lValidDataDevuelve .t. si el registro tiene datos validos
oRec:RecNoNumero de registro (0 para registro nuevo)
oRec:New(oCn, cTabla, cWhere)Constructor
oRec:Read(cWhere)Carga un registro segun condicion WHERE
oRec:Blank()Carga un registro en blanco para insercion
oRec:Load()Recarga el registro del servidor
oRec:Save()Guarda los cambios (INSERT o UPDATE segun corresponda)
oRec:Modified()Devuelve .t. si hubo cambios sin guardar
oRec:campoAcceso directo a cada campo como propiedad

14\. Comparacion FWH vs ADO vs Dolphin

FiveWin permite conectarse a MariaDB/MySQL usando tres bibliotecas diferentes. El ejemplo maria14.prg hace una comparacion de rendimiento en tiempo real.

AspectoFWH NativoADOTDolphin
Conexionmaria\_Connect()FW\_DemoDB('ADO')FW\_DemoDB('DLP')
QueryoCn:RowSet(cSql)FW\_OpenRecordSet(oCn,cSql)oCn:Query(cSql)
Cerrar RSoRs:Close()oRs:Close()oRs:End()
Cerrar CNoCn:Close()oCn:Close()oCn:End()
Driver extraNo (nativo)Necesita ODBCNecesita TDolphin
VelocidadMuy altaMediaAlta
RecomendadoSi (primera opcion)Solo si ya existeCompatible

Para nuevas aplicaciones se recomienda siempre el conector nativo FWH (maria\_Connect). Es el mas rapido, no requiere drivers adicionales y tiene mejor integracion con las clases de FiveWin.

15\. Referencia Rapida de Metodos

Conexion

ComandoDescripcion
maria\_Connect(cStr, lErr)Conectar al servidor
FWCONNECT oCn HOST ... USER ... PASSWORD ... DATABASE ...Conectar via comando
FW\_DemoDB(\[tipo\])Conectar al servidor de demo
oCn:Close() / oCn:End()Cerrar conexion

Consultas

ComandoDescripcion
oCn:RowSet(cSql \[,aParams\])Recordset editable
oCn:RecSet(cTabla, nRecs)Recordset paginado para tablas grandes
oCn:QueryResult(cSql)Valor unico (COUNT, SUM, etc.)
oCn:Execute(cSql)Ejecutar SQL sin resultado
oCn:ListTables()Array de tablas

Tablas

ComandoDescripcion
oCn:TableExists(cTabla)Verificar si existe
oCn:CreateTable(cTabla, aStruct \[,lPK\] \[,cCharset\])Crear tabla
oCn:DropTable(cTabla)Eliminar tabla
oCn:Insert(cTabla, cCampos, aData)Insertar registros
oCn:ImportFromDbf(cArchivo,...)Importar desde DBF
oCn:TableStructure(cTabla)Estructura de la tabla

RowSet

MetodoDescripcion
oRs:campoLeer/escribir campo directamente
oRs:Fields('campo'):lReadOnlyPoner campo de solo lectura
oRs:lAutoAppend := .t.Habilitar insercion desde browse
oRs:SetDefault('campo', val)Valor por defecto en nuevos registros
oRs:SetFilter(cExpr, aParams)Filtrar registros
oRs:ReFilter(aParams)Re-aplicar filtro con nuevos parametros
oRs:ReQuery(\[aParams\])Recargar datos
oRs:Delete()Eliminar registro actual
oRs:EditBaseRecord(...)Editar registro completo con dialogo
oRs:AddChild(cSQL)Agregar RowSet hijo
oRs:SyncChild()Sincronizar hijo con posicion actual
oRs:Close() / oRs:End()Cerrar RowSet

Documentacion adicional y foro de soporte: https://forums.fivetechsupport.com

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 05:59 PM

Versión ampliada usando Antigravity:

Manual de MariaDB para FiveWin / Harbour

Guía Práctica de Conexión, Consultas y Manejo de Datos

Basado en los ejemplos oficiales de FiveWin y el código fuente de fwmaria.prg y mariarec.prg

Versión 2025


---

Índice

Cap.TítuloFichero
1Introducción01_introduccion.md
2Conexión a MariaDB02_conexion.md
3Comandos y Funciones Principales03_comandos_funciones.md
4RowSet: Consultas y Edición de Datos04_rowset.md
5Operaciones CRUD05_crud.md
6Manejo de Tablas06_manejo_tablas.md
7Relaciones Padre-Hijo07_relaciones_padre_hijo.md
8Funciones Avanzadas08_funciones_avanzadas.md
9Encriptación AES09_encriptacion_aes.md
10Datos Espaciales (GEO)10_datos_espaciales.md
11Unicode y Codificaciones11_unicode.md
12Importar DBF a MariaDB12_importar_dbf.md
13FWMariaRecord: Edición de Registros13_fwmariarecord.md
14Comparación FWH vs ADO vs Dolphin14_comparacion.md
15RecSet: Tablas Grandes y Rendimiento15_recset_rendimiento.md
16Bloqueo de Registros (Locking)16_bloqueo_registros.md
17Replicación entre Servidores17_replicacion.md
18Sincronización DBF ↔ MariaDB18_sincronizacion_dbf.md
19Totales Acumulados (Running Totals)19_totales_acumulados.md
20Query Browser Interactivo20_query_browser.md
21Portar una App de DBF a MariaDB21_portar_dbf_a_mariadb.md
22Referencia Rápida y Constantes22_referencia_rapida.md
23Clase TField: Metadatos de Campos23_clase_tfield.md

---

Programas de Ejemplo Incluidos

EjemploDescripción
maria01.prgListar tablas y ver contenido con doble-click
maria02.prgJOIN izquierdo con edición en XBrowse
maria03.prgTablas Pivot con inversión de filas/columnas
maria04.prgRelaciones padre-hijo (2 métodos)
maria05.prgComparación FWH vs Dolphin vs ADO
maria06.prgImportar DBF con collation español
maria07.prgCargar información de zonas horarias
maria08.prgActualizar resúmenes master-detail
maria09.prgAuto-append con valores por defecto y columnas computadas
maria10.prgRecSet para tabla con millones de registros
maria11.prgUnicode con triggers automáticos
maria12.prgRunning totals (saldos acumulados)
maria14.prgBenchmark de rendimiento ADO vs FWH vs Dolphin
maria15.prgEditBaseRecord con formulario personalizado
maria16.prgPadre con 2 hijos usando SetFilter/ReFilter
mariaaes.prgCifrado AES de campos sensibles
mariabig.prgRecSet paginado para tabla con millones de registros
mariageo.prgTipo POINT y cálculo de distancias geográficas
mariainv.prgSistema de facturación completo
marialok.prgBloqueo pesimista con FOR UPDATE
mariaqry.prgQuery Browser interactivo con re-query dinámico
mariarpl.prgReplicación de cambios entre dos servidores
mariasyn.prgSincronización DBF → MariaDB en tiempo real

---

Documentación adicional y foro de soporte: https://forums.fivetechsupport.com

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:00 PM

1. Introducción

← Índice | Siguiente: Conexión →


---

FiveWin for Harbour (FWH) incluye soporte nativo para MariaDB/MySQL a través de la función maria_Connect() y un conjunto de clases que permiten trabajar con bases de datos relacionales de forma sencilla, sin necesidad de drivers ODBC ni componentes externos.

Este manual cubre el uso práctico de MariaDB en FiveWin, basado en los ejemplos oficiales incluidos en la distribución (maria01.prg a maria16.prg y otros) y el análisis del código fuente (fwmaria.prg, mariarec.prg).

Características principales

  • Conexión directa al servidor MariaDB/MySQL vía protocolo nativo TCP/IP
  • RowSets editables con soporte completo CRUD (Create, Read, Update, Delete)
  • RecSets paginados para tablas con millones de registros
  • Integración total con XBrowse para visualización y edición interactiva
  • Soporte Unicode (UTF-8) nativo
  • Tablas Pivot, relaciones padre-hijo, replicación, cifrado AES, datos geoespaciales
  • Importación/Exportación desde/hacia ficheros DBF
  • Compatibilidad con Harbour y xHarbour, 32 y 64 bits

Requisitos

  • FiveWin for Harbour 25.x o superior
  • Servidor MariaDB 10.x / MySQL 5.7+ accesible en red o local
  • Incluir fivewin.ch en el programa
  • Habilitar Unicode: FW_SetUnicode( .t. ) si se usan caracteres especiales

Arquitectura de la conexión

La conexión nativa de FiveWin a MariaDB se realiza directamente sin ODBC, lo que ofrece mayor velocidad y simplicidad. El objeto de conexión (oCn) es el punto central desde el que se realizan todas las operaciones.

┌─────────────────────────┐
│   Aplicación FWH        │
│   (Harbour / xHarbour)  │
├─────────────────────────┤
│   fwmaria.prg           │  ← Clases: FWMariaRowSet, FRecSet, TField
│   mariarec.prg          │  ← Clase: FWMariaRecord (TDataRow)
├─────────────────────────┤
│   libmariadb.dll        │  ← DLL nativa MariaDB (32/64 bits)
├─────────────────────────┤
│   Servidor MariaDB      │
│   o MySQL               │
└─────────────────────────┘

Ficheros necesarios

FicheroDescripción
libmariadb.dllDLL cliente MariaDB (32 bits)
libmariadb64.dllDLL cliente MariaDB (64 bits)
libmariadb.lib / libmariadb32.libLibrería de enlace (32 bits)
libmariadb64.lib / libmariadb64.aLibrería de enlace (64 bits)
fwmaria.prgCódigo fuente de las clases principales
mariarec.prgCódigo fuente de la clase FWMariaRecord

La DLL libmariadb.dll debe estar en la misma carpeta que el .exe o en una carpeta del PATH del sistema.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:01 PM

2. Conexión a MariaDB

← Introducción | Índice | Siguiente: Comandos →


---

2.1 Función maria_Connect()

La función principal para establecer la conexión es maria_Connect(). Devuelve un objeto de conexión o NIL si falla.

Sintaxis

Code (clipper): Select all Collapse
oCn := maria_Connect( cConexion, lMostrarError )
oCn := maria_Connect( cHost, cDatabase, cUser, cPassword [, nPort] )
oCn := maria_Connect( { cHost, cDB, cUser, cPassword } )

Parámetros — Forma con cadena de conexión

ParámetroTipoDescripción
cConexionCCadena con formato: host:puerto,base,usuario,password
lMostrarErrorL.t. muestra mensaje si falla la conexión

Parámetros — Forma extendida

ParámetroTipoDescripción
cHostCNombre o IP del servidor. Puede incluir :puerto (ej: localhost:3306)
cDatabaseCNombre de la base de datos
cUserCUsuario de MariaDB
cPasswordCContraseña
nPortNPuerto (opcional, default 3306)
nFlagsNFlags de conexión (opcional)
cCharSetCJuego de caracteres (opcional)
cMsgLangCIdioma de mensajes (opcional)
cLocaleCLocale (opcional)

Ejemplos de conexión

Code (clipper): Select all Collapse
// Forma 1: cadena compacta
oCn := maria_Connect( "208.91.198.197:3306,fwhdemo,usuario,password", .t. )

// Forma 2: parámetros separados (local)
oCn := maria_Connect( "localhost", "fwh", "usuario", "1234" )

// Forma 3: via comando FWCONNECT
FWCONNECT oCn HOST cHost USER cUser PASSWORD cPassword DATABASE cDB

// Forma 4: array de parámetros
oCn := maria_Connect( { "localhost", "mibase", "root", "clave" } )

// Forma 5: función de demo incluida en FiveWin
oCn := FW_DemoDB()          // conecta al servidor de demo de FiveWin
oCn := FW_DemoDB( 1 )       // servidor de demo alternativo
oCn := FW_DemoDB( "ADO" )   // conexión via ADO
oCn := FW_DemoDB( "DLP" )   // conexión via TDolphin

// Forma 6: desde objeto ADO o Dolphin existente
oCn := maria_Connect( oAdoConnection )    // desde ADODB.Connection
oCn := maria_Connect( oDolphinServer )    // desde TDolphinSrv

Nota: Internamente, maria_Connect() analiza los parámetros y acepta múltiples combinaciones: cadena CSV, array, parámetros individuales, e incluso objetos ADO o Dolphin existentes (ver función ParseConObject en fwmaria.prg).

Verificar conexión y cerrar

Code (clipper): Select all Collapse
if ( oCn := maria_Connect( "host,db,user,pwd", .t. ) ) == nil
   MsgStop( "No se pudo conectar al servidor" )
   return nil
endif

// ... operaciones con la base de datos ...

oCn:Close()   // o oCn:End()

Obtener error de conexión

Code (clipper): Select all Collapse
aErr := maria_ConnectError()  // devuelve { nErrorCode, cErrorMsg }

2.2 Conexión embebida (solo 32 bits)

Code (clipper): Select all Collapse
oCn := maria_Embedded( cDataFolder, cDB, cLangFolder )

Permite usar MariaDB sin servidor externo. Solo disponible en 32 bits.

2.3 Propiedades del objeto de conexión

PropiedadTipoDescripción
oCn:lOpenL.t. si la conexión está activa
oCn:cDBCNombre de la base de datos actual
oCn:lLogErrLActiva el registro de errores en archivo .log
oCn:lShowErrorsLMuestra errores en pantalla automáticamente
oCn:nErrorNCódigo del último error (0 = sin error)
oCn:LockTimeOutNTiempo de espera para bloqueos (segundos)
oCn:lUnicodeLModo Unicode activo
oCn:cLastSQLCÚltima sentencia SQL ejecutada
oCn:pMySqlPPuntero interno a la conexión MySQL
Code (clipper): Select all Collapse
oCn:lLogErr      := .t.   // activa log de errores
oCn:lShowErrors  := .t.   // muestra errores en pantalla

2.4 Ejemplo completo — maria01.prg

Code (clipper): Select all Collapse
#include "fivewin.ch"

REQUEST DBFCDX

static oCn

function Main()
   local aTables

   SET DATE ITALIAN
   SET CENTURY ON
   FW_SetUnicode( .t. )

   CursorWait()
   oCn := maria_Connect( "208.91.198.197:3306,fwhdemo,gnraofwh,Bharat@1950", .t. )

   XBROWSER oCn:ListTables() ;
      TITLE "Dbl-Click to View Table" ;
      SHOW RECID ;
      SETUP ( ;
      oBrw:aCols[ 1 ]:bLDClickData := { |r,c,f,o| ShowTable( o:Value ) } )

   oCn:Close()
return nil

function ShowTable( cTable )
   local oRs
   if cTable == "custbig"
      MsgRun( "Reading " + cTable, "Please wait", ;
         { || oRs := oCn:RecSet( cTable, -100 ) } )
   else
      MsgRun( "Reading " + cTable, "Please wait", ;
         { || oRs := oCn:RowSet( cTable ) } )
   endif
   XBROWSER oRs TITLE cTable FASTEDIT NOMODAL SHOW RECID
return nil

Este ejemplo conecta al servidor demo, lista todas las tablas y permite explorar cada una con doble-click.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:01 PM

3. Comandos y Funciones Principales

← Conexión | Índice | Siguiente: RowSet →


---

3.1 Resumen de métodos del objeto de conexión

MétodoDescripción
oCn:RowSet( cSQL [, aParams] )Ejecuta consulta y devuelve un RowSet editable
oCn:RecSet( cTabla, nRecs )Lee n registros con paginación (tablas grandes)
oCn:Query( cSQL )Ejecuta consulta (compatible TDolphin)
oCn:Execute( cSQL )Ejecuta sentencia SQL sin resultado (INSERT, UPDATE, DELETE, CREATE...)
oCn:QueryResult( cSQL )Devuelve el valor de la primera celda del resultado
oCn:ListTables()Devuelve array con nombres de tablas de la BD
oCn:TableExists( cTabla )Devuelve .t. si la tabla existe
oCn:TableStructure( cTabla )Devuelve estructura de la tabla
oCn:CreateTable( cTabla, aEstructura [, lPK] [, cCharset] )Crea una tabla nueva
oCn:DropTable( cTabla )Elimina una tabla
oCn:Insert( cTabla, cCampos, aData )Inserta uno o múltiples registros
oCn:Upsert( cTabla, nil, aVals )INSERT ... ON DUPLICATE KEY UPDATE
oCn:UpdateSummary( ... )Actualiza totales de tabla maestra desde detalle
oCn:ImportFromDbf( cArchivo [,...] )Importa un archivo DBF a MariaDB
oCn:CopyTableToServer( cTabla, oOtroCn )Copia tabla a otro servidor
oCn:PivotArray( cTabla, cFila, cCol, cVal, cAggr )Genera tabla pivot
oCn:BeginTransaction()Inicia transacción
oCn:CommitTransaction()Confirma transacción
oCn:RollBack()Revierte transacción
oCn:SetAutoCommit( lVal )Activa/desactiva auto commit
oCn:Close() / oCn:End()Cierra la conexión
oCn:ShowError()Muestra el último error
oCn:FieldsAndValsToSQL( aCols [, nil, lWhere] )Genera pares campo=valor para SQL
oCn:ValToSql( uVal )Convierte valor Harbour a literal SQL
oCn:ApplyParams( cSql, aParams )Sustituye ? por valores en SQL
oCn:ReadNextResults()Lee resultados adicionales (multi-statement)

3.2 Funciones públicas

FunciónDescripción
maria_Connect( ... )Conectar al servidor
maria_ConnectError()Obtener error de la última conexión { nErr, cErr }
maria_Embedded( cDataFolder, cDB, cLangFolder )Conexión embebida (32 bits)
mysql_RowSet( oCn, cSql )Crear RowSet (wrapper)
fwmarialib_Version()Versión de la librería
FW_SetUnicode( lSet )Activar/desactivar Unicode
FWMARIA_LCMESSAGES( lSet )Activar/desactivar mensajes localizados
FWMARIA_SET_PAD_CHAR_TO_FULL_LENGTH( lSet )Padding de campos CHAR
FW_DemoDB( [nType] )Conectar al servidor de demostración
MYSQL_TinyIntAsLogical( [lSet] )Tratar TINYINT como lógico
MYSQL_MaxPadLimit( [nLimit] )Establecer límite de padding

3.3 Configuración inicial recomendada

Code (clipper): Select all Collapse
#include "fivewin.ch"

function Main()
   local oCn

   SET DATE ITALIAN        // formato dd/mm/yyyy
   SET CENTURY ON
   FW_SetUnicode( .t. )    // habilitar Unicode
   FWNumFormat( "A", .t. ) // formato numérico con separadores

   oCn := maria_Connect( "localhost", "mibd", "usuario", "clave" )
   if oCn == nil
      MsgStop( "Error de conexión" )
      return nil
   endif

   oCn:lShowErrors := .t.

   // ... código de la aplicación ...

   oCn:Close()
return nil

3.4 Transacciones

Code (clipper): Select all Collapse
// Método 1: Desactivar auto-commit
oCn:SetAutoCommit( .f. )
// ... múltiples operaciones INSERT/UPDATE ...
oCn:SetAutoCommit( .t. )    // commit implícito

// Método 2: Transacciones explícitas
oCn:BeginTransaction()
oCn:Execute( "UPDATE cuentas SET saldo = saldo - 1000 WHERE id = 1" )
oCn:Execute( "UPDATE cuentas SET saldo = saldo + 1000 WHERE id = 2" )
if oCn:nError == 0
   oCn:CommitTransaction()
else
   oCn:RollBack()
endif

3.5 Llamar funciones del servidor

Las funciones creadas en el servidor pueden invocarse directamente como métodos del objeto de conexión:

Code (clipper): Select all Collapse
// Si existe la función 'distance_between' en el servidor:
? oCn:distance_between( "Madrid", "Paris" )
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:02 PM

4. RowSet: Consultas y Edición de Datos

← Comandos | Índice | Siguiente: CRUD →


---

El RowSet es el objeto central para trabajar con datos en FiveWin/MariaDB. Representa un conjunto de registros que puede mostrarse en un XBrowse y editarse directamente. Todos los datos se leen a memoria, permitiendo ordenación y filtrado local sin consultas adicionales al servidor.

4.1 Crear un RowSet

Code (clipper): Select all Collapse
// Tabla completa
oRs := oCn:RowSet( "clientes" )

// Acceso directo por nombre de tabla
oRs := oCn:clientes    // equivale a oCn:RowSet( "clientes" )

// Con SQL personalizado
oRs := oCn:RowSet( "SELECT * FROM clientes WHERE activo = 1" )

// SQL con parámetros (evita SQL injection)
oRs := oCn:RowSet( "SELECT * FROM clientes WHERE estado = ?", { cEstado } )

// JOIN entre tablas (ejemplo maria02.prg)
TEXT INTO cSql
   SELECT C.ID AS CustID, C.FIRST AS CustName, C.AGE AS AG,
          C.STATE AS ST, S.NAME AS StateName
   FROM customer C
   LEFT OUTER JOIN states S ON C.STATE = S.CODE
   ORDER BY CUSTID
ENDTEXT
oRs := oCn:RowSet( cSql )

4.2 Mostrar en XBrowse

Code (clipper): Select all Collapse
// Modo simple
XBROWSER oRs FASTEDIT TITLE "Clientes"

// Con opciones avanzadas
XBROWSER oRs FASTEDIT AUTOSORT AUTOFIT SHOW RECID ;
   TITLE "Clientes" ;
   SETUP ( oBrw:lFastEdit := .f., oBrw:aCols[ 4 ]:cEditPicture := "@!" )

// Control total programático
DEFINE DIALOG oDlg SIZE 800,600 PIXEL FONT oFont TITLE "Clientes"

@ 50,20 XBROWSE oBrw SIZE -20,-20 PIXEL OF oDlg ;
   DATASOURCE oRs ;
   COLUMNS "First", "City", "Salary" ;
   CELL LINES NOBORDER FOOTERS FASTEDIT

WITH OBJECT oBrw
   :nEditTypes := EDIT_GET
   :Salary:nFooterType := AGGR_SUM
   :MakeTotals()
   :CreateFromCode()
END

ACTIVATE DIALOG oDlg CENTERED

4.3 Navegación

MétodoDescripción
oRs:GoTop()Ir al primer registro
oRs:GoBottom()Ir al último registro
oRs:Skip( n )Saltar n registros
oRs:GoTo( nRec )Ir al registro nRec
oRs:Bof().t. si está al inicio
oRs:Eof().t. si está al final
oRs:RecNo()Número de registro actual (según ID del servidor)
oRs:KeyNo()Posición actual en el conjunto de datos
oRs:KeyCount()Total de registros
oRs:LastRec()Último número de registro
oRs:BookMarkLeer/asignar marcador de posición

4.4 Acceso a campos

Code (clipper): Select all Collapse
// Como propiedades (via error handler — la forma más cómoda)
? oRs:First       // lee el campo "First"
? oRs:Salary      // lee el campo "Salary"
oRs:First  := "Juan"        // escribe en el campo
oRs:Salary := 50000

// Por posición numérica
? oRs:FieldGet( 1 )
oRs:FieldPut( 2, "Nuevo valor" )

// Por nombre de campo
? oRs:FieldGet( "FIRST" )
oRs:FieldPut( "SALARY", 60000 )

// Información de campos
? oRs:FieldName( n )      // nombre del campo n
? oRs:FieldType( n )      // tipo ('C', 'N', 'D', 'L', 'T', 'M', '+', '=')
? oRs:FieldLen( n )       // longitud
? oRs:FieldDec( n )       // decimales
? oRs:FieldPos( "FIRST" ) // posición del campo por nombre
? oRs:FieldReadOnly( n )  // .t. si es solo lectura
? oRs:FCount()             // número total de campos

4.5 Objeto Fields

Cada campo del RowSet se accede como un objeto TField con metadatos completos:

Code (clipper): Select all Collapse
oField := oRs:Fields( "salary" )
? oField:Value              // valor actual
oField:Value := 60000       // asignar nuevo valor
oField:lReadOnly := .t.     // marcar como solo lectura
? oField:OriginalValue      // valor antes de editar
? oField:name, oField:type, oField:len, oField:dec
? oField:lPrimary           // .t. si es clave primaria
? oField:lAutoInc            // .t. si es auto-incremento

4.6 Ordenación

Code (clipper): Select all Collapse
oRs:SetOrder( "FIRST" )              // Ordenar por campo FIRST
oRs:SetOrder( "SALARY", , .t. )      // Ordenar descendente
oRs:OrdDescend( "CITY" )             // Cambiar orden
oRs:Sort := "FIRST"                  // Asignar orden vía propiedad
? oRs:Sort                           // Consultar orden actual
? oRs:lDescend                       // .t. si orden descendente

4.7 Filtros y búsqueda

Code (clipper): Select all Collapse
// Filtrar datos (se aplica localmente sobre los datos ya leídos)
oRs:SetFilter( "STATE = ?", { "CA" } )

// Re-filtrar con nuevos parámetros
oRs:ReFilter( { "NY" } )

// Búsqueda
oRs:Seek( "John" )                    // buscar exacto
oRs:Seek( "Jo", .t. )                 // búsqueda parcial (soft)
oRs:OrdWildSeek( "Jo*" )              // búsqueda con comodines
oRs:Locate( {|| oRs:Salary > 50000 } ) // buscar por condición

4.8 Guardado y edición

Code (clipper): Select all Collapse
oRs:First  := "Pedro"
oRs:Salary := 45000
oRs:Save()          // Genera UPDATE solo para columnas cambiadas

oRs:Cancel()        // Cancela cambios pendientes

// Agregar registros
oRs:Append( "first,last,salary", { "Ana", "López", 40000 } )
oRs:AppendBlank()   // Agrega registro en blanco

// Borrar registro actual
oRs:Delete()

4.9 Re-lectura de datos

Code (clipper): Select all Collapse
oRs:ReSync()                 // Re-leer registro actual del servidor
oRs:ReQuery()                // Re-leer todos los datos con mismo SQL
oRs:ReQuery( { "NY" } )     // Re-leer con nuevos parámetros
oRs:ReQuery( cNewSql )      // Re-leer con nuevo SQL
oRs:Refresh()                // Comprobar si hay cambios en el servidor

4.10 Propiedades importantes del RowSet

PropiedadTipoDescripción
oRs:SourceCSQL fuente del RowSet
oRs:cBaseTableCTabla base para operaciones CRUD
oRs:cBaseDBCBase de datos de la tabla base
oRs:lReadOnlyL.t. si el RowSet es de solo lectura
oRs:lAutoAppendLHabilitar auto-agregar desde browse
oRs:lAutoPageLPaginación automática
oRs:lBatchModeLModo batch para ediciones masivas
oRs:lShowErrorsLMostrar errores
oRs:lMultiTablesL.t. si el SQL involucra varias tablas
oRs:lLockedL.t. si usa FOR UPDATE
oRs:nReadSecsNSegundos que tardó la lectura
oRs:nFoundRowsNRegistros encontrados (con SQL_CALC_FOUND_ROWS)
oRs:aStruct / oRs:aStructureAEstructura de campos
oRs:aPrimaryAColumnas de clave primaria
oRs:aBaseColsAColumnas de la tabla base
oRs:oChildORowSet hijo enlazado
oRs:oReplServerOServidor de replicación
oRs:nAutoIncColNColumna auto-incremento
oRs:cLastSQLCÚltima sentencia SQL ejecutada
oRs:bDataRowBBloque para crear TDataRow
oRs:bTriggerBBloque trigger antes de guardar

4.11 Paginación

Code (clipper): Select all Collapse
// Navegación por páginas
oRs:GoToPage( nPage )
oRs:FirstPage()
oRs:PrevPage()
oRs:NextPage()
oRs:LastPage()
? oRs:nCurrentPage
? oRs:nMaxPages

4.12 Export

Code (clipper): Select all Collapse
oRs:ToExcel()            // Exportar a Excel
cJson := oRs:ToJson()    // Exportar a JSON
hHash := oRs:ToHash()    // Exportar a Hash
aRows := oRs:GetRows()   // Obtener como array
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:03 PM

5. Operaciones CRUD

← RowSet | Índice | Siguiente: Manejo de Tablas →


---

5.1 INSERT — Insertar registros

Code (clipper): Select all Collapse
// Un registro
oCn:Insert( "clientes", "nombre,ciudad,salario", ;
   { "Juan López", "Madrid", 3500 } )

// Múltiples registros de una vez (mucho más eficiente)
local aData := {}
for i := 1 to 1000
   AAdd( aData, { "Nombre" + Str(i), "Ciudad", HB_RandomInt(1000, 9999) } )
next

oCn:SetAutoCommit( .f. )     // inicio de transacción
oCn:Insert( "clientes", "nombre,ciudad,salario", aData )
oCn:SetAutoCommit( .t. )     // commit

Upsert (INSERT o UPDATE)

Code (clipper): Select all Collapse
// Inserta si no existe, actualiza si ya existe (basado en clave primaria)
oCn:Upsert( "clientes", nil, aValues )

5.2 SELECT — Consultar datos

Code (clipper): Select all Collapse
// Valor único (escalar)
nTotal := oCn:QueryResult( "SELECT COUNT(*) FROM clientes" )
nSuma  := oCn:QueryResult( "SELECT SUM(salario) FROM clientes" )

// Resultado como array
aEstados := oCn:Execute( "SELECT CODE, NAME FROM states" )

// RowSet completo
oRs := oCn:RowSet( "SELECT * FROM clientes WHERE ciudad = ?", { cCiudad } )

5.3 UPDATE — Actualizar datos

Code (clipper): Select all Collapse
// Directo vía SQL
oCn:Execute( "UPDATE clientes SET salario = salario * 1.10 WHERE ciudad = 'Madrid'" )

// A través del RowSet
oRs := oCn:RowSet( "SELECT * FROM clientes WHERE id = ?", { nId } )
oRs:salario := 4000
oRs:Save()   // genera UPDATE solo para columnas cambiadas

// Actualizar campo específico
oRs:Update( { "salario" }, { 5000 } )

// Verificar error
if oCn:nError != 0
   oCn:ShowError()
endif

5.4 DELETE — Eliminar registros

Code (clipper): Select all Collapse
// Vía SQL
oCn:Execute( "DELETE FROM clientes WHERE id = " + cValToChar( nId ) )

// Vía RowSet
oRs:Delete()
oRs:ReQuery()    // recargar datos

Eliminar desde XBrowse

Code (clipper): Select all Collapse
// Botón de eliminar en diálogo
@ 20,20 BTNBMP PROMPT "Delete" SIZE 60,20 PIXEL FLAT OF oDlg ;
   ACTION ( oRs:Delete(), oRs:ReQuery(), oBrw:MakeTotals(), ;
            oBrw:Refresh(), oBrw:SetFocus() )

5.5 Actualizar tabla maestra con totales del detalle

Code (clipper): Select all Collapse
// Actualiza campos de suma en tabla maestra a partir de tabla de detalle
// Parámetros: maestro, PK, campos_maestro, detalle, FK, campos_detalle
oCn:UpdateSummary( "facturas",  "id",  "total,cantidad", ;
                   "lineas",    "fid", "importe,qty" )

// Ver el SQL que se genera
? MYSQL_UpdateSummarySQL( "facturas", "id", "total,cantidad", ;
                          "lineas",   "fid", "importe,qty" )

Útil para mantener totales en cabeceras de facturas/pedidos actualizados automáticamente desde las líneas de detalle.

Ejemplo completo — maria08.prg

Code (clipper): Select all Collapse
// Crear tabla maestra con totales
oCn:CreateTable( "test_mas", { ;
   { "quantity",  'N', 14, 0 }, ;
   { "value",     'N', 14, 0 } } )

oCn:Insert( "test_mas", "quantity,value", ;
   { { 10, 10 }, { 20, 20 }, { 30, 30 }, { 40, 40 }, { 50, 50 } } )

// Crear tabla detalle con clave foránea
oCn:CreateTable( "test_det", { ;
   { "pid", "REFERENCES test_mas( id )" }, ;
   { "qty",    'N', 10, 0 }, ;
   { "val",    'N', 10, 0 } } )

// Insertar transacciones y actualizar resúmenes
oCn:Insert( "test_det", "pid,qty,val", aDet )
oCn:UpdateSummary( "test_mas", "id", "quantity,value", ;
                   "test_det", "pid", "qty,val" )
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:03 PM

6. Manejo de Tablas

← CRUD | Índice | Siguiente: Padre-Hijo →


---

6.1 Crear tabla

Code (clipper): Select all Collapse
// Estructura: { nombre, tipo, longitud, decimales [, opciones] }
oCn:CreateTable( "clientes", { ;
   { "nombre",   'C', 30, 0 }, ;
   { "ciudad",   'C', 20, 0 }, ;
   { "salario",  'N', 10, 2, "DEFAULT 0" }, ;
   { "activo",   'L',  1, 0 }, ;
   { "fecha",    'D',  8, 0 }, ;
   { "notas",    'M',  0, 0 } } )

Tipos de datos soportados

Tipo FWHTipo MariaDBDescripción
CVARCHARCarácter / texto corto
NDECIMALNumérico
DDATEFecha
LTINYINT(1)Lógico (booleano)
MTEXT / BLOBMemo / texto largo
+INT AUTO_INCREMENTEntero autoincrementable (clave primaria)
=TIMESTAMPFecha/hora automática (se actualiza al modificar)
@DATETIMEFecha y hora
^BLOBDatos binarios

6.2 Opciones especiales en columnas

Code (clipper): Select all Collapse
// Auto-increment (clave primaria automática — se añade siempre como campo "id")
{ "id", "+", 4, 0 }

// Timestamp automático (se actualiza al insertar/modificar)
{ "modificado", "=", 8, 0 }

// Comentarios para comportamiento especial
{ "codigo",  'C', 10, 0, "comment 'case:upper'" }   // fuerza mayúsculas
{ "nombre",  'C', 30, 0, "comment 'case:proper'" }   // primera letra mayúscula

// Con valor por defecto
{ "cantidad", 'N', 6, 3, "DEFAULT 1" }
{ "rate",     'N', 5, 2, "DEFAULT 10" }

// Columna computada (virtual)
{ "amount=qty*rate", 'N', 12, 2 }    // amount se calcula automáticamente

// Con charset específico por campo
{ "texto_utf8",  'C', 40, 0, "utf8" }
{ "texto_latin", 'C', 40, 0, "latin1" }
{ "idioma",      'C', 15, 0, "latin1 comment 'case:upper'" }

// Clave foránea (referencia a otra tabla)
{ "pid", "REFERENCES tabla_padre( id )" }

Ejemplo avanzado — maria09.prg

Code (clipper): Select all Collapse
oCn:CreateTable( "factura_det", { ;
   { "docid",  'N',  4, 0 }, ;
   { "code",   'C',  2, 0, "comment 'case:upper'"  }, ;
   { "name",   'C', 20, 0, "comment 'case:proper'" }, ;
   { "qty",    'N',  6, 3, "DEFAULT 1" }, ;
   { "rate",   'N',  5, 2, "DEFAULT 10" }, ;
   { "amount=qty*rate", 'N', 12, 2 } }, ;
   .t., "latin1" )   // .t. = con PK auto_increment, charset latin1

6.3 Verificar, listar y eliminar tablas

Code (clipper): Select all Collapse
// Verificar si existe
if oCn:TableExists( "clientes" )
   ? "La tabla existe"
endif

// Eliminar tabla
oCn:DropTable( "clientes" )

// Patrón seguro: verificar antes de crear
if .not. oCn:TableExists( "productos" )
   oCn:CreateTable( "productos", aCols )
endif

// Listar todas las tablas
aTables := oCn:ListTables()
XBROWSER aTables TITLE "Tablas disponibles"

// Obtener estructura
aStruct := oCn:TableStructure( "clientes" )

6.4 Crear tabla con SQL directo

Para tablas complejas se puede usar SQL directo:

Code (clipper): Select all Collapse
TEXT INTO cSql
CREATE TABLE test_aes_encrypt (
   `id`    int(11) NOT NULL AUTO_INCREMENT,
   `name`  varchar(20) DEFAULT NULL,
   `login` tinyblob,
   `pass`  varbinary(255),
   PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=latin1
ENDTEXT

oCn:Execute( cSql )

6.5 Crear triggers

Code (clipper): Select all Collapse
TEXT INTO cSql
CREATE TRIGGER mitabla_bi BEFORE INSERT ON mitabla
FOR EACH ROW
BEGIN
   SET NEW.username = SUBSTRING(CONCAT_WS(', ', @os_user, @pc_name), 1, 30);
END
ENDTEXT

oCn:Execute( "DROP TRIGGER IF EXISTS mitabla_bi" )
oCn:Execute( cSql )
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:04 PM

7. Relaciones Padre-Hijo

← Tablas | Índice | Siguiente: Avanzadas →


---

7.1 Un padre con un hijo — maria04.prg

Método 1: AddChild con SQL

Code (clipper): Select all Collapse
oStates := oCn:RowSet( "states" )
oStates:AddChild( "SELECT * FROM customer WHERE state = states.code" )

// Sincronizar al cambiar de registro padre
oBrwParent:bChange := { || oStates:SyncChild(), ;
                           oBrwChild:GoTop(), oBrwChild:Refresh() }

Método 2: AddChild con RowSet existente

Code (clipper): Select all Collapse
oStates := oCn:RowSet( "states" )
oCust   := oCn:RowSet( "customer" )
oStates:AddChild( oCust, "state = states.code" )

Ejemplo completo

Code (clipper): Select all Collapse
oStates := oCn:RowSet( "states" )
oStates:AddChild( "SELECT * FROM customer WHERE state = states.code" )

DEFINE DIALOG oDlg SIZE 900,600 PIXEL TRUEPIXEL FONT oFont ;
   TITLE "FWH MYSQL PARENT CHILD ROWSET"

@ 20,20 XBROWSE oBrwParent SIZE 250,-20 PIXEL OF oDlg ;
   DATASOURCE oStates COLUMNS "Code", "Name" CELL LINES NOBORDER

WITH OBJECT oBrwParent
   :nStretchCol := 2
   :bChange := { || oStates:SyncChild(), ;
                    oBrwChild:GoTop(), oBrwChild:Refresh() }
   :CreateFromCode()
END

@ 20,270 XBROWSE oBrwChild SIZE -20,-20 PIXEL OF oDlg ;
   DATASOURCE oStates:oChild ;
   COLUMNS "First", "City", "State", "Salary" CELL LINES NOBORDER

WITH OBJECT oBrwChild
   :CreateFromCode()
END

ACTIVATE DIALOG oDlg CENTERED

7.2 Un padre con dos hijos (SetFilter) — maria16.prg

Code (clipper): Select all Collapse
oRsState := oCn:RowSet( "SELECT * FROM states ORDER BY name" )
oRsCity  := oCn:RowSet( "SELECT id, state, city FROM customer ORDER BY state, city" )
oRsCust  := oCn:RowSet( "SELECT id, state, CONCAT_WS(', ', first, last) AS Name " + ;
            "FROM customer ORDER BY state, Name" )

// Filtros parametrizados vinculados al padre
oRsCity:SetFilter( "STATE = ?", { oRsState:code } )
oRsCust:SetFilter( "STATE = ?", { oRsState:code } )

// Al cambiar en el padre, re-filtrar ambos hijos
oBrwState:bChange := <||
   oRsCity:ReFilter( { oRsState:code } )
   oRsCust:ReFilter( { oRsState:code } )
   oBrwCity:Refresh()
   oBrwCust:Refresh()
   return nil
>

// Cada browse hijo tiene su cabecera
oBrwState:SetGroupHeader( "PARENT", 1, 2 )
oBrwCity:SetGroupHeader( "CHILD-1", 1, 2 )
oBrwCust:SetGroupHeader( "CHILD-2", 1, 2 )

7.3 También usando OrdSetRelation

Code (clipper): Select all Collapse
// Sintaxis alternativa (equivalente a AddChild)
oStates:OrdSetRelation( oChildRs, "state = ?", { oStates:code } )
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:05 PM

8. Funciones Avanzadas

← Padre-Hijo | Índice | Siguiente: AES →


---

8.1 AutoAppend: agregar registros desde el browse — maria09.prg

Code (clipper): Select all Collapse
oRs := oCn:RowSet( "SELECT * FROM detalle WHERE docid = ?", { nDocId } )
oRs:lAutoAppend := .t.              // habilita agregar con flecha abajo
oRs:SetDefault( "docid", nDocId )   // valor fijo para campo clave

// Para cambiar de documento más tarde:
// oRs:ReQuery( { nOtherDocId } )
// oRs:SetDefault( "docid", nOtherDocId )

XBROWSER oRs FASTEDIT TITLE "Detalle del documento" ;
   SETUP SetUpBrowse( oBrw )

static function SetUpBrowse( oBrw )
   WITH OBJECT oBrw
      // Color diferente para fila en proceso de agregar
      :bClrRowFocus := { || If( oBrw:oDbf:ID == 0, ;
         { CLR_BLACK, 0xAEEDFF }, { CLR_BLACK, RGB(185,220,255) } ) }
      // Totales en footers
      :lFooter := .t.
      :qty:nFooterType    := AGGR_SUM
      :Amount:nFooterType := AGGR_SUM
      :MakeTotals()
      // Mensaje cuando no hay datos
      :bPainted := { |dc,ps,brw| If( brw:nLen == 0, ;
         brw:SayText( "Press Down Arrow to Start" ), nil ) }
   END
return nil

8.2 EditBaseRecord: editar registro completo — maria15.prg

Cuando el RowSet contiene solo algunas columnas, EditBaseRecord() permite editar el registro completo de la tabla base con un formulario personalizado.

Code (clipper): Select all Collapse
oRs := oCn:RowSet( "SELECT ID,FIRST,CITY,SALARY FROM customer" )

// Botones para agregar y editar
@ 20, 20 BTNBMP PROMPT "ADD"  SIZE 100,30 PIXEL FLAT OF oDlg ;
   ACTION oRs:EditBaseRecord( nil, .t., { |oRec| MyEditDlg( oRec ) }, oBrw )

@ 20,130 BTNBMP PROMPT "EDIT" SIZE 100,30 PIXEL FLAT OF oDlg ;
   ACTION oRs:EditBaseRecord( nil, .f., { |oRec| MyEditDlg( oRec ) }, oBrw, .t. )
// Parámetros: cFieldList, lAppend, bEditDlg, oBrw [, lLock]

static function MyEditDlg( oRec )
   local lNew := ( oRec:RecNo == 0 )

   DEFINE DIALOG oDlg SIZE 400,470 PIXEL TRUEPIXEL FONT oFont ;
      TITLE If( lNew, "ADD NEW", "EDIT" ) + " RECORD"

   @ 060,120 GET oRec:First   SIZE 240,22 PIXEL OF oDlg VALID !Empty(oRec:First)
   @ 090,120 GET oRec:Last    SIZE 240,22 PIXEL OF oDlg VALID !Empty(oRec:Last)
   @ 120,120 GET oRec:Street  SIZE 240,22 PIXEL OF oDlg
   @ 150,120 GET oRec:City    SIZE 240,22 PIXEL OF oDlg
   @ 240,120 GET oRec:HireDate SIZE 240,22 PIXEL OF oDlg
   @ 270,120 CHECKBOX oRec:Married PROMPT "" SIZE 22,22 PIXEL OF oDlg
   @ 300,120 GET oRec:Age     SIZE 240,22 PIXEL OF oDlg PICTURE "99" RIGHT
   @ 330,120 GET oRec:Salary  SIZE 240,22 PIXEL OF oDlg PICTURE "999,999.99" RIGHT

   // DBCOMBO para seleccionar estado de una lista
   @ 180,120 DBCOMBO oRec:State SIZE 240,300 PIXEL OF oDlg ;
      ALIAS aStates ITEMFIELD "1" LISTFIELD "2"

   @ 420,020 BTNBMP PROMPT "Save" SIZE 150,30 PIXEL FLAT OF oDlg ;
      ACTION ( If( oRec:Modified(), oRec:Save(), nil ), oDlg:End() ) ;
      WHEN oRec:Modified()

   @ 420,240 BTNBMP PROMPT "Cancel" SIZE 150,30 PIXEL FLAT OF oDlg ;
      ACTION oDlg:End()

   ACTIVATE DIALOG oDlg CENTERED
return nil

8.3 Tabla Pivot — maria03.prg

Code (clipper): Select all Collapse
// Generar tabla pivot
// Parámetros: tabla, campo_fila, campo_columna, campo_valor, función_agregación
aPivot := oCn:PivotArray( "ventas", "region", "producto", "importe", "SUM" )
// La función de agregación puede ser: "SUM", "COUNT", "AVG"

// Mostrar e invertir ejes
@ 30,10 XBROWSE oBrw SIZE -10,-10 PIXEL OF oDlg ;
   DATASOURCE aPivot AUTOCOLS CELL LINES FOOTERS NOBORDER

WITH OBJECT oBrw
   :bRClicked := { || oBrw:InvertPivot() }   // click derecho invierte
   :CreateFromCode()
END

// Botón para invertir
@ 08,10 BTNBMP PROMPT "Change View" PIXEL OF oDlg FLAT ;
   ACTION ( oBrw:InvertPivot(), oBrw:SetFocus() )

8.4 Batch Mode — ediciones masivas

Code (clipper): Select all Collapse
oRs:SetBatchMode( .t. )     // activar modo batch
// ... múltiples ediciones ...
oRs:SaveBatch()              // guardar todo de una vez
// o
oRs:CancelBatch()            // cancelar todas las ediciones
? oRs:IsBatchEdited()        // .t. si hay cambios pendientes

8.5 Carga de zonas horarias — maria07.prg

Code (clipper): Select all Collapse
FWCONNECT oCn HOST cHost USER cUser PASSWORD cPassword DATABASE "mysql"
oCn:lLogErr := .t.

// Cargar archivo timezone_posix.sql (descargable de dev.mysql.com)
hFile := FOpen( "timezone_posix.sql", 0 )
cText := FReadStr( hFile, FileSize( "timezone_posix.sql" ) + 10000 )

// Ejecutar cada sentencia del archivo
do while ( nUpto := HB_At( ";" + CHR(10), cText, nFrom ) ) > 0
   cSql  := SubStr( cText, nFrom, nUpto - nFrom )
   oCn:Execute( cSql )
   if oCn:nError != 0
      oCn:ShowError()
      exit
   endif
   nFrom := nUpto + 2
enddo
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:05 PM

9. Encriptación AES

← Avanzadas | Índice | Siguiente: GEO →


---

MariaDB incluye funciones nativas AES_ENCRYPT() y AES_DECRYPT() que permiten almacenar datos sensibles como contraseñas y credenciales de forma segura.

9.1 Crear tabla con campos encriptados — mariaaes.prg

Code (clipper): Select all Collapse
TEXT INTO cSql
CREATE TABLE `test_aes_encrypt` (
   `id`    int(11) NOT NULL AUTO_INCREMENT,
   `name`  varchar(20) DEFAULT NULL,
   `login` tinyblob,
   `pass`  varbinary(255),
   PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=latin1
ENDTEXT
oCn:Execute( cSql )

Nota: Los campos encriptados deben ser de tipo TINYBLOB, BLOB o VARBINARY, ya que AES_ENCRYPT() produce datos binarios.

9.2 Insertar datos encriptados

Code (clipper): Select all Collapse
cSql := "INSERT INTO test_aes_encrypt ( name, login, pass ) VALUES ( " + ;
        "'andrew', AES_ENCRYPT( 'Andrew', 'myencryptkey' ), " + ;
        "AES_ENCRYPT( 'Pass123', 'myencryptkey' ) )"
oCn:Execute( cSql )

// Actualizar un campo encriptado
cSql := "UPDATE test_aes_encrypt SET pass = " + ;
        "AES_ENCRYPT( 'NewPass@456', 'myencryptkey' ) WHERE id = 1"
oCn:Execute( cSql )

9.3 Consultar con desencriptación

Code (clipper): Select all Collapse
cSql := "SELECT id, name, " + ;
        "AES_DECRYPT( login, 'myencryptkey' ) AS login, " + ;
        "AES_DECRYPT( pass, 'myencryptkey' ) AS pass " + ;
        "FROM test_aes_encrypt"
oRs := oCn:RowSet( cSql )
XBROWSER oRs
oRs:Close()
oCn:Close()

Seguridad: La clave de encriptación nunca se almacena en la base de datos. Guárdala de forma segura en tu aplicación o en un archivo de configuración externo.


---

10. Datos Espaciales (GEO)

← AES | Índice | Siguiente: Unicode →


---

MariaDB 5.7.1+ soporta el tipo de dato POINT y funciones geométricas para calcular distancias entre coordenadas geográficas.

10.1 Crear tabla y insertar coordenadas — mariageo.prg

Code (clipper): Select all Collapse
oCn:Execute( "CREATE TABLE citylatlong (" + ;
   "id INT AUTO_INCREMENT PRIMARY KEY, " + ;
   "city VARCHAR(20), pt POINT )" )

TEXT INTO cSql
INSERT INTO citylatlong (city, pt) VALUES
   ( "Hyd",    POINT( 78.4867, 17.3850 ) ),
   ( "Mumbai", POINT( 72.8777, 19.0760 ) ),
   ( "Delhi",  POINT( 77.1025, 20.7041 ) ),
   ( "London", POINT(  0.1278, 51.5074 ) ),
   ( "Paris",  POINT(  2.3522, 48.8566 ) ),
   ( "Madrid", POINT(  3.7038, 40.4168 ) )
ENDTEXT
oCn:Execute( cSql )

10.2 Crear función de distancia en el servidor

Code (clipper): Select all Collapse
oCn:Execute( "DROP FUNCTION IF EXISTS distance_between" )

TEXT INTO cSql
CREATE FUNCTION distance_between( city1 VARCHAR(20), city2 VARCHAR(20) )
RETURNS DOUBLE
BEGIN
   DECLARE p1 POINT;
   DECLARE p2 POINT;
   SELECT pt INTO p1 FROM citylatlong WHERE city = city1;
   SELECT pt INTO p2 FROM citylatlong WHERE city = city2;
   RETURN ST_Distance_Sphere( p1, p2 );
END;
ENDTEXT
oCn:Execute( cSql )

10.3 Usar desde FiveWin

Code (clipper): Select all Collapse
// Consultar coordenadas
oRs := oCn:RowSet( "SELECT id, city, X(pt) AS Longitude, " + ;
       "Y(pt) AS Lattitude FROM citylatlong" )
XBROWSER oRs

// Llamar función de distancia (resultado en metros)
? oCn:distance_between( "London", "Paris" )
? oCn:distance_between( "Madrid", "Paris" )
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 06:15 PM

Todos los ficheros con la documentación para poder dársela a la IA:

https://github.com/FiveTechSoft/FWH_tools/blob/master/manual_mariadb.zip

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 08:47 PM

MANUAL DE MARIADB

para FiveWin / Harbour

Guia Practica Completa: Conexion, Consultas, CRUD, Migracion y Funciones Avanzadas

Basado en los ejemplos oficiales de FiveWin (maria01.prg \- maria20.prg)

Version 2025

Contenido

1\. Introduccion

2\. Conexion a MariaDB

3\. Comandos y Funciones Principales

4\. RowSet: Consultas y Edicion de Datos

5\. Operaciones CRUD

6\. Manejo de Tablas

7\. Relaciones Padre-Hijo

8\. Funciones Avanzadas

9\. Encriptacion AES

10\. Datos Espaciales (GEO)

11\. Unicode y Codificaciones

12\. Importar DBF a MariaDB

13\. FWMariaRecord: Edicion de Registros Individuales

14\. Comparacion FWH vs ADO vs Dolphin

15\. RecSet: Tablas Grandes y Rendimiento

16\. Bloqueo de Registros (Locking)

17\. Replicacion entre Servidores

18\. Sincronizacion DBF \<-\> MariaDB

19\. Totales Acumulados (Running Totals)

20\. Query Browser Interactivo

21\. Portar una Aplicacion DBF a MariaDB

22\. Referencia Rapida y Constantes

23\. Clase TField: Metadatos de Campos

1\. Introduccion

FiveWin for Harbour (FWH) incluye soporte nativo para MariaDB/MySQL a traves de la funcion maria\_Connect() y un conjunto de clases que permiten trabajar con bases de datos relacionales de forma sencilla, sin necesidad de drivers ODBC ni componentes externos.

Caracteristicas principales

  • Conexion directa al servidor MariaDB/MySQL via protocolo nativo TCP/IP

  • RowSets editables con soporte completo CRUD (Create, Read, Update, Delete)

  • RecSets paginados para tablas con millones de registros

  • Integracion total con XBrowse para visualizacion y edicion interactiva

  • Soporte Unicode (UTF-8) nativo

  • Tablas Pivot, relaciones padre-hijo, replicacion, cifrado AES, datos geoespaciales

  • Importacion/Exportacion desde/hacia ficheros DBF

  • Compatibilidad con Harbour y xHarbour, 32 y 64 bits

Requisitos

  • FiveWin for Harbour 25.x o superior

  • Servidor MariaDB 10.x / MySQL 5.7+ accesible en red o local

  • Incluir fivewin.ch en el programa

  • Habilitar Unicode: FW\_SetUnicode( .t. ) si se usan caracteres especiales

Ficheros necesarios

FicheroDescripcion
libmariadb.dllDLL cliente MariaDB (32 bits)
libmariadb64.dllDLL cliente MariaDB (64 bits)
libmariadb.lib / libmariadb32.libLibreria de enlace (32 bits)
libmariadb64.lib / libmariadb64.aLibreria de enlace (64 bits)
fwmaria.prgCodigo fuente de las clases principales
mariarec.prgCodigo fuente de la clase FWMariaRecord

La DLL libmariadb.dll debe estar en la misma carpeta que el .exe o en una carpeta del PATH del sistema.

2\. Conexion a MariaDB

2.1 Funcion maria\_Connect()

La funcion principal para establecer la conexion es maria\_Connect(). Devuelve un objeto de conexion o NIL si falla.

Sintaxis

oCn := maria\_Connect( cConexion, lMostrarError )

oCn := maria\_Connect( cHost, cDatabase, cUser, cPassword \[, nPort\] )

oCn := maria\_Connect( { cHost, cDB, cUser, cPassword } )

Ejemplos de conexion

// Forma 1: cadena compacta host:puerto,base,usuario,password

oCn := maria\_Connect( "208.91.198.197:3306,fwhdemo,usuario,pwd", .t. )

// Forma 2: parametros separados (servidor local)

oCn := maria\_Connect( "localhost", "fwh", "usuario", "1234" )

// Forma 3: via comando FWCONNECT (fivewin.ch)

FWCONNECT oCn HOST cHost USER cUser PASSWORD cPassword DATABASE cDB

// Forma 4: array de parametros

oCn := maria\_Connect( { "localhost", "mibase", "root", "clave" } )

// Forma 5: funcion de demo de FiveWin

oCn := FW\_DemoDB() // servidor de demo

oCn := FW\_DemoDB( "ADO" ) // via ADO

oCn := FW\_DemoDB( "DLP" ) // via TDolphin

// Forma 6: conexion embebida (sin servidor, solo 32 bits)

oCn := maria\_Embedded( cDataFolder, "mibase", cLangFolder )

Verificar conexion y cerrar

if ( oCn := maria\_Connect( "host,db,user,pwd", .t. ) ) \== nil

MsgStop( "No se pudo conectar" )

return nil

endif

// ... operaciones ...

oCn:Close() // o oCn:End()

2.2 Propiedades del objeto de conexion

PropiedadTipoDescripcion
oCn:lOpenL.t. si la conexion esta activa
oCn:cDBCNombre de la base de datos actual
oCn:lLogErrLActiva el registro de errores en archivo .log
oCn:lShowErrorsLMuestra errores en pantalla automaticamente
oCn:nErrorNCodigo del ultimo error (0 \= sin error)
oCn:LockTimeOutNTiempo de espera para bloqueos (segundos)
oCn:lUnicodeLModo Unicode activo
oCn:cLastSQLCUltima sentencia SQL ejecutada
oCn:pMySqlPPuntero interno a la conexion MySQL

oCn:lLogErr := .t. // activa log de errores

oCn:lShowErrors := .t. // muestra errores en pantalla

aErr := maria\_ConnectError() // { nErrorCode, cErrorMsg } del ultimo intento

3\. Comandos y Funciones Principales

3.1 Metodos del objeto de conexion

MetodoDescripcion
oCn:RowSet( cSQL \[, aParams\] )Ejecuta consulta y devuelve un RowSet editable en memoria
oCn:RecSet( cTabla, nRecs )Lee n registros con paginacion (tablas grandes)
oCn:Query( cSQL )Ejecuta consulta (compatible TDolphin)
oCn:Execute( cSQL )Ejecuta sentencia SQL sin resultado (INSERT, UPDATE, DDL...)
oCn:QueryResult( cSQL )Devuelve el valor de la primera celda del resultado
oCn:ListTables()Devuelve array con nombres de tablas de la BD
oCn:TableExists( cTabla )Devuelve .t. si la tabla existe
oCn:TableStructure( cTabla )Devuelve estructura de la tabla
oCn:CreateTable( cTabla, aStruct \[,lPK\] \[,cCharset\] )Crea una tabla nueva
oCn:DropTable( cTabla )Elimina una tabla
oCn:Insert( cTabla, cCampos, aData )Inserta uno o multiples registros
oCn:Upsert( cTabla, nil, aVals )INSERT ... ON DUPLICATE KEY UPDATE
oCn:UpdateSummary( ... )Actualiza totales de tabla maestra desde detalle
oCn:ImportFromDbf( cArchivo \[,...\] )Importa un archivo DBF a MariaDB
oCn:CopyTableToServer( cTabla, oOtroCn )Copia tabla a otro servidor
oCn:PivotArray( cTabla, cFila, cCol, cVal, cAggr )Genera tabla pivot
oCn:BeginTransaction()Inicia transaccion
oCn:CommitTransaction()Confirma transaccion
oCn:RollBack()Revierte transaccion
oCn:SetAutoCommit( lVal )Activa/desactiva auto commit
oCn:Close() / oCn:End()Cierra la conexion
oCn:ShowError()Muestra el ultimo error
oCn:ValToSql( uVal )Convierte valor Harbour a literal SQL
oCn:ApplyParams( cSql, aParams )Sustituye ? por valores en SQL

3.2 Configuracion inicial recomendada

\#include "fivewin.ch"

function Main()

local oCn

SET DATE ITALIAN // formato dd/mm/yyyy

SET CENTURY ON

FW\_SetUnicode( .t. ) // habilitar Unicode

FWNumFormat( "A", .t. ) // formato numerico

oCn := maria\_Connect( "localhost", "mibd", "usuario", "clave" )

if oCn \== nil

  MsgStop( "Error de conexion" )

  return nil

endif

oCn:lShowErrors := .t.

// ... codigo ...

oCn:Close()

return nil

3.3 Transacciones

// Metodo 1: desactivar auto-commit

oCn:SetAutoCommit( .f. )

// ... multiples INSERT/UPDATE ...

oCn:SetAutoCommit( .t. ) // commit implicito

// Metodo 2: transacciones explicitas

oCn:BeginTransaction()

oCn:Execute( "UPDATE cuentas SET saldo \= saldo \- 1000 WHERE id \= 1" )

oCn:Execute( "UPDATE cuentas SET saldo \= saldo \+ 1000 WHERE id \= 2" )

if oCn:nError \== 0

oCn:CommitTransaction()

else

oCn:RollBack()

endif

4\. RowSet: Consultas y Edicion de Datos

El RowSet es el objeto central para trabajar con datos en FiveWin/MariaDB. Representa un conjunto de registros cargados en memoria que puede mostrarse en un XBrowse y editarse directamente. Permite ordenacion y filtrado local sin consultas adicionales al servidor.

4.1 Crear un RowSet

// Tabla completa

oRs := oCn:RowSet( "clientes" )

// Acceso directo por nombre de tabla

oRs := oCn:clientes // equivale a oCn:RowSet('clientes')

// Con SQL personalizado

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE activo \= 1" )

// SQL con parametros (evita SQL injection)

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE estado \= ?", { cEstado } )

// JOIN entre tablas

TEXT INTO cSql

SELECT C.ID AS CustID, C.FIRST AS CustName,

      C.STATE AS ST, S.NAME AS StateName

FROM customer C

LEFT OUTER JOIN states S ON C.STATE \= S.CODE

ORDER BY CUSTID

ENDTEXT

oRs := oCn:RowSet( cSql )

4.2 Mostrar en XBrowse

// Modo simple

XBROWSER oRs FASTEDIT TITLE "Clientes"

// Con opciones

XBROWSER oRs FASTEDIT AUTOSORT AUTOFIT SHOW RECID TITLE "Clientes" ;

SETUP ( oBrw:lFastEdit := .f., oBrw:aCols\[4\]:cEditPicture := "@\!" )

// Control total programatico

@ 50,20 XBROWSE oBrw SIZE \-20,-20 PIXEL OF oDlg ;

DATASOURCE oRs AUTOCOLS CELL LINES NOBORDER FOOTERS FASTEDIT

WITH OBJECT oBrw

:nEditTypes := EDIT\_GET

:Salary:nFooterType := AGGR\_SUM

:MakeTotals()

:CreateFromCode()

END

4.3 Navegacion y acceso a campos

MetodoDescripcion
oRs:GoTop() / GoBottom()Ir al primer/ultimo registro
oRs:Skip(n)Saltar n registros
oRs:GoTo(nRec)Ir al registro nRec
oRs:Bof() / Eof()Inicio/fin de datos
oRs:RecNo()Numero de registro actual (ID del servidor)
oRs:KeyCount() / LastRec()Total de registros
oRs:BookMarkLeer/asignar marcador de posicion
oRs:campoLeer/escribir campo directamente (forma mas comoda)
oRs:FieldGet(n) / FieldPut(n,v)Por posicion numerica
oRs:FieldGet("NOMBRE")Por nombre de campo
oRs:FCount()Numero total de campos
oRs:FieldName(n) / FieldType(n)Nombre y tipo del campo n

4.4 Ordenacion, filtros y busqueda

// Ordenar

oRs:SetOrder( "FIRST" ) // ascendente

oRs:SetOrder( "SALARY", , .t. ) // descendente

// Filtrar (localmente sobre datos ya leidos)

oRs:SetFilter( "STATE \= ?", { "CA" } )

oRs:ReFilter( { "NY" } ) // re-filtrar con nuevo parametro

// Busqueda

oRs:Seek( "John" ) // exacto

oRs:Seek( "Jo", .t. ) // parcial (soft)

oRs:Locate( {|| oRs:Salary \> 50000 } )

4.5 Guardado, recarga y exportacion

oRs:Save() // genera UPDATE solo para columnas cambiadas

oRs:Cancel() // cancela cambios pendientes

oRs:Delete() // borra registro actual

oRs:AppendBlank() // agrega registro en blanco

oRs:ReSync() // re-leer registro actual del servidor

oRs:ReQuery() // re-leer todos los datos con mismo SQL

oRs:ReQuery( { 'NY' } ) // re-leer con nuevos parametros

oRs:ReQuery( cNewSql ) // re-leer con nuevo SQL

oRs:ToExcel() // exportar a Excel

cJson := oRs:ToJson() // exportar a JSON

aRows := oRs:GetRows() // obtener como array

4.6 Propiedades importantes del RowSet

PropiedadTipoDescripcion
oRs:SourceCSQL fuente del RowSet
oRs:lReadOnlyL.t. si el RowSet es de solo lectura
oRs:lAutoAppendLHabilitar auto-agregar desde browse
oRs:lBatchModeLModo batch para ediciones masivas
oRs:nReadSecsNSegundos que tardo la lectura
oRs:aPrimaryAColumnas de clave primaria
oRs:oChildORowSet hijo enlazado
oRs:oReplServerOServidor de replicacion
oRs:cLastSQLCUltima sentencia SQL ejecutada
oRs:bTriggerBBloque trigger antes de guardar

5\. Operaciones CRUD

5.1 INSERT \- Insertar registros

// Un registro

"clientes", "nombre,ciudad,salario", { "Juan Lopez", "Madrid", 3500 }

oCn:Insert( "clientes", "nombre,ciudad,salario", ;

{ { "Juan Lopez", "Madrid", 3500 } } )

// Multiples registros de una vez (mucho mas eficiente)

local aData := {}

for i := 1 to 1000

AAdd( aData, { "Nombre" \+ Str(i), "Ciudad", HB\_RandomInt(1000,9999) } )

next

oCn:SetAutoCommit( .f. ) // inicio de transaccion

"clientes", "nombre,ciudad,salario", aData )

oCn:Insert( "clientes", "nombre,ciudad,salario", aData )

oCn:SetAutoCommit( .t. ) // commit

Upsert: INSERT o UPDATE automatico

// Inserta si no existe, actualiza si ya existe (basado en clave primaria)

oCn:Upsert( "clientes", nil, aValues )

5.2 SELECT \- Consultar datos

// Valor unico (escalar)

nTotal := oCn:QueryResult( "SELECT COUNT(\*) FROM clientes" )

nSuma := oCn:QueryResult( "SELECT SUM(salario) FROM clientes" )

// Resultado como array

aEstados := oCn:Execute( "SELECT CODE, NAME FROM states" )

// RowSet completo

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE ciudad \= ?", { cCiudad } )

5.3 UPDATE \- Actualizar datos

// Directo via SQL

oCn:Execute( "UPDATE clientes SET salario \= salario \* 1.10 WHERE ciudad \= 'Madrid'" )

// A traves del RowSet

oRs := oCn:RowSet( "SELECT \* FROM clientes WHERE id \= ?", { nId } )

oRs:salario := 4000

oRs:Save() // genera UPDATE solo para columnas cambiadas

5.4 DELETE \- Eliminar registros

// Via SQL

oCn:Execute( "DELETE FROM clientes WHERE id \= " \+ cValToChar( nId ) )

// Via RowSet

oRs:Delete()

oRs:ReQuery() // recargar datos

// Desde boton en browse

@ 20,20 BTNBMP PROMPT "Delete" SIZE 60,20 PIXEL FLAT OF oDlg ;

ACTION ( oRs:Delete(), oRs:ReQuery(), oBrw:MakeTotals(), oBrw:Refresh() )

5.5 Actualizar tabla maestra con totales del detalle

// Parametros: maestro, PK, campos\_maestro, detalle, FK, campos\_detalle

"facturas", "id", "total,cantidad", ;

oCn:UpdateSummary( "facturas", "id", "total,cantidad", ;

               "lineas",   "fid", "importe,qty" )

// Ver el SQL generado

? MYSQL\_UpdateSummarySQL( "facturas", "id", "total,cantidad", ;

                      "lineas",   "fid", "importe,qty" )

Util para mantener totales en cabeceras de facturas/pedidos actualizados automaticamente desde las lineas de detalle.

6\. Manejo de Tablas

6.1 Crear tabla

// Estructura: { nombre, tipo, longitud, decimales \[, opciones\] }

oCn:CreateTable( "clientes", { ;

{ "nombre", 'C', 30, 0 }, ;

{ "ciudad", 'C', 20, 0 }, ;

{ "salario", 'N', 10, 2, "DEFAULT 0" }, ;

{ "activo", 'L', 1, 0 }, ;

{ "fecha", 'D', 8, 0 }, ;

{ "notas", 'M', 0, 0 } } )

6.2 Tipos de datos

Tipo FWHTipo MariaDBDescripcion
CVARCHARCaracter / texto corto
NDECIMALNumerico
DDATEFecha
LTINYINT(1)Logico (booleano)
MTEXT / BLOBMemo / texto largo
\+INT AUTO\_INCREMENTEntero autoincrementable (clave primaria)
\=TIMESTAMPFecha/hora automatica (se actualiza al modificar)
@DATETIMEFecha y hora
^BLOBDatos binarios

6.3 Opciones especiales en columnas

// Auto-increment (se agrega siempre como campo 'id')

{ "id", "+", 4, 0 }

// Con comentarios para comportamiento especial

{ "codigo", 'C', 10, 0, "comment 'case:upper'" }, // fuerza mayusculas

{ "nombre", 'C', 30, 0, "comment 'case:proper'" }, // primera letra mayuscula

// Columna computada (virtual)

{ "amount=qty\*rate", 'N', 12, 2 } // amount se calcula automaticamente

// Con charset especifico por campo

{ "texto\_utf8", 'C', 40, 0, "utf8" }

{ "texto\_latin", 'C', 40, 0, "latin1" }

// Clave foranea

{ "pid", "REFERENCES tabla\_padre( id )" }

6.4 Verificar, listar y eliminar tablas

if oCn:TableExists( "clientes" )

oCn:DropTable( "clientes" )

endif

// Listar todas las tablas

XBROWSER oCn:ListTables() TITLE "Tablas disponibles"

// Obtener estructura

aStruct := oCn:TableStructure( "clientes" )

6.5 Crear tabla con SQL directo y triggers

TEXT INTO cSql

CREATE TABLE usuarios (

id INT AUTO\_INCREMENT PRIMARY KEY,

nombre VARCHAR(30),

pass VARBINARY(255)

) ENGINE=InnoDB CHARSET=latin1

ENDTEXT

oCn:Execute( cSql )

// Trigger

TEXT INTO cSql

CREATE TRIGGER mitabla\_bi BEFORE INSERT ON mitabla

FOR EACH ROW

BEGIN

SET NEW.username \= SUBSTRING(CONCAT\_WS(', ', @os\_user, @pc\_name), 1, 30);

END

ENDTEXT

oCn:Execute( "DROP TRIGGER IF EXISTS mitabla\_bi" )

oCn:Execute( cSql )

7\. Relaciones Padre-Hijo

7.1 Un padre con un hijo

// Metodo 1: AddChild con SQL

oStates := oCn:RowSet( "states" )

oStates:AddChild( "SELECT \* FROM customer WHERE state \= states.code" )

// Sincronizar al cambiar de registro padre

oBrwParent:bChange := { || oStates:SyncChild(), ;

                       oBrwChild:GoTop(), oBrwChild:Refresh() }

// Metodo 2: AddChild con RowSet existente

oStates := oCn:RowSet( "states" )

oCust := oCn:RowSet( "customer" )

oStates:AddChild( oCust, "state \= states.code" )

// Metodo 3: OrdSetRelation (alternativo)

oStates:OrdSetRelation( oChildRs, "state \= ?", { oStates:code } )

7.2 Un padre con dos hijos (SetFilter)

oRsState := oCn:RowSet( "SELECT \* FROM states ORDER BY name" )

oRsCity := oCn:RowSet( "SELECT id, state, city FROM customer" )

oRsCust := oCn:RowSet( "SELECT id, state, first FROM customer" )

// Filtros parametrizados vinculados al padre

oRsCity:SetFilter( "STATE \= ?", { oRsState:code } )

oRsCust:SetFilter( "STATE \= ?", { oRsState:code } )

// Al cambiar en el padre, re-filtrar ambos hijos

oBrwState:bChange := \<||

oRsCity:ReFilter( { oRsState:code } )

oRsCust:ReFilter( { oRsState:code } )

oBrwCity:Refresh()

oBrwCust:Refresh()

return nil \>

8\. Funciones Avanzadas

8.1 AutoAppend: agregar registros desde el browse

oRs := oCn:RowSet( "SELECT \* FROM detalle WHERE docid \= ?", { nDocId } )

oRs:lAutoAppend := .t. // habilita agregar con flecha abajo

oRs:SetDefault( "docid", nDocId ) // valor fijo para campo clave

// Para cambiar de documento mas tarde:

// oRs:ReQuery( { nOtherDocId } )

// oRs:SetDefault( "docid", nOtherDocId )

XBROWSER oRs FASTEDIT TITLE "Detalle del documento" SETUP SetUpBrowse( oBrw )

static function SetUpBrowse( oBrw )

WITH OBJECT oBrw

  :bClrRowFocus := { || If( oBrw:oDbf:ID \== 0, ;

     { CLR\_BLACK, 0xAEEDFF }, { CLR\_BLACK, RGB(185,220,255) } ) }

  :lFooter := .t.

  :qty:nFooterType    := AGGR\_SUM

  :Amount:nFooterType := AGGR\_SUM

  :MakeTotals()

  :bPainted := { |dc,ps,brw| If( brw:nLen \== 0, brw:SayText( "Press Down Arrow" ), nil ) }

END

8.2 EditBaseRecord: editar registro completo

Cuando el RowSet contiene solo algunas columnas, EditBaseRecord() permite editar el registro completo de la tabla base con un formulario personalizado.

oRs := oCn:RowSet( "SELECT ID,FIRST,CITY,SALARY FROM customer" )

@ 20, 20 BTNBMP PROMPT "ADD" PIXEL FLAT OF oDlg ;

ACTION oRs:EditBaseRecord( nil, .t., { |oRec| MyEditDlg( oRec ) }, oBrw )

@ 20,130 BTNBMP PROMPT "EDIT" PIXEL FLAT OF oDlg ;

ACTION oRs:EditBaseRecord( nil, .f., { |oRec| MyEditDlg( oRec ) }, oBrw, .t. )

static function MyEditDlg( oRec )

local lNew := ( oRec:RecNo \== 0 )

@ 060,120 GET oRec:First SIZE 240,22 PIXEL OF oDlg VALID \!Empty(oRec:First)

@ 270,120 CHECKBOX oRec:Married PROMPT "" SIZE 22,22 PIXEL OF oDlg

@ 330,120 GET oRec:Salary SIZE 240,22 PIXEL OF oDlg PICTURE "999,999.99" RIGHT

// DBCOMBO para estado desde lista

@ 180,120 DBCOMBO oRec:State SIZE 240,300 PIXEL OF oDlg ALIAS aStates ITEMFIELD "1" LISTFIELD "2"

@ 420,020 BTNBMP PROMPT "Save" PIXEL FLAT OF oDlg ;

  ACTION ( If( oRec:Modified(), oRec:Save(), nil ), oDlg:End() ) WHEN oRec:Modified()

8.3 Tabla Pivot

// Parametros: tabla, campo\_fila, campo\_columna, campo\_valor, funcion\_agregacion

aPivot := oCn:PivotArray( "ventas", "region", "producto", "importe", "SUM" )

// Funciones: SUM, COUNT, AVG

@ 30,10 XBROWSE oBrw SIZE \-10,-10 PIXEL OF oDlg ;

DATASOURCE aPivot AUTOCOLS CELL LINES FOOTERS NOBORDER

WITH OBJECT oBrw

:bRClicked := { || oBrw:InvertPivot() } // click derecho invierte ejes

:CreateFromCode()

END

8.4 Batch Mode: ediciones masivas

oRs:SetBatchMode( .t. ) // activar modo batch

// ... multiples ediciones ...

oRs:SaveBatch() // guardar todo de una vez

// o

oRs:CancelBatch() // cancelar todas las ediciones

? oRs:IsBatchEdited() // .t. si hay cambios pendientes

9\. Encriptacion AES

MariaDB incluye funciones nativas AES\_ENCRYPT() y AES\_DECRYPT() para almacenar datos sensibles como contrasenas y credenciales de forma segura.

9.1 Crear tabla con campos encriptados

TEXT INTO cSql

CREATE TABLE \`usuarios\_enc\` (

\`id\` int(11) NOT NULL AUTO\_INCREMENT,

\`name\` varchar(20) DEFAULT NULL,

\`login\` tinyblob,

\`pass\` varbinary(255),

PRIMARY KEY (\`id\`)

) ENGINE=InnoDB CHARSET=latin1

ENDTEXT

oCn:Execute( cSql )

Los campos encriptados deben ser de tipo TINYBLOB, BLOB o VARBINARY, ya que AES\_ENCRYPT() produce datos binarios.

9.2 Insertar y actualizar datos encriptados

cSql := "INSERT INTO usuarios\_enc ( name, login, pass ) VALUES ( " \+ ;

    "'andrew', AES\_ENCRYPT( 'Andrew', 'miClave' ), " \+ ;

    "AES\_ENCRYPT( 'Pass123', 'miClave' ) )"

oCn:Execute( cSql )

cSql := "UPDATE usuarios\_enc SET pass \= " \+ ;

    "AES\_ENCRYPT( 'NuevoClave', 'miClave' ) WHERE id \= 1"

oCn:Execute( cSql )

9.3 Consultar con desencriptacion

cSql := "SELECT id, name, " \+ ;

    "AES\_DECRYPT(login,'miClave') AS login, " \+ ;

    "AES\_DECRYPT(pass, 'miClave') AS pass " \+ ;

    "FROM usuarios\_enc"

oRs := oCn:RowSet( cSql )

XBROWSER oRs

oRs:Close()

La clave de encriptacion nunca se almacena en la base de datos. Guardala de forma segura en tu aplicacion o en un archivo de configuracion externo.

10\. Datos Espaciales (GEO)

Requiere MariaDB/MySQL 5.7.1+. Soporta el tipo POINT y funciones geometricas para calcular distancias entre coordenadas geograficas.

10.1 Crear tabla e insertar coordenadas

oCn:Execute( "CREATE TABLE ciudades (" \+ ;

"id INT AUTO\_INCREMENT PRIMARY KEY, " \+ ;

"ciudad VARCHAR(20), pt POINT )" )

TEXT INTO cSql

INSERT INTO ciudades (ciudad, pt) VALUES

( 'Madrid', POINT( 3.7038, 40.4168 ) ),

( 'Paris', POINT( 2.3522, 48.8566 ) ),

( 'London', POINT( 0.1278, 51.5074 ) )

ENDTEXT

oCn:Execute( cSql )

10.2 Crear funcion de distancia en el servidor

oCn:Execute( "DROP FUNCTION IF EXISTS distancia" )

TEXT INTO cSql

CREATE FUNCTION distancia( ciudad1 VARCHAR(20), ciudad2 VARCHAR(20) )

RETURNS DOUBLE

BEGIN

 DECLARE p1 POINT;

 DECLARE p2 POINT;

 SELECT pt INTO p1 FROM ciudades WHERE ciudad \= ciudad1;

 SELECT pt INTO p2 FROM ciudades WHERE ciudad \= ciudad2;

 RETURN ST\_Distance\_Sphere( p1, p2 );

END

ENDTEXT

oCn:Execute( cSql )

// Llamar desde FiveWin (resultado en metros)

? oCn:distancia( "Madrid", "Paris" )

11\. Unicode y Codificaciones

11.1 Activar Unicode

FW\_SetUnicode( .t. ) // activar ANTES de conectar

oCn := maria\_Connect( "localhost", "mibd", "user", "pass" )

11.2 Charset por tabla y por campo

oCn:CreateTable( "testunicode", { ;

{ "language", 'C', 15, 0, "latin1 comment 'case:upper'" }, ;

{ "unicodetext", 'C', 40, 0, "utf8" }, ;

{ "username", 'C', 30, 0, "utf8" }, ;

{ "writedt", '=', 8, 0 } }, nil, "utf8" )

11.3 Charset en ImportFromDbf

oCn:ImportFromDbf( "clientes.dbf",,,,,, "utf8\_spanish2\_ci" )

11.4 Triggers para registrar usuario automaticamente

TEXT INTO cSql

CREATE TRIGGER tabla\_bi BEFORE INSERT ON mitabla

FOR EACH ROW

BEGIN

SET NEW.username \= SUBSTRING(CONCAT\_WS(', ', @os\_user, @pc\_name), 1, 30);

END

ENDTEXT

oCn:Execute( "DROP TRIGGER IF EXISTS tabla\_bi" )

oCn:Execute( cSql )

12\. Importar DBF a MariaDB

12.1 Importacion basica

RDDSETDEFAULT( "DBFCDX" )

SET DELETED ON

FW\_SetUnicode( .f. )

oCn := maria\_Connect( "localhost", "fwh", "usuario", "clave" )

oCn:lLogErr := .t.

oCn:ImportFromDbf( "clientes.dbf",,,,,, "utf8\_spanish2\_ci" )

oRs := oCn:RowSet( "clientes" )

XBROWSER oRs FASTEDIT AUTOSORT

// Revisar log de errores si existe

if File( cFileSetExt( ExeName(), "log" ) )

WinExec( "notepad.exe " \+ cFileSetExt( ExeName(), "log" ) )

endif

12.2 Copiar tabla entre servidores

oMain := FW\_DemoDB()

oRepl := FW\_DemoDB( 6 )

oMain:CopyTableToServer( "states", oRepl )

12.3 Proceso recomendado de migracion

  • Conectar a MariaDB con oCn

  • Activar log: oCn:lLogErr := .t.

  • Llamar a oCn:ImportFromDbf() para cada tabla DBF

  • Revisar el archivo .log por si hubiera errores de conversion

  • Verificar los datos importados con XBROWSER oCn:RowSet(cTabla)

  • Ajustar charsets si hay problemas con caracteres especiales

13\. FWMariaRecord: Edicion de Registros Individuales

La clase FWMariaRecord (mariarec.prg) hereda de TDataRow y permite leer, editar y guardar un registro individual de MariaDB. Es ideal para formularios de alta/edicion.

13.1 Crear y usar un FWMariaRecord

// Leer un registro existente

oRec := FWMariaRecord():New( oCn, "clientes", "id \= " \+ cValToChar( nId ) )

// Registro en blanco (para alta)

oRec := FWMariaRecord():New( oCn, "clientes" )

oRec:Blank()

// Acceder y modificar campos

oRec:nombre := "Juan Garcia"

oRec:salario := 3500

// Guardar cambios

if oRec:Modified()

if oRec:Save()

  ? "Guardado correctamente"

endif

endif

Save() usa internamente oCn:Upsert(): si el registro es nuevo hace INSERT, si existe hace UPDATE. Despues recarga el registro del servidor para obtener valores actualizados (auto-increment, timestamps, etc.).

13.2 Propiedades y metodos

Propiedad / MetodoDescripcion
oRec:lValidData.t. si el registro tiene datos validos
oRec:RecNoNumero de registro (0 \= nuevo)
oRec:oCnObjeto de conexion
oRec:cTableNombre de la tabla
oRec:aPrimaryIndices de campos de clave primaria
oRec:nAutoIncFldIndice del campo auto-incremento
oRec:New( oCn, cTabla, cWhere )Constructor
oRec:Read( cWhere )Carga un registro segun condicion WHERE
oRec:Blank()Carga un registro en blanco para insercion
oRec:Load( \[lBlank\] )Recarga el registro del servidor
oRec:Save()Guarda los cambios (INSERT o UPDATE segun corresponda)
oRec:Modified()Devuelve .t. si hubo cambios sin guardar
oRec:MakePrimaryWhere()Genera clausula WHERE desde clave primaria
oRec:campoAcceso directo a cada campo como propiedad

14\. Comparacion FWH vs ADO vs Dolphin

AspectoFWH NativoADOTDolphin
Conexionmaria\_Connect()FW\_DemoDB('ADO')FW\_DemoDB('DLP')
QueryoCn:RowSet(cSql)FW\_OpenRecordSet(oCn,cSql)oCn:Query(cSql)
Cerrar RSoRs:Close()oRs:Close()oRs:End()
Cerrar CNoCn:Close()oCn:Close()oCn:End()
Driver extraNo (nativo)Necesita ODBCNecesita TDolphin
Velocidad lecturaMuy altaMediaAlta
Trafico de redMinimoAltoMedio
EdicionCompleta \+ localVia servidorVia servidor
RecomendadoSi (primera opcion)Solo si ya existeCompatible

Para nuevas aplicaciones se recomienda siempre el conector nativo FWH (maria\_Connect). Es el mas rapido, no requiere drivers adicionales y tiene mejor integracion con las clases de FiveWin.

Benchmark de rendimiento

cSql := "SELECT \* FROM custbig WHERE id \<= 51000"

nSecs := SECONDS()

MsgRun( "Reading", "FWH", { || oRsFWH := oCn:RowSet( cSql ) } )

nFWHSecs := SECONDS() \- nSecs

nSecs := SECONDS()

MsgRun( "Reading", "ADO", { || oRsADO := FW\_OpenRecordSet( oAdo, cSql ) } )

nAdoSecs := SECONDS() \- nSecs

15\. RecSet: Tablas Grandes y Rendimiento

El RecSet esta disenado para tablas muy grandes (millones de registros). A diferencia del RowSet que carga todo en memoria, el RecSet usa paginacion para cargar solo una porcion a la vez, cargando mas datos bajo demanda cuando el usuario navega.

15.1 Creacion

// RecSet con pagina de 1000 registros

oRs := oCn:RecSet( "custbig", 1000 )

// RecSet leyendo los ultimos 100 registros primero

oRs := oCn:RecSet( "custbig", \-100 )

// Establecer el total de registros (para barra de scroll correcta)

oRs:nLastRec := oCn:QueryResult( "SELECT COUNT(\*) FROM custbig" )

// Ejemplo completo con tabla de millon de registros

nSecs := SECONDS()

MsgRun( "Reading custbig", "Please wait", { || ;

oRs := oCn:RecSet( "custbig", \-100 ), ;

nSum := oCn:QueryResult( "SELECT SUM(SALARY) FROM custbig" ) } )

nSecs := SECONDS() \- nSecs

XBROWSER oRs TITLE "CUSTBIG (" \+ cValToChar( nSecs ) \+ " seconds)" ;

FASTEDIT SHOW RECID ;

SETUP ( ;

oBrw:id:cEditPicture := "99,999,999", ;

oBrw:nFreeze := 1, ;

oBrw:Salary:nFooterType := AGGR\_SUM, ;

oBrw:Salary:nTotal := nSum )

15.2 Propiedades del RecSet

PropiedadTipoDescripcion
nPageSizeNTamano de pagina (por defecto 1000\)
nLastRecNUltimo registro total
nAtNPosicion actual dentro de la pagina
nOffsetNOffset de la pagina actual
nReadSecsNTiempo de lectura
aStructAEstructura de campos
aDataADatos de la pagina actual

16\. Bloqueo de Registros (Locking)

Para aplicaciones multiusuario, el bloqueo pesimista con FOR UPDATE garantiza que solo un usuario pueda editar un registro a la vez.

Patron de bloqueo pesimista

static function LockedEdit( oBrw )

local oRsMain, oRsEdit, oRec, nWait, oCn

oRsMain := oBrw:oDbf

oCn := oRsMain:oCn

// Guardar timeout actual y poner uno corto

nWait := oCn:QueryResult( "SHOW SESSION VARIABLES LIKE 'innodb\_lock\_wait\_timeout'" )\[ 2 \]

oCn:Execute( "SET SESSION innodb\_lock\_wait\_timeout \= 1" )

// Iniciar transaccion y bloquear el registro

oCn:BeginTransaction()

MsgRun( "Reading Record", "WAIT", { || ;

  oRsEdit := oCn:RowSet( ;

     "SELECT \* FROM customer WHERE ID \= ? FOR UPDATE", ;

     { oRsMain:ID } ) } )

if oCn:nError \== 0

  // Registro bloqueado OK \- editar

  oCn:Execute( "SET SESSION innodb\_lock\_wait\_timeout \= " \+ cValToChar( nWait ) )

  oRec := TDataRow():New( oRsEdit )

  oRec:Edit()

  oCn:CommitTransaction()

  oRsMain:ReSync()

  oBrw:RefreshCurrent()

else

  // No se pudo bloquear \- otro usuario lo tiene

  oCn:RollBack()

  ? "Registro bloqueado por otro usuario"

endif

17\. Replicacion entre Servidores

Con oRs:oReplServer, cada INSERT, UPDATE o DELETE ejecutado en el RowSet principal se replica automaticamente al servidor de replica.

oMain := FW\_DemoDB() // servidor principal

oRepl := FW\_DemoDB( 6 ) // servidor de replica

// Primera vez: copiar tabla al servidor replica

// oMain:CopyTableToServer( "states", oRepl )

oRsMain := oMain:RowSet( "states" )

oRsMain:oReplServer := oRepl // ACTIVAR REPLICACION

oRsRepl := oRepl:RowSet( "states" )

// Cada cambio en oRsMain se replica automaticamente a oRepl

oBrwMain:nEditTypes := EDIT\_GET

oBrwMain:bOnChanges := { || oRsRepl:ReSync(), oBrwRepl:RefreshCurrent() }

oBrwMain:lColChangeNotify := .t.

18\. Sincronizacion DBF \<-\> MariaDB

Permite mantener el DBF como fuente principal mientras todos los cambios se replican en tiempo real al servidor MariaDB, ideal para una migracion gradual.

oCn := FW\_DemoDB()

// Crear DBF e importarlo a MariaDB

oCn:DropTable( "statex" )

oCn:ImportFromDBF( "statex.dbf" )

// Abrir DBF y activar replicacion

oDbf := TDatabase():Open( nil, "statex", "DBFCDX", .t. )

oDbf:SetReplicationServer( oCn ) // \<- CLAVE

// Ahora cada modificacion en el DBF se replica a MariaDB

// El SQL generado se puede ver en:

? oCn:cLastSQL

Este patron es ideal para una migracion gradual: el DBF sigue funcionando y MariaDB recibe una copia en tiempo real. Ver ejemplo completo en mariasyn.prg.

19\. Totales Acumulados (Running Totals)

El patron @bal calcula saldos acumulados directamente en el servidor MariaDB, sin procesar en el cliente.

TEXT INTO cSql

SELECT id, fecha, descripcion, tipo, importe,

( @bal := IF( tipo \= '1', @bal \+ importe, @bal \- importe ) ) AS nsaldo

FROM ctacte,

( SELECT @bal := 0 ) AS t

WHERE ncli \= ?

ORDER BY fecha

ENDTEXT

oRs := oCn:RowSet( cSql, { 101 } ) // parametro: nCliente

oRs:GoBottom()

@ 50,20 XBROWSE oBrw SIZE \-20,-20 PIXEL OF oDlg ;

DATASOURCE oRs ;

COLUMNS 'id', 'Fecha', 'Descripcion', ;

  'If( tipo \== ' \+ Chr(39) \+ '1' \+ Chr(39) \+ ', importe, 0 )', ;

  'If( tipo \== ' \+ Chr(39) \+ '1' \+ Chr(39) \+ ', 0, importe )', ;

  'nsaldo' ;

HEADERS 'DocID', nil, nil, 'DEBE', 'PAGO', 'SALDO' ;

CELL LINES NOBORDER FOOTERS FASTEDIT

WITH OBJECT oBrw

:Debe:nFooterType := AGGR\_SUM

:Pago:nFooterType := AGGR\_SUM

:Saldo:bFooter := { || oBrw:Debe:nTotal \- oBrw:Pago:nTotal }

:MakeTotals()

:CreateFromCode()

END

20\. Query Browser Interactivo

Permite escribir cualquier SQL y ver los resultados inmediatamente en un XBrowse. Usa oRs:Requery(cSql) para cambiar la consulta dinamicamente.

FW\_SetUnicode( .t. )

oCn := FW\_DemoDB()

cSql := "SELECT Now()"

oRs := oCn:RowSet( cSql )

// Area de edicion SQL \- al validar re-ejecuta

@ 20,20 GET oGet VAR cSql MEMO SIZE 840,150 PIXEL OF oDlg FONT oFixed

oGet:bValid := \<|o|

if Upper( cSql ) \== Upper( oRs:Source )

  return .t.

endif

if oRs:Requery( cSql )

  oRs:SetXbrColumns( oBrw )

  oBrw:nEditTypes := EDIT\_GET

  oBrw:GoTop()

  oBrw:Refresh()

  return .t.

endif

MsgAlert( "Invalid SQL" )

return .f. \>

// Browse con resultados

@ 190,20 XBROWSE oBrw SIZE \-20,-20 PIXEL OF oDlg ;

DATASOURCE oRs AUTOCOLS CELL LINES NOBORDER FOOTERS FASTEDIT

21\. Portar una Aplicacion DBF a MariaDB

Migrar una aplicacion FWH de DBF a MariaDB puede hacerse tabla por tabla, de forma gradual. Las principales ventajas son: acceso multiusuario concurrente, SQL para consultas complejas, rendimiento superior con grandes volumenes e integridad referencial.

21.1 Tabla de Equivalencias DBF \-\> MariaDB

Operacion DBFEquivalente MariaDB/FWH
USE "clientes"oRs := oCn:RowSet("clientes")
APPEND BLANKoRs:AppendBlank()
DELETEoRs:Delete()
REPLACE field WITH valoRs:field := val; oRs:Save()
GO TOP / GO BOTTOMoRs:GoTop() / oRs:GoBottom()
SKIP noRs:Skip(n)
RECNO()oRs:RecNo()
RECCOUNT() / LASTREC()oRs:KeyCount() / oRs:LastRec()
BOF() / EOF()oRs:Bof() / oRs:Eof()
FIELD-\>nameoRs:name
SET INDEX TO / INDEX ONoRs:SetOrder('campo')
SEEK valoRs:Seek(val)
SET FILTER TO exproRs:SetFilter('campo \= ?', {val})
LOCATE FOR condoRs:Locate({oRs:campo \== val})
SET RELATION TOoRs:AddChild(...)
TDatabase()FWMariaRecord() \+ oCn:RowSet()
CLOSE DATAoRs:Close(); oCn:Close()
PACK / ZAPoRs:Zap() / oRs:Truncate

21.2 Migracion gradual con replicacion

// El DBF sigue siendo la fuente principal

oDbf := TDatabase():Open( nil, "clientes", "DBFCDX", .t. )

// Cada cambio en el DBF se replica al servidor MariaDB

oDbf:SetReplicationServer( oCn )

// Cuando estes listo, cambia directamente al RowSet:

// oRs := oCn:RowSet( "clientes" )

21.3 Errores comunes al migrar

ErrorCausaSolucion
Caracteres extranosCharset incorrectoFW\_SetUnicode(.t.) antes de conectar
Datos truncadosCampo muy cortoVerificar longitudes al crear tabla
No se puede editarSin clave primariaFWH agrega id AUTO\_INCREMENT con CreateTable
Lentitud al abrirTabla muy grandeUsar RecSet con paginacion o limitar con WHERE
Registro bloqueadoOtro usuario editandoImplementar locking con FOR UPDATE y timeout
Error en Save()nError \!= 0Activar oCn:lShowErrors := .t. para ver detalles
DBF con DELETEDRegistros marcadosEn MariaDB se borran de verdad; no hay SET DELETED

22\. Referencia Rapida y Constantes

Conexion

ComandoDescripcion
maria\_Connect(cStr, lErr)Conectar al servidor
FWCONNECT oCn HOST ... USER ... PASSWORD ... DATABASE ...Conectar via comando
FW\_DemoDB(\[tipo\])Conectar al servidor de demo
maria\_ConnectError(){ nErr, cErr } del ultimo intento
oCn:Close() / oCn:End()Cerrar conexion

Consultas y Tablas

ComandoDescripcion
oCn:RowSet(cSql \[,aParams\])Recordset editable en memoria
oCn:RecSet(cTabla, nRecs)Recordset paginado (tablas grandes)
oCn:QueryResult(cSql)Valor unico (COUNT, SUM, etc.)
oCn:Execute(cSql)Ejecutar SQL sin resultado
oCn:TableExists(cTabla)Verificar si existe
oCn:CreateTable(cTabla, aStruct \[,lPK\] \[,cCharset\])Crear tabla
oCn:DropTable(cTabla)Eliminar tabla
oCn:Insert(cTabla, cCampos, aData)Insertar registros
oCn:ImportFromDbf(cArchivo,...)Importar desde DBF

RowSet

MetodoDescripcion
oRs:campoLeer/escribir campo directamente
oRs:Fields('campo'):lReadOnlyPoner campo de solo lectura
oRs:lAutoAppend := .t.Habilitar insercion desde browse
oRs:SetDefault('campo', val)Valor por defecto en nuevos registros
oRs:SetFilter(cExpr, aParams)Filtrar registros
oRs:ReFilter(aParams)Re-aplicar filtro con nuevos parametros
oRs:ReQuery(\[aParams\])Recargar datos del servidor
oRs:ReSync()Re-leer registro actual
oRs:Delete()Eliminar registro actual
oRs:EditBaseRecord(...)Editar registro completo con dialogo
oRs:AddChild(cSQL)Agregar RowSet hijo
oRs:SyncChild()Sincronizar hijo con posicion actual
oRs:SetOrder('campo')Ordenar
oRs:Seek(val)Buscar
oRs:Save() / Cancel()Guardar / cancelar cambios
oRs:ToExcel() / ToJson()Exportar
oRs:Close()Cerrar RowSet

Constantes \- Flags de Campo

\#define NOT\_NULL\_FLAG 1

\#define PRI\_KEY\_FLAG 2

\#define UNIQUE\_KEY\_FLAG 4

\#define BLOB\_FLAG 16

\#define UNSIGNED\_FLAG 32

\#define AUTO\_INCREMENT\_FLAG 512

\#define NO\_DEFAULT\_VALUE\_FLAG 4096

Constantes \- Indices de Estructura

\#define STRUCT\_FIELDNAME 1 // Nombre del campo

\#define STRUCT\_FIELDTYPE 2 // Tipo FWH (C, N, D, L, M, \+, \=, @)

\#define STRUCT\_FIELDLEN 3 // Longitud

\#define STRUCT\_FIELDDEC 4 // Decimales

\#define STRUCT\_MYSQLTYPE 5 // Tipo MySQL nativo

\#define STRUCT\_FLAGS 6 // Bit flags

\#define STRUCT\_ALIAS 7 // Alias

\#define STRUCT\_TABLE 9 // Tabla

\#define STRUCT\_DB 10 // Base de datos

\#define STRUCT\_CHARSETNR 11 // Numero de charset

\#define STRUCT\_RO 12 // Solo lectura

23\. Clase TField: Metadatos de Campos

Cada campo de un RowSet se representa como un objeto TField con metadatos completos del servidor.

Propiedades

PropiedadTipoDescripcion
nameCNombre del campo (alias en SQL)
org\_nameCNombre original en la tabla
typeCTipo FWH: C, N, D, L, T, M, \+, \=, @
len / decNLongitud / decimales
sqltypeNTipo MySQL nativo
flagsNFlags del campo (bit flags)
table / org\_tableCAlias de tabla / nombre real
lReadOnlyLSolo lectura
lAutoIncLAuto-incremento
lPrimaryLClave primaria
lUniqueLUnico
lNoNullLNo permite NULL
Value\*Valor actual (lectura/escritura)
OriginalValue\*Valor original sin modificar
Default\*Valor por defecto
cCaseCFormato: 'upper', 'proper'

Acceso

oField := oRs:Fields( "salary" ) // por nombre

oField := oRs:Fields( 3 ) // por posicion

? oField:Value // valor actual

oField:Value := 50000 // asignar

? oField:OriginalValue // valor antes de editar

oField:lReadOnly := .t. // marcar solo lectura

Tipos FWH \-\> MySQL

Tipo FWHSignificadoMySQL
CCaracterVARCHAR, CHAR
NNumericoINT, DECIMAL, FLOAT, DOUBLE
DFechaDATE
TFecha+HoraDATETIME
LLogicoTINYINT(1), BIT
MMemo (texto)TEXT
mMemo (binario)BLOB
\+Auto-incrementoINT AUTO\_INCREMENT
\=Timestamp autoTIMESTAMP
@DateTime autoDATETIME (con default)

Documentacion adicional y foro de soporte: https://forums.fivetechsupport.com

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1710
Joined: Tue Oct 28, 2008 06:26 PM
Re: Manual de MariaDB
Posted: Wed Mar 04, 2026 09:13 PM

Excelente trabajo

Muchas gracias a Carlos Atuncar y a Antonio

Saludos,



Adhemar C.
Posts: 1144
Joined: Mon Feb 05, 2007 07:15 PM
Re: Manual de MariaDB
Posted: Thu Mar 05, 2026 05:24 PM

Carlos Atuncar y a Antonio
No hay mas que decir muchas gracias :roll:

Cesar Cortes Cruz

SysCtrl Software

Mexico



' Sin +- FWH es mejor "