Manual de MariaDB

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Manual de MariaDB

Post by Antonio Linares »

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

Code: Select all | Expand

  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 \= " \+ ;

Code: Select all | Expand

         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", ;

Code: Select all | Expand

               "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 (" \+ ;

Code: Select all | Expand

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

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

oCn:Execute( cSql )

9.3 Consultar con desencriptacion

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

Code: Select all | Expand

    "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 (" \+ ;

Code: Select all | Expand

    "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

Code: Select all | Expand

 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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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.

Code: Select all | Expand

┌─────────────────────────┐
│   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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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: Select all | Expand

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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

aErr := maria_ConnectError()  // devuelve { nErrorCode, cErrorMsg }
2.2 Conexión embebida (solo 32 bits)

Code: Select all | Expand

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: Select all | Expand

oCn:lLogErr      := .t.   // activa log de errores
oCn:lShowErrors  := .t.   // muestra errores en pantalla
2.4 Ejemplo completo — maria01.prg

Code: Select all | Expand

#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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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: Select all | Expand

#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: Select all | Expand

// 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: Select all | Expand

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

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

// Navegación por páginas
oRs:GoToPage( nPage )
oRs:FirstPage()
oRs:PrevPage()
oRs:NextPage()
oRs:LastPage()
? oRs:nCurrentPage
? oRs:nMaxPages
4.12 Export

Code: Select all | Expand

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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

5. Operaciones CRUD

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

--- 5.1 INSERT — Insertar registros

Code: Select all | Expand

// 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: Select all | Expand

// Inserta si no existe, actualiza si ya existe (basado en clave primaria)
oCn:Upsert( "clientes", nil, aValues )
5.2 SELECT — Consultar datos

Code: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

// 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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

6. Manejo de Tablas

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

--- 6.1 Crear tabla

Code: Select all | Expand

// 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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

7. Relaciones Padre-Hijo

← Tablas | Índice | Siguiente: Avanzadas →

--- 7.1 Un padre con un hijo — maria04.prg Método 1: AddChild con SQL

Code: Select all | Expand

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: Select all | Expand

oStates := oCn:RowSet( "states" )
oCust   := oCn:RowSet( "customer" )
oStates:AddChild( oCust, "state = states.code" )
Ejemplo completo

Code: Select all | Expand

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: Select all | Expand

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: Select all | Expand

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

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

8. Funciones Avanzadas

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

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

Code: Select all | Expand

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: Select all | Expand

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: Select all | Expand

// 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: Select all | Expand

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: Select all | Expand

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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

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: Select all | Expand

// 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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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

https://github.com/FiveTechSoft/FWH_too ... ariadb.zip

regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual de MariaDB

Post by Antonio Linares »

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

Code: Select all | Expand

  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,

Code: Select all | Expand

      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", ;

Code: Select all | Expand

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

// Ver el SQL generado

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

Code: Select all | Expand

                      "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(), ;

Code: Select all | Expand

                       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

Code: Select all | Expand

  :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 ;

Code: Select all | Expand

  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 ( " \+ ;

Code: Select all | Expand

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

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

oCn:Execute( cSql )

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

Code: Select all | Expand

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

oCn:Execute( cSql )

9.3 Consultar con desencriptacion

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

Code: Select all | Expand

    "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

Code: Select all | Expand

 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()

Code: Select all | Expand

  ? "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", { || ;

Code: Select all | Expand

  oRsEdit := oCn:RowSet( ;

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

     { oRsMain:ID } ) } )

if oCn:nError \== 0

Code: Select all | Expand

  // 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

Code: Select all | Expand

  // 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', ;

Code: Select all | Expand

  '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 )

Code: Select all | Expand

  return .t.

endif

if oRs:Requery( cSql )

Code: Select all | Expand

  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
User avatar
acuellar
Posts: 1713
Joined: Tue Oct 28, 2008 6:26 pm
Location: Santa Cruz-Bolivia

Re: Manual de MariaDB

Post by acuellar »

Excelente trabajo

Muchas gracias a Carlos Atuncar y a Antonio

Saludos,

Adhemar Cuéllar

User avatar
sysctrl2
Posts: 1144
Joined: Mon Feb 05, 2007 7:15 pm
Contact:

Re: Manual de MariaDB

Post by sysctrl2 »

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

Cesar Cortes Cruz
SysCtrl Software
Mexico

' Sin +- FWH es mejor "
Post Reply