Tag Archives: DDL Triggers

Data Definition Language (DDL) Triggers in Sql Server

What is DDL Trigger?

Data Definition Language (DDL) Triggers are special kind of Stored Procedure or an operation that gets executed automatically when a DDL Statements like CREATE, ALTER, DROP, GRANT, DENY, REVOKE, and UPDATE STATISTICS statements are executed. Certain system stored procedures that perform DDL like operations can also fire DDL triggers.

Scope of DDL Triggers

DDL triggers can be DATABASE scoped or SERVER scoped. The DDL triggers with Server level scope gets fired in response to a DDL statement with server scope like CREATE DATABASE, CREATE LOGIN, GRANT_SERVER, ALTER DATABASE, ALTER LOGIN etc. Where as DATABASE scoped DDL triggers fire in response to DDL statement with DATABASE SCOPE like CREATE TABLE, CREATE PROCEDURE, CREATE FUNCTION, ALTER TABLE, ALTER PROCEDURE, ALTER FUNCTION etc.

[ALSO READ] Data Manipulation Language (DML) Triggers in Sql Server

Uses of DDL Triggers

DDL triggers will be very handy to audit and control the DDL changes. Below are such example scenarios:

  • Track the DLL changes
    • Track the DDL T-Sql statement which is fired
    • Who has fired the DDL statement. For example we may be interested in identifying who has dropped the table.
    • When the DDL statement is fired etc
  • Block user from doing some DDL changes like DROP TABLE, DROP PROCEDURE etc
  • Allow DDL changes only during specified window (i.e. only during particular hours of the day)

Understand DDL Triggers with extensive list of examples

To understand DDL triggers with extensive list of examples, let us create a demo database with a Table and a Stored Procedure by executing the following Script:

--Create Demo Database
CREATE DATABASE SqlHintsDDLTriggersDemo
GO
USE SqlHintsDDLTriggersDemo
GO
--Create Customer Table
CREATE TABLE Customer 
( CustomerId INT IDENTITY (1, 1) NOT NULL ,
  FirstName NVARCHAR(50), LastName NVARCHAR(50))
GO
--Create a Stored Procedure
CREATE PROCEDURE GetCustomers
AS
BEGIN
	SELECT * FROM Customer
END
GO

EXAMPLE 1: This example DDL trigger blocks user FROM modifying or dropping any Table in the SqlHintsDDLTriggersDemo database

Execute the following statement to create a Trigger which fires in response to ALTER TABLE and DROP TABLE statements

USE SqlHintsDDLTriggersDemo
GO
CREATE TRIGGER DDLTriggerToBlockTableDDL
ON DATABASE
FOR DROP_TABLE,ALTER_TABLE
AS
BEGIN
 PRINT 'Disable trigger DDLTriggerToBlockTableDDL to drop or alter tables'
 ROLLBACK
END

Now try to drop the Customer table by executing the following statement

DROP TABLE Customer 

RESULT:

Disable trigger DDLTriggerToBlockTableDDL to drop or alter tables
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.

From the above result we can see that the DDL trigger DDLTriggerToBlockTableDDL is blocking the user from dropping the Customer table

Let us try to execute the following statement to add DOB column to the Customer Table

ALTER TABLE dbo.Customer ADD DOB DATETIME

RESULT:

Disable trigger DDLTriggerToBlockTableDDL to drop or alter tables
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.

From the above result we can see that the DDL trigger DDLTriggerToBlockTableDDL is blocking the user from adding DOB column to the Customer table

In Sql Server we have an option to disable the trigger without dropping it. Execute the below script to disable the trigger DDLTriggerToBlockTableDDL:

DISABLE TRIGGER DDLTriggerToBlockTableDDL ON DATABASE

Now try to re-execute the following statement to add the DOB column to the Customer Table

ALTER TABLE dbo.Customer ADD DOB DATETIME
GO
SELECT * FROM dbo.Customer

RESULT
ALTER TABLE After Disabling Trigger 1

Execute the following statement to enable back the Disabled trigger DDLTriggerToBlockTableDDL

ENABLE TRIGGER DDLTriggerToBlockTableDDL ON DATABASE

Now try to execute the following statement drop the newly added column DOB from the Customer table by executing the following statement

ALTER TABLE dbo.Customer DROP COLUMN DOB

RESULT:

Disable trigger DDLTriggerToBlockTableDDL to drop or alter tables
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.

We can drop the DDLTriggerToBlockTableDDL trigger by executing the following statement

DROP TRIGGER DDLTriggerToBlockTableDDL ON DATABASE

[ALSO READ] Logon Triggers in Sql Server

EXAMPLE 2: This example DDL trigger blocks user from creating, modifying or dropping any Stored Procedures in the SqlHintsDDLTriggersDemo database

USE SqlHintsDDLTriggersDemo
GO
CREATE TRIGGER DDLTriggerToBlockProcedureDDL
ON DATABASE
FOR DDL_PROCEDURE_EVENTS
AS
BEGIN
 PRINT 'Disable trigger DDLTriggerToBlockProcedureDDL to create, alter or drop procedures'
 ROLLBACK
END

After creating the above trigger, try to drop the stored procedure GetCustomers by executing the following statement

DROP PROCEDURE GetCustomers

RESULT:

Disable trigger DDLTriggerToBlockProcedureDDL to create, alter or drop procedures
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.

In the above example trigger, the trigger action DDL_PROCEDURE_EVENTS is an aggregate event name for the individual trigger event names: CREATE_PROCEDURE, ALTER_PROCEDURE and DROP_PROCEDURE. The system stored procedure SYS.TRIGGER_EVENT_TYPES lists out all the trigger events.

If you want to just block dropping of the stored procedure then in the FOR clause just mention the event DROP_PROCEDURE instead of DDL_PROCEDURE_EVENTS

The below query explains that the DDL_PROCEDURE_EVENTS is an aggregate event name for the individual trigger event names: CREATE_PROCEDURE, ALTER_PROCEDURE and DROP_PROCEDURE.

SELECT * FROM SYS.TRIGGER_EVENT_TYPES 
WHERE parent_type = 10024 or type = 10024
RESULT: TriggerEventTypes

We can drop this stored procedure DDL trigger by executing the following statement:

DROP TRIGGER DDLTriggerToBlockProcedureDDL ON DATABASE

EXAMPLE 3: This example server level trigger blocks user from creating, altering or dropping database

USE MASTER
GO
CREATE TRIGGER DDLTriggerToBlockDatabaseDDL
ON ALL SERVER
FOR DDL_DATABASE_EVENTS
AS
BEGIN
 PRINT 'Disable trigger DDLTriggerToBlockDatabaseDDL to create, alter or drop database'
 ROLLBACK
END

Note: Server scoped triggers need to be created in the MASTER database

After creating the above trigger, try to create the database TestDemoDB

CREATE DATABASE TestDemoDB

RESULT:

Disable trigger DDLTriggerToBlockDatabaseDDL to create, alter or drop database
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.

From the above result we can see that the DDL trigger DDLTriggerToBlockDatabaseDDL is blocking the user from Creating the database

In the above example trigger, the trigger action DDL_DATABASE_EVENTS is an aggregate event name for the individual trigger event names: CREATE_DATABASE, ALTER_DATABASE and DROP_DATABASE. The system stored procedure SYS.TRIGGER_EVENT_TYPES lists out all the trigger events.

If you want to just block creating of the new database then in the FOR clause JUST mention the event CREATE_DATABASE instead of DDL_DATABASE_EVENTS

The below query explains that the DDL_DATABASE_EVENTS is an aggregate event name for the individual trigger event names: CREATE_DATABASE, ALTER_DATABASE and DROP_DATABASE.

SELECT * FROM SYS.TRIGGER_EVENT_TYPES
WHERE parent_type = 10004 or type = 10004

RESULT:
DDLTriggerEventTypes

We can drop this DDL trigger by executing the following statement

DROP TRIGGER DDLTriggerToBlockDatabaseDDL ON ALL SERVER

[ALSO READ] INSERTED and DELETED Logical Tables in Sql Server

EXAMPLE 4: This example explains how we can use DDL triggers to log the stored procedure DDL statement, statement execution time, the user who has fired the statement etc

Let us execute the following statement to create a Audit log table: LogDDLEvents, to capture the Stored Procedure DDL statement

CREATE TABLE dbo.LogDDLEvents
( 	
 EventTime DATETIME,
 LoginName VARCHAR(50),
 TSQLCommand NVARCHAR(MAX) 
)

Now execute the following statement to create a SP DDL actions. This SP is using the EVENTDATA() function to capture the triggering SP DDL statement into the audit log table LogDDLEvents

USE SqlHintsDDLTriggersDemo
GO
CREATE TRIGGER DDLTriggerForSPDDL
ON DATABASE
FOR DDL_PROCEDURE_EVENTS
AS
BEGIN
 SET NOCOUNT ON
 DECLARE @EventData XML = EVENTDATA()
 INSERT INTO dbo.LogDDLEvents(EventTime,LoginName, TSQLCommand)	
 SELECT @EventData.value('(/EVENT_INSTANCE/PostTime)[1]'
                                  , 'DATETIME'),
  @EventData.value('(/EVENT_INSTANCE/LoginName)[1]'
                                  , 'VARCHAR(50)'),
 @EventData.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]'
                                  , 'NVARCHAR(MAX)')
END

Now try to create a Stored Procedure by executing the following statement

CREATE PROCEDURE GetAllCustomers
AS
BEGIN
    SELECT * FROM Customers
END

After executing the above statement let us verify the audit table: LogDDLEvents data by executing the following statement

SELECT * FROM dbo.LogDDLEvents

RESULT
DDL Trigger Audit Log Table
Let us check whether apart from adding to the audit log table: LogDDLEvents, the Stored Procedure is created or not by executing the following statement?

SP_HELPTEXT GetAllCustomers

StoredProcedure

We can drop this DDL trigger by executing the following statement

DROP TRIGGER DDLTriggerForSPDDL ON DATABASE

[ALSO READ]
Introduction to Triggers
Data Manipulation Language (DML) Triggers
INSERTED and DELETED Logical Tables
Logon Triggers