sql >> Base de Datos >  >> RDS >> Sqlserver

Activadores de SQL Server:parte 2 Activadores DDL y de inicio de sesión

En SQL Server, los disparadores son objetos de la base de datos que se ejecutarán cada vez que ocurra un evento disparador en la base de datos o el servidor. Los disparadores juegan un papel clave en el cumplimiento de los requisitos comerciales, como alertar a las personas objetivo en función de una condición lograda, iniciar un trabajo u otras operaciones. En el artículo anterior sobre disparadores DML, hablamos sobre disparadores, tipos de disparadores y varias opciones de disparadores disponibles para disparadores DML. En este artículo, exploraremos los disparadores SQL DDL y LOGON.

Disparadores DDL

Los disparadores DDL se pueden activar para una variedad de eventos de ámbito de servidor o base de datos, incluidos los comandos DDL y DCL. DDL significa lenguaje de definición de datos que se utiliza para CREAR, ALTERAR, SOLTAR cualquier objeto y DCL significa declaraciones de lenguaje de control de datos como los comandos GRANT, DENY y REVOKE. A continuación se muestran las características de los activadores SQL DDL.

  1. Los disparadores DDL se pueden crear en el nivel de la base de datos o en el nivel de la instancia del servidor, cubriendo una amplia variedad de operaciones DDL u operaciones similares a DDL, p. Comandos DCL.
  2. Los disparadores DDL solo se pueden invocar o disparar como un tipo de disparador FOR o AFTER. SQL Server no es compatible con INSTEAD OF DDL Trigger y podemos ver cómo evitar algunas operaciones DDL a través de DDL Triggers.
  3. SQL Server tiene funciones integradas como EVENTDATA() y IS_MEMBER() para usar dentro de los disparadores DDL para obtener más información relacionada con los eventos disparadores.
    1. La función EVENTDATA() devuelve detalles completos sobre los eventos del ámbito de la base de datos o del servidor en formato XML dentro del ámbito del activador DDL del ámbito de la base de datos o del servidor o también de los activadores de inicio de sesión. La función EVENTDATA() devuelve los detalles completos del evento para la sesión que realiza las actividades de inicio de sesión o DDL. EVENTDATA() devuelve los siguientes detalles
      • EventType:tipo de evento que activa el activador DDL disponible en la tabla sys.trigger_event_types.
      • PostTime:hora en que se activó o publicó el evento.
      • SPID:ID de sesión del evento.
      • ServerName:nombre de la instancia de SQL Server en la que se activó el evento.
      • LoginName:nombre de inicio de sesión de SQL Server que realizó el evento.
      • Nombre de usuario:nombre de usuario del inicio de sesión que será dbo de forma predeterminada.
      • DatabaseName:nombre de la base de datos con el que se activó el disparador DDL.
      • SchemaName:nombre de esquema del objeto que se vio afectado.
      • ObjectName:nombre del objeto que se vio afectado.
      • ObjectType:tipo de objeto de SQL Server como tabla, vista, procedimiento almacenado.
      • TSQLCommand:secuencia de comandos T-SQL que fue ejecutada por un usuario que invocó el disparador DDL.
      • SetOptions:opciones SET utilizadas por el usuario o el cliente como SSMS mientras se ejecutaba TSQLCommand.
      • CommandText:instrucciones DDL o DCL reales con el evento DDL especificado en la tabla sys.trigger_event_types.
    2. La función IS_MEMBER() devuelve si el usuario actual es miembro del grupo de Windows o del rol de la base de datos de SQL Server o no.
  4. System DMV sys.triggers almacena la lista de todos los activadores del ámbito de la base de datos. Podemos usar la siguiente consulta para obtener los detalles de todos los activadores DDL con ámbito de base de datos.
SELECT * 
FROM sys.triggers
WHERE type = 'TR';
  1. System DMV sys.server_triggers almacena la lista de todos los activadores del ámbito del servidor y podemos usar la siguiente consulta para obtener los detalles sobre todos los activadores DDL del ámbito del servidor.
SELECT * 
FROM sys.server_triggers;
  1. Las definiciones de disparador DDL se pueden ver si el disparador no está encriptado usando cualquiera de las siguientes opciones de sys.sql_modules o usando la función OBJECT_DEFINITION() o usando el procedimiento almacenado sp_helptext.
SELECT OBJECT_SCHEMA_NAME(object_id, db_id()) Schema_name, OBJECT_NAME(object_id) Trigger_Name, definition
FROM sys.sql_modules  
WHERE object_id = OBJECT_ID(<trigger_name>);   

SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 

EXEC sp_helptext '<trigger_name>';
  1. Todos los eventos DDL posibles están disponibles en la tabla sys.trigger_event_types y se pueden ver mediante la siguiente consulta.
SELECT *
FROM sys.trigger_event_types;

La sintaxis de un disparador DDL es:

CREATE TRIGGER <trigger_name>
ON < ALL SERVER | DATABASE > 
[ WITH <DDL_trigger_option> [ ,...n ] ]  
{ FOR | AFTER } <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }  

Creación de desencadenador DDL con ámbito de base de datos

Vamos a crear un activador de ámbito de base de datos para realizar un seguimiento de todas las creaciones de tablas e iniciar sesión en una tabla de registro denominada Track_DDL_Changes mediante el siguiente script.

CREATE TABLE Track_DDL_Changes (EventData xml, PostDtm datetime)
GO
CREATE TRIGGER TR_D_CREATETABLE
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
	INSERT INTO Track_DDL_Changes
	SELECT EVENTDATA(),GETDATE()
END
GO

Vamos a crear una nueva tabla llamada trigger_test y verificar si el evento CREATE TABLE fue auditado o no usando el siguiente script.

CREATE TABLE Trigger_Test ( a int, b datetime);

Al seleccionar los datos de la tabla Track_DDL_Changes, se muestra que el evento CREATE_TABLE anterior se capturó correctamente como se muestra a continuación:

Al hacer clic en el valor EventData, se abrirá el valor XML EVENTDATA() en una nueva ventana, como se muestra a continuación.

Podemos verificar los detalles completos sobre el evento desencadenante a través de la función EVENTDATA() y, por lo tanto, la función EVENTDATA() desempeñaría un papel importante para cualquier desencadenante DDL o LOGON.

Podemos mejorar aún más nuestro Disparador DDL con la ayuda de la función EVENTDATA() y el análisis XML y evitar que alguien cree una tabla en la base de datos de prueba usando el script que se proporciona a continuación:

CREATE TRIGGER TR_D_PREVENT_CREATETABLE
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
   SELECT EVENTDATA().value  
        ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
   RAISERROR ('Creation of New tables restricted in this database, Kindly contact DBA.', 16, 1)   
   ROLLBACK  
END
GO

La creación del activador del alcance de la base de datos se completó con éxito y verifiquemos creando otra tabla usando el siguiente script.

CREATE TABLE Trigger_Test1 (a int, b datetime);

El activador nos ha impedido crear nuevas tablas en esta base de datos y también ha dejado un mensaje significativo a los usuarios. De manera similar, podemos manejar cualquier otro DDL o eventos de ámbito de servidor para cumplir con los requisitos.

Para eliminar el disparador DDL con ámbito de base de datos, necesitamos usar la siguiente sintaxis:

DROP TRIGGER <trigger_name> ON DATABASE;

Y para soltar el activador que acabamos de crear, el script sería

DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Para ver los activadores DDL en el ámbito de la base de datos en SSMS, expanda la Base de datos de prueba -> Programabilidad -> Activadores de base de datos como se muestra a continuación.

Al igual que los disparadores DML de SQL, los disparadores DDL se pueden eliminar, deshabilitar o habilitar simplemente haciendo clic con el botón derecho en el nombre del disparador, como se muestra a continuación.

A través de T-SQL, podemos descartar, deshabilitar o habilitar el disparador DDL con ámbito de base de datos usando la siguiente sintaxis:

-- DROP Database scoped DDL Trigger
DROP TRIGGER <trigger_name> ON DATABASE;
-- Enable Database scoped DDL Trigger
ENABLE TRIGGER <trigger_name> ON DATABASE;
-- Disable Database scoped DDL Trigger
DISABLE TRIGGER <trigger_name> ON DATABASE;

Para deshabilitar el activador que hemos creado, es posible que debamos usar el siguiente script.

-- DROP Database scoped DDL Trigger
DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
-- Enable Database scoped DDL Trigger
ENABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
-- Disable Database scoped DDL Trigger
DISABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Creación de activador DDL con ámbito de servidor

El activador DDL con ámbito de servidor sigue la misma sintaxis similar al activador DDL con ámbito de base de datos, excepto que los eventos se basarán en el ámbito del servidor.

Intentemos crear un disparador DDL con alcance de servidor para evitar que cualquier usuario cree una nueva base de datos en esta instancia de servidor usando el siguiente script.

CREATE TRIGGER TR_S_PREVENT_CREATEDATABASE
ON ALL SERVER
FOR CREATE_DATABASE
AS
BEGIN
   SELECT EVENTDATA().value  
        ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
   RAISERROR ('Creation of New Databases restricted in this Instance, Kindly contact DBA.', 16, 1)   
   ROLLBACK  
END
GO

Al intentar crear una nueva base de datos usando el siguiente comando, recibiremos un error como se muestra a continuación.

CREATE DATABASE DATABASE_TEST;

En SSMS, los activadores DDL del ámbito del servidor se encuentran en Activadores en la sección Objetos del servidor, como se muestra a continuación.

Podemos descartar, deshabilitar o habilitar el disparador DDL del ámbito del servidor simplemente haciendo clic con el botón derecho en el disparador DDL del ámbito del servidor como se muestra a continuación.

A través de T-SQL, podemos descartar, deshabilitar o habilitar usando el siguiente comando.

-- DROP Server scoped DDL Trigger
DROP TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
-- Disable Server scoped DDL Trigger
DISABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
-- Enable Server scoped DDL Trigger
ENABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;

El propósito de los disparadores DDL

  1. Para auditar cualquier evento DDL que ocurra en la base de datos o en el nivel del servidor.
  2. Para evitar que ocurran eventos DDL en la base de datos o en el nivel del servidor.
  3. Para alertar cada vez que ocurra algún evento DDL en la base de datos o en el nivel del servidor.

Activadores de inicio de sesión

Los disparadores de inicio de sesión, como su nombre lo indica, se ejecutan para eventos de INICIO DE SESIÓN en SQL Server. Una vez que se completa la fase de autenticación para un evento de inicio de sesión, el script de inicio de sesión se ejecutará además de la actividad de inicio de sesión. Si el inicio de sesión no se autentica correctamente, no se activarán los disparadores de INICIO DE SESIÓN. Los activadores de inicio de sesión se enumerarán en SSMS en la sección Activadores de Objetos del servidor. La sintaxis de un activador de inicio de sesión es la siguiente:

CREATE TRIGGER <schema_name.trigger_name>
ON ALL SERVER
{ FOR| AFTER } LOGON    
AS { sql_statement  [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier >  [ ; ] }  

Crear disparadores

Vamos a crear un disparador de INICIO DE SESIÓN simple para capturar más información sobre el evento de INICIO DE SESIÓN de la función EVENTDATA() como se muestra a continuación.

CREATE TABLE Track_LOGON_EVENTS (EventData xml, PostDtm datetime)
GO
CREATE TRIGGER TR_LOGON
ON ALL SERVER
FOR LOGON
AS
BEGIN
	INSERT INTO Track_LOGON_EVENTS
	SELECT EVENTDATA(),GETDATE();
END
GO

El disparador de INICIO DE SESIÓN anterior capturará todos los detalles sobre una actividad de inicio de sesión similar a la que hemos notado al usar la función EVENTDATA() en DDL Trigger. Debemos tener cuidado al planear el uso de disparadores de INICIO DE SESIÓN, ya que si hay algún error de lógica dentro del disparador, no permitiría que nadie o la mayoría de los usuarios se conecten a la instancia de SQL Server.

Para DROP, Disabled o Enable LOGON triggers, podemos usar el siguiente script.

-- DROP LOGON Trigger
DROP TRIGGER TR_LOGON ON ALL SERVER;
-- Disable LOGON Trigger
DISABLE TRIGGER TR_LOGON ON ALL SERVER;
-- Enable LOGON Trigger
ENABLE TRIGGER TR_LOGON ON ALL SERVER;

El propósito de los activadores LOGON

  1. Para auditar cualquier evento de INICIO DE SESIÓN que ocurra en el servidor.
  2. Para evitar que ocurran eventos de INICIO DE SESIÓN en el servidor
  3. Para alertar cada vez que ocurra algún evento de INICIO DE SESIÓN en el servidor.

Propiedades del disparador

sp_settriggerorder

sp_settriggerorder se usa para definir el orden de ejecución del disparador solo para el primero y el último disparador. Si hay más de 2 activadores DML en una tabla, digamos 5 activadores DML, podemos definir el primer activador DML y el último activador DML, pero no podemos definir el orden de los 3 activadores intermedios.

Nota: La configuración de la opción PRIMERO o ÚLTIMO es específica de una categoría de evento en particular para activadores DML. Por ejemplo, en una tabla que tiene 3 disparadores INSERT, podemos definir qué disparador INSERT es el PRIMERO y cuál es el ÚLTIMO. Si tiene 3 disparadores en una tabla como INSERTAR, ACTUALIZAR y ELIMINAR, entonces no es necesario establecer la condición de Orden de disparador.

La sintaxis para establecer el orden de activación sería así:

exec sp_settriggerorder @triggername = '<trigger_schema_name.trigger_name>' 
    , @order = 'FIRST' | 'LAST'   
    , @stmttype = '<trigger event type>'   
    , @namespace = 'DATABASE' | 'SERVER' | 'NULL'

Para los disparadores DDL, podemos definir el primero y el último disparador del ámbito del servidor y luego definir el primero y el último disparador del ámbito de la base de datos. Por ejemplo, si tenemos 5 disparadores con alcance de servidor y 5 disparadores con alcance de base de datos, entonces el orden puede definirse así:

  1. Primer disparador para el disparador DDL con ámbito de servidor
  2. 3 activadores de DDL con ámbito de otro servidor en orden aleatorio
  3. Último activador para el activador DDL con ámbito de servidor.
  4. Primer disparador para el disparador DDL con ámbito de base de datos (uno por base de datos)
  5. 3 activadores de DDL en el ámbito de otra base de datos en orden aleatorio
  6. Último disparador para el disparador DDL con ámbito de base de datos.

Con respecto a la configuración de la primera o la última opción, los activadores DDL en el ámbito de la base de datos se pueden ordenar dentro de la base de datos y los activadores DDL en el ámbito del servidor en el nivel de instancia.

Aunque SQL Server nos permite crear muchos disparadores en una tabla, se recomienda analizar cuidadosamente los requisitos del disparador para un mejor mantenimiento y solución de problemas.

Disparadores recursivos

SQL Server también admite la invocación recursiva de disparadores para disparadores DML. Los disparadores recursivos se pueden clasificar como directos o indirectos, como se muestra a continuación.

Disparadores recursivos directos – El usuario o la aplicación actualiza un registro en la Tabla A. El disparador UPDATE A en la Tabla A se activa y actualiza la Tabla A nuevamente. Dado que el registro en la Tabla A se actualizó a través de Trigger, volverá a invocar UPDATE Trigger A y esto sucederá de forma recursiva.

Vamos a crear disparadores recursivos directos en la tabla de ventas usando el siguiente script:

CREATE TRIGGER TR_UPD_Recursive_Sales ON Sales
FOR UPDATE 
AS
BEGIN
  UPDATE Sales 
  SET SalesDate = GETDATE() 
  WHERE SalesId = (SELECT SalesId FROM Inserted)
END
GO

Ejecute el siguiente script:

UPDATE Sales 
SET SalesDate = GETDATE() 
WHERE SalesId = 3;

Disparadores recursivos indirectos – El usuario o la aplicación actualiza un registro en la tabla A. El disparador UPDATE A en la tabla A se activa y actualiza un registro en la tabla B. Si la tabla B tiene un disparador UPDATE para actualizar los registros a la tabla A, invocará el disparador UPDATE en Tabla A que ocurrirá recursivamente.

Vamos a crear un disparador recursivo indirecto en las tablas IDR_Test1 e IDR_Test2 usando el siguiente script:

DROP TABLE IDR_Test1
DROP TABLE IDR_Test2

CREATE TABLE IDR_Test1 (PK int NOT NULL);
GO
INSERT INTO IDR_Test1 
values (10),(20)
GO
CREATE TABLE IDR_Test2 (PK int NOT NULL);
GO
INSERT INTO IDR_Test2
values (10),(20)
GO

CREATE TRIGGER TR_IDR_Test1
ON IDR_Test1
FOR UPDATE 
AS
BEGIN
	UPDATE IDR_Test2
	SET PK = 30
	WHERE PK IN (SELECT PK FROM inserted);
END
GO
 
CREATE TRIGGER TR_Temp2
ON IDR_Test2
FOR UPDATE 
AS
BEGIN
	UPDATE IDR_Test1
	SET PK = 30
	WHERE PK IN (SELECT PK FROM inserted);
END
GO

Ejecute el siguiente script:

UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;

Para evitar este tipo de invocación de disparadores recursivos en el nivel de la base de datos, SQL Server tiene una opción llamada RECURSIVE_TRIGGERS en cada nivel de la base de datos para interrumpir la activación del disparador recursivo. De forma predeterminada, la opción Activador recursivo se establece en Falso para una base de datos. Habilite solo según sea necesario después de una cuidadosa consideración de los impactos en el rendimiento o los cambios de datos involucrados.

En SSMS, haga clic con el botón derecho en nuestra base de datos de prueba -> Elija Propiedades -> Haga clic en Opciones y desplácese hacia abajo para ver si la opción Disparadores recursivos está habilitada o no, como se muestra a continuación. Para Test Database, se establece en False ya que False es el valor predeterminado para la opción Recursive Triggers. Para activar la opción Activadores recursivos para una base de datos específica, simplemente haga clic en el valor desplegable, cámbielo a Verdadero y haga clic en Aceptar.

A través de T-SQL, podemos verificar la opción Recursive Trigger de la base de datos Test al verificar la columna is_recursive_triggers_on de sys.databases DMV como se muestra a continuación.

select name, is_recursive_triggers_on
from sys.databases
where name = 'test'

Para cambiar la opción de disparadores recursivos para una base de datos (Prueba en mi ejemplo), podemos ejecutar el siguiente script.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO

Para deshabilitarlo de nuevo al estado falso (estado predeterminado) para una base de datos (Prueba en mi ejemplo), ejecute el siguiente script.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO

Disparadores anidados

Los disparadores recursivos son un ejemplo clásico de disparadores anidados, pero puede haber algunos otros casos que resulten en el anidamiento de múltiples disparadores. SQL Server permite el anidamiento de disparadores hasta un máximo de 32 niveles y SQL Server cancelará cualquier disparador que supere ese nivel de anidamiento. SQL Server tiene una configuración de toda la instancia para deshabilitar la opción de activadores anidados. Tenga en cuenta que el anidamiento de activadores de SQL Server que usan código CLR o código administrado no se encuentra por debajo del límite de 32 niveles, ya que está fuera del alcance de SQL Server. De manera predeterminada, la opción de activadores anidados estará habilitada en todas las instancias de SQL Server y podemos deshabilitarla según sea necesario.

Podemos verificar si la opción de activadores anidados está habilitada a nivel de instancia en SSMS siguiendo los pasos a continuación:

Haga clic derecho en Servidor -> Elija Propiedades -> Haga clic en Avanzado

Para deshabilitar o desactivar la opción de activadores anidados, haga clic en el menú desplegable, cámbielo a Falso y haga clic en Aceptar. .

A través de T-SQL, podemos verificar si la opción Activadores anidados está habilitada comprobando la columna value_in_use en sys.configurations DMV para el nombre de configuración de activadores anidados.

Para deshabilitar esta opción, necesitamos usar sp_configure el procedimiento almacenado del sistema como se muestra a continuación:

EXEC sp_configure 'nested triggers', 0;  
GO  
RECONFIGURE;  
GO  

Dentro de cualquier disparador DML o DDL, para encontrar el nivel actual de anidamiento, SQL Server tiene una función integrada llamada TRIGGER_NESTLEVEL para devolver la cantidad de disparadores ejecutados para la declaración actual que activó el disparador, incluido él mismo. La sintaxis de la función TRIGGER_NESTLEVEL sería:

SELECT TRIGGER_NESTLEVEL ( object_id, <trigger_type> , <trigger_event_category> )

Donde object_id es el id del objeto del disparador, trigger_type será DESPUÉS para DESPUÉS del disparador e IOT para INSTEAD OF disparador y trigger_event_category será DML o DDL.

Por ejemplo, si necesitamos permitir solo el nivel de anidamiento hasta el 10 y generar un error después de 10 niveles, entonces podemos hacerlo en el activador de prueba como aquí:

IF ((SELECT TRIGGER_NESTLEVEL(OBJECT_ID('test_trigger'), 'AFTER’, 'DML’)) > 10)  
   RAISERROR ('Trigger test_trigger nested more than 10 levels.',16, -1)   

CIFRADO

Para cifrar la definición o la lógica del disparador, la opción CON ENCRIPTACIÓN se puede usar en la definición del disparador de forma similar a todos los demás objetos de SQL Server.

Cláusula EXECUTE AS

Para ejecutar el activador utilizando un contexto de seguridad específico, se puede utilizar la cláusula EXECUTE AS en la definición del activador.

NO PARA REPLICAR

Para identificar que el activador DML no debe invocarse mientras se ejecuta a través de cambios de replicación, la propiedad NOT FOR REPLICATION se establecerá para todos los objetos en la base de datos del suscriptor.

Conclusión

Gracias por leer el poderoso artículo sobre Disparadores DDL y Disparadores de inicio de sesión donde hemos entendido el propósito de los disparadores DDL y de inicio de sesión, cómo crear o eliminar, deshabilitar o habilitar estos disparadores junto con cómo usar la función EVENTDATA() para seguimiento de actividades de inicio de sesión o DDL. Además de eso, hemos aprendido cómo establecer el orden de ejecución de múltiples disparadores SQL DML o DDL junto con disparadores recursivos y anidados en detalle y cómo manejar los disparadores recursivos o anidados con cuidado.