Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Evitar que un usuario se conecte más de una vez (https://www.clubdelphi.com/foros/showthread.php?t=91776)

Angel.Matilla 22-04-2017 12:52:42

Evitar que un usuario se conecte más de una vez
 
A partir del código que he encontrado en un blog estoy tratando de implementar esa funcionalidad en mi base de datos FB. Tengo esta tabla en la base de datos:
Código:

CREATE TABLE Conexion (Id_Servidor INTEGER NOT NULL, Nombre VARCHAR(15) NOT NULL, Hora TIMESTAMP,
CONSTRAINT UQ_Conexion UNIQUE (Nombre))

y del blog que decía antes he adaptado dos triggers de la base de datos:
Código:

CREATE TRIGGER CONECTADO
ACTIVE ON
  CONNECT
POSITION 0
AS
BEGIN
  DELETE FROM Conexion WHERE Id_Servidor NOT IN (SELECT MON$ATTACHMENT_ID FROM MON$ATTACHMENTS);

  INSERT INTO Conexion (Id_Servidor, Nombre, Hora) VALUES (CURRENT_CONNECTION, CURRENT_USER, CURRENT_TIMESTAMP);
END

Código:

CREATE TRIGGER DESCONECTADO
ACTIVE ON
  DISCONNECT
POSITION 1
AS
BEGIN
  DELETE FROM Conexion WHERE Nombre = CURRENT_USER;
END

En teoría con el primero de ellos primero se borran todas las conexiones que no están activas (por ejemplo si se ha apagado el servidor a lo bruto) y luego se inserta un registro en la tabla que he puesto arriba. Dado que hay un índice único con el nombre del usaurio, si está conectado debe saltar un error. El segundo disparador debería borrar la conexión cuando se sale de la aplicación correctamente.

Sin embargo algo debo estar haciendo mal y no sé que es. Si lanzo aplicación me pide el usuario y clave de acceso y la aplicación se lanza bien. Salgo de la misma de forma correcta y me voy a EMS SQL Manager y abro la base de datos con sysdba / masterkey.

Y aquí viene el problema: Si trato de ver el contenido de cualquier tabla o simplemente abrir el panel para escribir un query me da un error que intuyo genera el primero de los dos triggers.
Cita:

Can't format message 13:197 -- message file C:\Windows\firebird.msg not found.
Violation of PRIMARY or UNIQUE KEY constraint "UQ_CONEXION" on table "CONEXION".
Problematic key value is ("NOMBRE" = 'SYSDBA').
At trigger 'CONECTADO' line: 5, col: 3.

SQL Code: -803
IB Error Number: 335544665
Si ya estoy conectado, porque estoy viendo el árbol de la base de datos, y trato de abrir una tabla ¿por qué me da ese error? ¿No se supone que ese trigger sólo se ejecuta al conectarse a la base de datos?

jhonny 23-04-2017 09:16:31

Asumiendo que ya verificó que CURRENT_CONNECTION haya enviado a la tabla Conexion un ID correcto, ensayar lo siguiente a ver que sucede... crear un nuevo trigger de base de datos, así:

Código SQL [-]
CREATE OR ALTER trigger "DESCONECTADO"
active on disconnect position 0
AS
begin
  DELETE FROM Conexion WHERE Id_Servidor = current_connection;
end

Pues en teoria veo correcto lo que estás haciendo, sin embargo te propongo esto, por ensayar, a lo mejor de una nueva idea al respecto.

Angel.Matilla 24-04-2017 10:35:54

Cita:

Empezado por jhonny (Mensaje 515813)
Asumiendo que ya verificó que CURRENT_CONNECTION haya enviado a la tabla Conexion un ID correcto, ensayar lo siguiente a ver que sucede... crear un nuevo trigger de base de datos, así

Gracias por la respuesta. Acabo de probar tu sugerencia y me sigue dando el mismo problema.

Al margen de que tu idea con trigger de desconexión me parece correcta lo que me sorprende es, como comentaba en primer mensaje, que el error que señalaba se da cuando intento ver el contenido de una tabla. Es decir: ese mensaje de error no tiene lugar cuando me conecto a la base de datos y sí cuando con SQL Manager quiero ver el contenido de una tabla (evidentemente con el programa también) o escribir un query; es sólo entonces, cuando seleccióno la pestaña Data en SQL Manager o abro el panel de los querys, que aparece ese mensaje de error. Es como si tratara de volver a conectarse a la base dedatos.

Por otra parte: ¿cómo verifico que se haya enviado un ID correcto si no puedo ver el contenido de las tablas?

Angel.Matilla 24-04-2017 11:33:04

Me estoy dando cuenta de una cosa pero no sé por dónde coger el problema. Estoy haciendo pruebas con EMS SQL Manager; he eliminado los triggers que he descrito antes para poder ver el contenido de las tablas y evidentemente ahora no da error. Sin embargo me ha llamdo la atención una cosa.
Me conecto a la BB.DD. y ejecuto este query que, más o menos, va dentro del primer trigger:
Código SQL [-]
SELECT * FROM MON$ATTACHMENTS

y curiosamente me devuelve dos filas:


Y si abro cualquier tabla de la BB.DD. y ejecuto el mismo query se añade un tercera fila:


por lo tanto, y dado que esa tabla MON$ATTACHMENTS añade una fila cada vez que se abre una tabla o query, el campo MON$ATTACHMENT_ID no puede ser el que se use para controlar cuantas veces se conecta un usuario. Y el problema que veo es que no hay ninguna columna de esa tabla que pueda usarse para controlar ese acceso único para cada usuario porque la segunda columna MON$SERVER_PID tiene siempre ese mismo valor, que imagino variará cuando se vuelva a encender el PC; acaso una combinación de MON$ATTACHMENT_ID y MON$STATE pudiera ser válida.

Casimiro Notevi 24-04-2017 11:46:28

Que yo recuerde, cada registro es una conexión.
Puedes mirar, si quieres, el campo MON$USER también, así sabes cuántas conexiones de cada uno hay.

Angel.Matilla 24-04-2017 12:33:52

Efectivamente cada registro es una conexión, pero por lo que he deducido no es una conexión a la base de datos si no a cada uno de sus elemntos. Así si abro una tabla tengo lo que se ve en la primera imagen, con dos líneas. Dado que el trigger graba en la tabla de conexiones una entrada con el valor de MON$ATTACHMENT_ID y otra con el de MON$USER y la clave única está sobre este último campo en cuanto trato de acceder a una tabla se produce una violación de índices ya que intenta insertar una nueva fila en la tabla.

He probado a modificar dicha tabla Conexion haciendo que el índice sea sobre los dos primeros campos (Id_servidor y nombre) pero entonces no me impide dos conexiones para el mismo usuario. Tal vez lo que haya que hacer es eliminar ese índice y comprobar en el trigger que el usuario no está ya conectado. Estoy dándole vueltas y hecaindo pruebas. Ya os diré como me ha ido, pero se acepta cualquier idea.

Casimiro Notevi 24-04-2017 13:15:45

Cita:

Empezado por Angel.Matilla (Mensaje 515846)
Efectivamente cada registro es una conexión, pero por lo que he deducido no es una conexión a la base de datos si no a cada uno de sus elemntos. Así si abro una tabla tengo lo que se ve en la primera imagen, con dos líneas.

No, cada registro es una conexión.
Si tienes más de un registro es porque tienes más de una conexión abierta, seguramente el propio delphi/bcb con la base de datos conectada.

Angel.Matilla 24-04-2017 13:39:10

Cita:

Empezado por Casimiro Notevi (Mensaje 515849)
No, cada registro es una conexión.
Si tienes más de un registro es porque tienes más de una conexión abierta, seguramente el propio delphi/bcb con la base de datos conectada.

Eso pensaba yo. Si tengo lanzado SQL Manager y la aplicación con un usuario registrado al ejecutar ese query aparece este resultado:


pero si sólo se está ejecutando SQL Manager y todo lo demás (BCB y la aplicación) cerrado sale esto:


Por eso deduzco que por cada elemento que se abre se inserta una línea en la tabla. También es cierto que con la aplicación, al menos, para cada usuario debería haber una única línea y por eso me llama la atención que con el usuario SYSDBA desde SQL Manager haya dos: una que supongo que es la conexión a la BB.DD. (MON$STATE = 0) y la otra la del query (MON$STATE = 1). Además, como puedes ver, la primera entrada del susuario SYSDBA no cambia el valor MON$ATTACHMENT_ID; por eso deduzco que se trata de la conexión a la BB.DD.

Buscando por internet he ido a dar con un blog de un programador que en un artículo explica las tablas de monitorización, pero no me ha aclarado gran cosa.

Casimiro Notevi 24-04-2017 13:43:13

A ver si el sqlmanager (que no lo he usado nunca) abre dos conexiones por algún motivo.
Prueba al revés, cierra el sqlmanager y ejecuta tu programa, debe salir solamente una línea.

Angel.Matilla 24-04-2017 13:49:20

¿Y como hago la prueba? Porque eso si que no se me ocurre.

Casimiro Notevi 24-04-2017 13:58:15

Un botón en el formulario principal de tu programa, ejecutas en select y muestras el resultado en un dbgrid, por ejemplo, o un shomessage( select count ... ))

Angel.Matilla 24-04-2017 14:00:00

Pues tienes razón, creo. He modificado el programa de forma que después de la primera conexión se borre el trigger que graba en la tabla y efectivamente sólo hay una entrada en la misma. Ahora ya si que no entiendo nada. Seguiré haciendo pruebas.

Angel.Matilla 24-04-2017 14:22:10

Cita:

Empezado por Casimiro Notevi (Mensaje 515852)
A ver si el sqlmanager (que no lo he usado nunca) abre dos conexiones por algún motivo.
Prueba al revés, cierra el sqlmanager y ejecuta tu programa, debe salir solamente una línea.

Puede ser que tengas razón, todo apunta en esa dirección y parece que efectivamente SQL Manager abre una conexión por cada consulta. Tendré que tocar el trigger para que cuando entre con SYSDBA, por ejemplo, no grabe ninguna entrada en la tabla de conexiones.

Angel.Matilla 24-04-2017 14:46:58

Efectivamente el problema es el que señala Casimiro: SQL Manager abre una coexión para cada elemento que se trata de consultar y de ahí viene el error. He modificado el trigger de conexión dejándolo de esta manera:
Código:

CREATE TRIGGER CONECTADO
ACTIVE ON
  CONNECT
POSITION 0
AS
BEGIN
  DELETE FROM Conexion
  WHERE Id_Servidor NOT IN (SELECT MON$ATTACHMENT_ID FROM MON$ATTACHMENTS);

  IF (CURRENT_USER IN (SELECT Nombre FROM Conexion)) THEN
    EXCEPTION Usuario_conectado;

  IF (CURRENT_USER <> 'SYSDBA') THEN
    INSERT INTO Conexion (Id_Servidor, Nombre, Hora)
    VALUES (CURRENT_CONNECTION, CURRENT_USER, CURRENT_TIMESTAMP);
END

De esta forma, además de verificar si un usuario ya está conectado y sacar el correspondiente mensaje de error, siempre queda liberado el usuario SYSDBA por si hay que intervenir de forma inesperada en la base de datos con alguna herramienta externa. Muchas gracias todos por vuestra ayuda, ha sido inestimable.


La franja horaria es GMT +2. Ahora son las 08:12:55.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi