BLOQUEOS "VIRTUALES" CON MySQL
De todos es bien sabido la imposibilidad de bloquear registros individuales en MySQL u otras bases de datos de arquitectura Cliente-Servidor. No existe la función RLOCK() como la conocemos para tablas DBF, que permite bloquear un registro o varios en el area de trabajo que le designamos, o que estamos posicionados en ese momento.
Pero en MySQL, con un poco de imaginación, Si es posible realizar un bloqueo "virtual", simulando un bloqueo de registro. Ahora vamos a ver como.
Para ello se utilizan tres funciones propias de la base de datos MySQL:
GET_LOCK(str,timeout)
Intenta obtener un bloqueo con el nombre dado por la cadena str, con un tiempo límite de timeout segundos. Devuelve 1 si se ha obtenido el bloqueo, 0 si no se ha obtenido en el tiempo indicado (por ejemplo, porque otro cliente ha bloqueado ya el nombre), o NULL si ha habido un error (como falta de memoria o si el hilo fue matado por mysqladmin kill). Un bloqueo se libera cuando se ejecuta la función , se ejecuta un nuevo GET_LOCK() o el hilo termina (ya sea normal o anormalmente). Esta función se puede usar para implementar bloqueos de aplicación o para simular bloqueos de registro. Los nombres se bloquean en bases del servidor. Si un nombre ha sido bloqueado por un cliente, GET_LOCK() bloquea cualquier petición de otro cliente para bloquear el mismo nombre. Esto permite que clientes que se ponen de acuerdo para bloquear un nombre dado usar el mismo nombre para realizar un bloqueo coordinado.
IS_FREE_LOCK(str)
Verifica si el nombre de bloqueo str está libre para usarse (es decir, no está bloqueado). Devuelve 1 si el bloqueo está libre (nadie está usando el bloqueo), 0 si el bloqueo está en uso y NULL si hay errores (como argumentos incorrectos).
RELEASE_LOCK(str)
Libera el bloqueo con el nombre str que se obtuvo mediante. Devuelve 1 si el bloqueo fue liberado, 0 si no fue bloqueado por este hilo (en cuyo caso no fue liberado), y NULL si el nombre de bloqueo no existe. (El bloqueo no existirá si nunca fue obtenido por una llamada a o si ya ha sido liberado.) Es conveniente usar la sentencia con RELEASE_LOCK().
Vamos a ver un ejemplo de su utilización con VFP y a través de SQL Pass Through.
El ejemplo va a consistir en el bloqueo de un código determinado, es indiferente que sea un código de Cliente, de Proveedor, de un Artículo, o un número de Factura. Para el ejemplo vamos a seguir tres pasos:
- Conectarnos a la base de datos MySQL
- Realizar un bloqueo virtual a un código determinado (LOCK)
- Liberar dicho bloqueo (UNLOCK)
cSQL= "DRIVER={MySQL ODBC 5.1 Driver};" + ; "SERVER=localhost" + ; "PORT=3306" + ; "UID=xxxx" + ; "PWD=xxxx" + ; "DATABASE=MyBaseDatos" + ; "OPTION=2049" SQLSETPROP(00,"Transactions", 1) SQLSETPROP(00,"DispLogin", 3) NH = SQLSTRINGCONNECT(""+cSQL, .T.) IF NH < 0 MESSAGEBOX("Error de CONEXIÓN : " + ALLT(STR(NH)),48,"Atencion") ENDIF
Realizar un bloqueo virtual
El ejemplo muestra el intento de bloqueo de un código almacenado en la variable cKey . La función GET_LOCK reintenta automáticamente el bloqueo durante 10 segundos. Si consigue el bloqueo exitosamente devuelve el valor ‘1’, y salimos del bucle DO WHILE. Si al cabo de 10 segundos no es así, el valor devuelto será ‘0’ o Nulo con lo cual mostraremos un mensaje indicando que el código ya se encuentra bloqueado, o que su bloqueo es imposible.
También se puede utilizar la función IS_FREE_LOCK, para comprobar si el código en cuestión se encuentra bloqueado.
cKey="000001" && Código a Bloquear DO WHILE .T. cSQL="SELECT IFNULL(GET_LOCK('"+cKey+"',10),'0') AS cRESULT" SQLEXEC(NH,""+cSQL,"CURSOR") SELECT CURSOR GO TOP IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR"))="1" EXIT && Código Bloqueado ENDIF OPB=MESSAGEBOX("Error de BLOQUEO Reintentar",5+48+0,"Atencion") IF OPB <> 04 EXIT ENDIF ENDDO
Liberar Bloqueo
Liberar los bloqueos es lo más sencillo con la función DO RELEASE_LOCK. Si se consigue liberar el código del bloqueo realizado por el mismo usuario el valor devuelto por la función es "1". No es necesario tener un control muy exahustivo sobre el resultado de la función, ya que el hilo que la ejecuta debe ser el mismo que ha realizado el bloqueo anteriormente.
cSQL="DO RELEASE_LOCK(''+cKey)" SQLEXEC(NH,""+cSQL)
Detectar si un Código se encuentra bloqueado
otra función que también se utiliza es IS_FREE_LOCK, nos servirá para detectar si el código que vamos ha bloquear se encuentra libre de bloqueos. Si es así nos devolverá el valor "1". Se puede utilizar conjuntamente con la función GET_LOCK, dentro de un bucle de comprobación y realización de bloqueos.
cKey="000001" && Código a Bloquear DO WHILE .T. cSQL="SELECT IFNULL(IS_FREE_LOCK('"+cKey+"'),'0') AS cRESULT" SQLEXEC(NH,""+cSQL,"CURSOR") SELECT CURSOR GO TOP IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR"))="1" ** Código libre para Bloqueo (Bloqueamos) cSQL="SELECT IFNULL(GET_LOCK('"+cKey+"',10),'0') AS cRESULT" SQLEXEC(NH,""+cSQL,"CURSOR2") SELECT CURSOR2 GO TOP IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR2"))="1" EXIT && Código Bloqueado Salir ENDIF ENDIF OPB=MESSAGEBOX("Error de BLOQUEO Reintentar",5+48+0,"Atencion") IF OPB <> 04 EXIT ENDIF ENDDO
Gráficamente este sería el resultado real de todo lo que hemos explicado hasta ahora, y su utilización práctica. En la imagen inferior se muestra el mensaje de bloqueo que aparece cuando un usuario intenta acceder en este caso, a modificar una factura utilizada en este preciso instante por otro usuario.
Antonio L. Montagut
(www.ontarioxb.es)
No hay comentarios. :
Publicar un comentario
Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.