No SQL Server, gatilhos são objetos de banco de dados que serão executados sempre que um evento de gatilho acontecer no banco de dados ou no servidor. Os gatilhos desempenham um papel fundamental no cumprimento dos requisitos de negócios, como alertar as pessoas-alvo com base em uma condição alcançada, iniciar um trabalho ou outras operações. No artigo anterior sobre gatilhos DML, falamos sobre gatilhos, tipos de gatilhos e várias opções de gatilho disponíveis para gatilhos DML. Neste artigo, exploraremos os gatilhos SQL DDL e LOGON.
Acionadores de DDL
DDL Triggers podem ser acionados para uma variedade de eventos com escopo de servidor ou banco de dados, incluindo comandos DDL e DCL. DDL significa Linguagem de Definição de Dados que é usada para CREATE, ALTER, DROP quaisquer objetos e DCL significa instruções de Linguagem de Controle de Dados como comandos GRANT, DENY e REVOKE. Abaixo estão as características dos gatilhos SQL DDL.
- DDL Triggers podem ser criados no nível de banco de dados ou no nível de instância do servidor, cobrindo uma ampla variedade de operações DDL ou operações semelhantes a DDL, por exemplo, Comandos DCL.
- Os acionadores DDL só podem ser invocados ou disparados como um tipo de acionador FOR ou AFTER. O SQL Server não suporta INSTEAD OF DDL Trigger e podemos ver como evitar algumas operações DDL por meio de DDL Triggers.
- O SQL Server tem funções internas como EVENTDATA() e IS_MEMBER() para uso em gatilhos DDL para obter mais informações relacionadas aos eventos Trigger.
- A função
- EVENTDATA() retorna detalhes completos sobre eventos com escopo de banco de dados ou servidor em formato XML dentro do escopo do gatilho DDL com escopo de banco de dados ou servidor ou gatilhos de logon também. A função EVENTDATA() retorna os detalhes completos do evento para a sessão que executa as atividades DDL ou de logon. EVENTDATA() retorna os detalhes abaixo
- EventType – Tipo de evento que aciona o gatilho DDL disponível na tabela sys.trigger_event_types.
- PostTime – Hora em que o evento foi acionado ou postado.
- SPID – ID da sessão do evento.
- ServerName – nome da instância do SQL Server na qual o evento foi acionado.
- LoginName – Nome de login do SQL Server que executou o evento.
- UserName – Nome de usuário do Login que será dbo por padrão.
- DatabaseName – nome do banco de dados sob o qual o gatilho DDL foi acionado.
- SchemaName – Nome do esquema do objeto que foi afetado.
- ObjectName – Nome do objeto que foi afetado.
- ObjectType – Tipo de objeto do SQL Server, como tabela, exibição, procedimento armazenado.
- TSQLCommand – Script T-SQL que foi executado por um usuário que invocou o DDL Trigger.
- SetOptions – opções SET usadas pelo usuário ou cliente como SSMS enquanto o TSQLCommand foi executado.
- CommandText – instruções DDL ou DCL reais com o evento DDL especificado na tabela sys.trigger_event_types.
- A função IS_MEMBER() retorna se o usuário atual é membro do grupo do Windows ou da função de banco de dados do SQL Server ou não.
- EVENTDATA() retorna detalhes completos sobre eventos com escopo de banco de dados ou servidor em formato XML dentro do escopo do gatilho DDL com escopo de banco de dados ou servidor ou gatilhos de logon também. A função EVENTDATA() retorna os detalhes completos do evento para a sessão que executa as atividades DDL ou de logon. EVENTDATA() retorna os detalhes abaixo
- System DMV sys.triggers armazena a lista de todos os gatilhos no escopo do banco de dados. Podemos usar a consulta abaixo para buscar os detalhes de todos os gatilhos DDL no escopo do banco de dados.
SELECT *
FROM sys.triggers
WHERE type = 'TR';
- System DMV sys.server_triggers armazena a lista de todos os gatilhos no escopo do servidor e podemos usar a consulta abaixo para buscar os detalhes sobre todos os gatilhos DDL no escopo do servidor.
SELECT *
FROM sys.server_triggers;
- As definições do gatilho DDL podem ser visualizadas se o gatilho não estiver criptografado usando qualquer uma das opções abaixo de sys.sql_modules ou usando a função OBJECT_DEFINITION() ou usando o procedimento armazenado 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>';
- Todos os eventos DDL possíveis estão disponíveis na tabela sys.trigger_event_types e podem ser visualizados usando a consulta abaixo.
SELECT *
FROM sys.trigger_event_types;
A sintaxe de um gatilho DDL é:
CREATE TRIGGER <trigger_name>
ON < ALL SERVER | DATABASE >
[ WITH <DDL_trigger_option> [ ,...n ] ]
{ FOR | AFTER } <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }
Criando gatilho DDL com escopo de banco de dados
Vamos criar um gatilho com escopo de banco de dados para rastrear todas as criações de tabela e fazer login em uma tabela de log chamada Track_DDL_Changes usando o script abaixo.
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 criar uma nova tabela chamada trigger_test e verificar se o evento CREATE TABLE foi auditado ou não usando o script abaixo.
CREATE TABLE Trigger_Test ( a int, b datetime);
A seleção dos dados da tabela Track_DDL_Changes mostra que o evento CREATE_TABLE acima foi capturado com sucesso, conforme mostrado abaixo:
Clicar no valor EventData abrirá o valor XML EVENTDATA() em uma nova janela, conforme mostrado abaixo.
Podemos verificar os detalhes completos sobre o evento de acionamento por meio da função EVENTDATA() e, portanto, a função EVENTDATA() desempenharia um papel significativo para qualquer acionador DDL ou LOGON.
Podemos aprimorar ainda mais nosso DDL Trigger com a ajuda da função EVENTDATA() e análise XML e impedir que qualquer pessoa crie qualquer tabela no banco de dados de teste usando o script abaixo:
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
Criação do acionador com escopo de banco de dados concluído com sucesso e vamos verificar criando outra tabela usando o script abaixo.
CREATE TABLE Trigger_Test1 (a int, b datetime);
O gatilho nos impediu de criar novas tabelas neste banco de dados e deixou uma mensagem significativa para os usuários também. Da mesma forma, podemos lidar com qualquer outro evento DDL ou com escopo de servidor para atender aos requisitos.
Para descartar o gatilho DDL no escopo do banco de dados, precisamos usar a sintaxe abaixo:
DROP TRIGGER <trigger_name> ON DATABASE;
E para descartar o gatilho que criamos agora, o script seria
DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
Para visualizar os gatilhos DDL no escopo do banco de dados no SSMS, expanda o banco de dados Test -> Programmability -> Database Triggers, conforme mostrado abaixo.
Semelhante aos gatilhos DML do SQL, os gatilhos DDL podem ser descartados, desativados ou ativados apenas clicando com o botão direito do mouse no nome do gatilho, conforme mostrado abaixo.
Por meio do T-SQL, podemos descartar ou desabilitar ou habilitar o gatilho DDL no escopo do banco de dados usando a sintaxe abaixo:
-- 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 desabilitar o gatilho que criamos, talvez seja necessário usar o script abaixo.
-- 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;
Criando acionador DDL no escopo do servidor
O gatilho DDL no escopo do servidor segue a mesma sintaxe semelhante ao gatilho DDL no escopo do banco de dados, exceto que os eventos serão baseados no escopo do servidor.
Vamos tentar criar um gatilho DDL no escopo do servidor para evitar que qualquer usuário crie um novo banco de dados nesta instância do servidor usando o script abaixo.
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
Ao tentar criar um novo banco de dados usando o comando abaixo, receberemos um erro conforme mostrado abaixo.
CREATE DATABASE DATABASE_TEST;
No SSMS, os acionadores DDL com escopo do servidor em Acionadores na seção Objetos de servidor, conforme mostrado abaixo.
Podemos descartar, desabilitar ou habilitar o gatilho DDL no escopo do servidor simplesmente clicando com o botão direito do mouse no gatilho DDL no escopo do servidor, conforme mostrado abaixo.
Via T-SQL, podemos descartar ou desabilitar ou habilitar usando o comando abaixo.
-- 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;
O objetivo dos gatilhos DDL
- Para auditar quaisquer eventos DDL que ocorram no banco de dados ou no nível do servidor.
- Para evitar que eventos DDL aconteçam no banco de dados ou no nível do servidor.
- Para alertar sempre que algum evento DDL ocorrer no banco de dados ou no nível do servidor.
Acionadores de logon
Os gatilhos de logon, como o nome indica, são executados para eventos de logon no SQL Server. Depois que a fase de autenticação for concluída para um evento de logon, o script LOGON Trigger será executado além da atividade de logon. Se o login não for autenticado com sucesso, os gatilhos LOGON não serão acionados. Os acionadores de logon serão listados no SSMS na seção Acionadores de Objetos de servidor. A sintaxe de um LOGON Trigger é a seguinte:
CREATE TRIGGER <schema_name.trigger_name>
ON ALL SERVER
{ FOR| AFTER } LOGON
AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier > [ ; ] }
Criar gatilhos
Vamos criar um gatilho LOGON simples para capturar mais informações sobre o evento LOGON da função EVENTDATA() conforme mostrado abaixo.
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
O gatilho LOGON acima capturará todos os detalhes sobre uma atividade de login semelhante ao que notamos ao usar a função EVENTDATA() no DDL Trigger. Devemos ter cuidado ao planejar o uso de gatilhos LOGON, pois se houver algum erro de lógica dentro do gatilho, isso não permitiria que ninguém ou a maioria dos usuários se conectassem à instância do SQL Server.
Para DROP, Disable ou Enable LOGON triggers, podemos usar o script abaixo.
-- 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;
A finalidade dos gatilhos de LOGON
- Para auditar quaisquer eventos de LOGON que ocorram no servidor.
- Para evitar que eventos de LOGON aconteçam no servidor
- Para alertar sempre que algum evento de LOGON ocorrer no servidor.
Propriedades do gatilho
sp_settriggerorder
sp_settriggerorder é usado para definir a ordem de execução do gatilho apenas para o primeiro e o último gatilho. Se houver mais de 2 gatilhos DML em uma tabela, digamos 5 gatilhos DML, podemos definir o primeiro gatilho DML e o último gatilho DML, mas não podemos definir a ordem dos 3 gatilhos do meio.
Observação: A configuração da opção FIRST ou LAST é específica para uma categoria de evento específica para acionadores DML. Por exemplo em uma tabela com 3 triggers INSERT, podemos definir qual trigger INSERT é FIRST e qual trigger INSERT é LAST. Se você tiver 3 gatilhos em uma tabela como INSERT, UPDATE e DELETE, não há necessidade de definir a condição de ordem do gatilho.
A sintaxe para definir a ordem do acionador seria assim:
exec sp_settriggerorder @triggername = '<trigger_schema_name.trigger_name>'
, @order = 'FIRST' | 'LAST'
, @stmttype = '<trigger event type>'
, @namespace = 'DATABASE' | 'SERVER' | 'NULL'
Para gatilhos DDL, podemos definir o primeiro e o último gatilhos no escopo do servidor e, em seguida, definir o primeiro e o último gatilhos no escopo do banco de dados. Por exemplo, se tivermos 5 gatilhos no escopo do servidor e 5 gatilhos no escopo do banco de dados, a ordem pode ser definida assim:
- Primeiro gatilho para gatilho DDL no escopo do servidor
- 3 outros gatilhos DDL no escopo do servidor em ordem aleatória
- Último acionador para o acionador DDL no escopo do servidor.
- Primeiro gatilho para gatilho DDL com escopo de banco de dados (um por banco de dados)
- 3 outros gatilhos DDL com escopo de banco de dados em ordem aleatória
- Último gatilho para gatilho DDL com escopo de banco de dados.
Com relação à configuração da primeira ou da última opção, os gatilhos DDL no escopo do banco de dados podem ser solicitados no banco de dados e os gatilhos DDL no escopo do servidor no nível da instância.
Embora o SQL Server nos permita criar muitos gatilhos em uma tabela, é recomendável analisar cuidadosamente os requisitos do gatilho para uma melhor manutenção e solução de problemas.
Acionadores recursivos
O SQL Server também dá suporte à invocação de gatilhos recursivamente para gatilhos DML. Os gatilhos recursivos podem ser classificados como diretos ou indiretos, conforme mostrado abaixo.
Acionadores recursivos diretos – O usuário ou aplicativo atualiza um registro na Tabela A. UPDATE O gatilho A na Tabela A é acionado e atualiza a Tabela A novamente. Como o registro na Tabela A foi atualizado via Trigger, ele invocará novamente UPDATE Trigger A e isso acontecerá de forma recursiva.
Vamos criar um Direct Recursive Triggers na tabela Sales usando o script abaixo:
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
Execute o script abaixo:
UPDATE Sales
SET SalesDate = GETDATE()
WHERE SalesId = 3;
Acionadores recursivos indiretos – O usuário ou aplicativo atualiza um registro na tabela A. O gatilho UPDATE A na tabela A é acionado e atualiza um registro na tabela B. Se a tabela B tiver um gatilho UPDATE para atualizar os registros de volta para a tabela A, ele invocará o gatilho UPDATE no Tabela A que acontecerá recursivamente.
Vamos criar um Indirect Recursive Trigger nas tabelas IDR_Test1 e IDR_Test2 usando o script abaixo:
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
Execute o script abaixo:
UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;
Para evitar esses tipos de invocação de gatilhos recursivos no nível do banco de dados, o SQL Server tem uma opção chamada RECURSIVE_TRIGGERS em cada nível de banco de dados para interromper o acionamento do gatilho recursivo. Por padrão, a opção de gatilho recursivo é definida como False para um banco de dados. Ative somente conforme necessário após uma análise cuidadosa dos impactos no desempenho ou das alterações de dados envolvidas.
No SSMS, clique com o botão direito do mouse em nosso banco de dados de teste -> Escolha Propriedades -> Clique em Opções e role para baixo para ver se a opção Recursive Triggers está habilitada ou não, conforme mostrado abaixo. Para Banco de Dados de Teste, é definido como Falso, pois Falso é o valor padrão para a opção Acionadores Recursivos. Para ativar a opção Recursive Triggers para um banco de dados específico, basta clicar no valor do dropdown, alterá-lo para True e clicar em OK.
Através do T-SQL, podemos verificar a opção Recursive Trigger do banco de dados Test verificando a coluna is_recursive_triggers_on do sys.databases DMV conforme mostrado abaixo.
select name, is_recursive_triggers_on
from sys.databases
where name = 'test'
Para alterar a opção de gatilhos recursivos para um banco de dados (teste no meu exemplo), podemos executar o script abaixo.
ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO
Para desativá-lo de volta ao status falso (status padrão) para um banco de dados (teste no meu exemplo), execute o script abaixo.
ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO
Acionadores aninhados
Os gatilhos recursivos são um exemplo clássico de gatilhos aninhados, mas pode haver poucos outros casos que resultem em aninhamento de vários gatilhos. O SQL Server permite o aninhamento de gatilhos até um máximo de 32 níveis e qualquer gatilho que exceda esse nível de aninhamento será cancelado pelo SQL Server. O SQL Server tem uma configuração em toda a instância para desabilitar a opção de gatilhos aninhados. Observe que o aninhamento de gatilhos do SQL Server usando código CLR ou código gerenciado não está abaixo do limite de 32 níveis, pois está fora do escopo do SQL Server. Por padrão, a opção de gatilhos aninhados será habilitada em todas as instâncias do SQL Server e podemos desativá-la conforme necessário.
Podemos verificar se a opção de gatilhos aninhados está habilitada no nível da instância no SSMS seguindo as etapas abaixo:
Clique com o botão direito do mouse em Servidor -> Escolha Propriedades -> Clique em Avançado
Para desativar ou desativar a opção de gatilhos aninhados, clique no menu suspenso e altere-o para Falso e clique em OK .
Por meio do T-SQL, podemos verificar se a opção de gatilhos aninhados está habilitada verificando a coluna value_in_use em sys.configurations DMV para o nome de configuração de gatilhos aninhados.
Para desabilitar esta opção, precisamos usar o sp_configure o procedimento armazenado do sistema conforme mostrado abaixo:
EXEC sp_configure 'nested triggers', 0;
GO
RECONFIGURE;
GO
Dentro de qualquer gatilho DML ou DDL, para encontrar o nível atual de aninhamento, o SQL Server tem uma função interna chamada TRIGGER_NESTLEVEL para retornar o número de gatilhos executados para a instrução atual que disparou o gatilho, incluindo ele mesmo. A sintaxe da função TRIGGER_NESTLEVEL seria:
SELECT TRIGGER_NESTLEVEL ( object_id, <trigger_type> , <trigger_event_category> )
Onde object_id é o id do objeto do gatilho, trigger_type será AFTER para AFTER gatilho e IOT para INSTEAD OF gatilho e trigger_event_category será DML ou DDL.
Por exemplo, se precisarmos permitir apenas o nível de aninhamento até 10 e aumentar o erro após 10 níveis, podemos fazer isso no gatilho de teste como aqui:
IF ((SELECT TRIGGER_NESTLEVEL(OBJECT_ID('test_trigger'), 'AFTER’, 'DML’)) > 10)
RAISERROR ('Trigger test_trigger nested more than 10 levels.',16, -1)
CRIPTAÇÃO
Para criptografar a lógica ou definição do gatilho, a opção WITH ENCRYPTION pode ser usada na definição do gatilho semelhante a todos os outros objetos do SQL Server.
EXECUTAR COMO Cláusula
Para executar o gatilho usando um contexto de segurança específico, a cláusula EXECUTE AS pode ser usada na definição do gatilho.
NÃO PARA REPLICAÇÃO
Para identificar que o gatilho DML não deve ser invocado enquanto executado por meio de alterações de replicação, a propriedade NOT FOR REPLICATION será definida para todos os objetos no banco de dados do Assinante.
Conclusão
Obrigado por ler o artigo repleto de recursos sobre DDL Triggers e Logon Triggers, onde entendemos o propósito dos DDL e dos acionadores de logon, como criar ou descartar, desabilitar ou habilitar esses acionadores, além de como usar a função EVENTDATA() para rastreamento de atividades DDL ou de logon. Além disso, aprendemos como definir a ordem de execução de vários gatilhos SQL DML ou DDL junto com gatilhos recursivos e aninhados em detalhes e como lidar com gatilhos recursivos ou aninhados com cuidado.