0% found this document useful (0 votes)
233 views

DDL Triggers Implementation For Repository-Like Solution To User Objects Source Versioning

This document describes the implementation of DDL triggers in SQL Server to provide versioning of database objects. It involves creating log tables to store metadata about DDL changes, creating DDL triggers on database events like CREATE, ALTER and DROP that insert records into the log tables. The triggers also grant appropriate permissions to users when objects are created. Sample scripts are provided to set up the log tables, parameters table, create the DDL triggers and prepopulate the log with existing objects.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
233 views

DDL Triggers Implementation For Repository-Like Solution To User Objects Source Versioning

This document describes the implementation of DDL triggers in SQL Server to provide versioning of database objects. It involves creating log tables to store metadata about DDL changes, creating DDL triggers on database events like CREATE, ALTER and DROP that insert records into the log tables. The triggers also grant appropriate permissions to users when objects are created. Sample scripts are provided to set up the log tables, parameters table, create the DDL triggers and prepopulate the log with existing objects.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 6

1

DDL Triggers implementation for repository-like solution to


user objects source versioning
Author: Federico Marzullo ([email protected])
Date: 03/25/2011
Works under: Microsoft SQL Server 2005 or above
Full script file:
ASSET1.sql

Introduction:
The objective of this asset is to easily show the implementation of DDL Triggers as a feasible option for DDL code
versioning. How many times we ended up on situations like: I dropped the wrong View or Oooops, I wasnt
supposed to commit those changes to this Stored Procedure yet or Hey, till the last change done to this Function
everything was working perfectly fine, whats different???? Every SQL Server developer knows that here theres
no such thing as the Ctrl+Z (undo) once you have made DDL changes like the above, right?
Well, these script chunks will allow you to have a nice workaround in a few steps.
In addition, the scripts below include objects appropriate granting for selection or execution depending on the
object type.
What really is a DDL trigger anyway?
One thing that most of the times scare more than one DBA out of their pants is the trigger concept. There is this
well known sort of panic related to the risk of performance leaks and locks around them, which is actually pretty
accurate; working with triggers isnt a thing to be taken slightly into consideration.
Until SQL Server 2005 the only triggers existing were DML (Data Manipulation Language) type, that means only for
transactional operations: INSERT, UPDATE, DELETE
But DDL triggers are a whole different thing Even when at the beginning they behave the same way as the DML
triggers do, these ones works with Server and Database events, but this time being called thru: CREATE, UPDATE,
DROP commands (remember DDL stands for Data Definition Language).
Below youll find a reference chart, containing both Server and Database related events that can be applied on a
DDL Trigger implementation:


2




3

For practical and illustrative purposes, this asset will use only Tables, Stored Procedures, Functions and Views
related events.
How to:
1. Create the table structures needed to support the implementation
-- this table will hold the objects versioning, one row per version per object as
they're created/updated/dropped
CREATE TABLE dbo.AdministratorLog(AdministratorLogID int IDENTITY(1, 1) NOT NULL,
EventType varchar(50) NULL,
ObjectName varchar(256) NULL,
ObjectType varchar(25) NULL,
SQLCommand text NULL,
LoginName varchar(256) NULL,
[TimeStamp] datetime NOT NULL)

GO

-- now we need to set the proper constraints, indexes and defaults for the table
columns
ALTER TABLE dbo.AdministratorLog ADD CONSTRAINT
DF_AdministratorLog_TimeStamp DEFAULT getdate() FOR TimeStamp
GO

ALTER TABLE dbo.AdministratorLog ADD CONSTRAINT PK_AdministratorLog PRIMARY KEY
CLUSTERED (AdministratorLogID) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY =
OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

CREATE NONCLUSTERED INDEX IX_AdministratorLog_EventType ON dbo.AdministratorLog
(EventType) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX IX_AdministratorLog_ObjectName ON dbo.AdministratorLog
(ObjectName) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX IX_AdministratorLog_ObjectType ON dbo.AdministratorLog
(ObjectType) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

-- standard parameters table, this time used to keep track on the users you want to
give grants to on the given object. In this script its use is intended to keep the
code dinamically alive as the application evolves
CREATE TABLE dbo.Parameters(ParameterID int IDENTITY(1, 1) NOT NULL,
Parameter varchar(50) NULL,
Param_Value varchar(250) NULL)

GO


4

-- now we need to set the proper index as before
ALTER TABLE dbo.Parameters ADD CONSTRAINT PK_Parameters PRIMARY KEY CLUSTERED
(ParameterID) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO


2. The DDL Trigger implementation
-- variables declaration
DECLARE @dbname varchar(50), @grants varchar(500), @strSQL nvarchar(500)

SELECT @dbname = DB_NAME() --the current DB where the script is being executed
SET @grants = 'webapp_user, webapp_job' -- the DB users to which grants will be given
on objects creations

-- now we insert the corresponding parameter into the table
INSERT INTO dbo.Parameters(Parameter, Param_Value) VALUES('DDL Triggers - Grants',
@grants)

SET @strSQL = 'GRANT SELECT, INSERT ON dbo.AdministratorLog TO ' + @grants

EXEC sp_executesql @strSQL

-- now we state the trigger creation and its events
CREATE TRIGGER DDL_TRG_Admin_SourceControl_Grants ON DATABASE
FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW

AS

SET NOCOUNT ON

DECLARE @data xml, @ObjectName nvarchar(256), @EventType varchar(50), @ObjectType
nvarchar(25), @grantsTo nvarchar(256), @strSQL nvarchar(500)
SET @data = eventdata()

-- now we insert the user objects info with all its content
INSERT INTO dbo.AdministratorLog(EventType, ObjectName, ObjectType, SQLCommand,
LoginName)
VALUES(@data.value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(50)'),
@data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(256)'),
@data.value('(/EVENT_INSTANCE/ObjectType)[1]', 'varchar(25)'),
@data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'varchar(max)'),
@data.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(256)'))

-- getting the event type
SET @EventType = eventdata().value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(50)')






5

-- if the event type is CREATE a new user object has been created and therefore
corresponding grants should be given
IF left(@EventType, charindex('_', @EventType) - 1) = 'CREATE' BEGIN
SET @ObjectName = eventdata().value('(/EVENT_INSTANCE/ObjectName)[1]',
'nvarchar(256)')
SET @ObjectType = eventdata().value('(/EVENT_INSTANCE/ObjectType)[1]',
'nvarchar(25)')

-- getting the users to whom the grants will be assigned
SELECT @grantsTo = Param_Value FROM dbo.parameters WHERE Parameter = 'DDL
Triggers - Grants'

IF @ObjectType = 'FUNCTION'
SET @strSQL = 'EXECUTE ON dbo.' + @ObjectName

IF @ObjectType = 'PROCEDURE'
SET @strSQL = 'EXECUTE ON dbo.' + @ObjectName

IF @ObjectType = 'TABLE'
SET @strSQL = 'SELECT, INSERT, UPDATE, DELETE ON dbo.' + @ObjectName

IF @ObjectType = 'VIEW'
SET @strSQL = 'SELECT ON dbo.' + @ObjectName

--SQL GRANTS
SET @strSQL = 'GRANT ' + @strSQL + ' TO ' + @grantsTo

--EXEC SQL
EXEC sp_executesql @strSQL
END

GO


3. Preload the Log table as version 0 for every user object in the DB
The aim of the script below is to preload the repository table, working as version 0 for every user object in the
DB. Once executed, the table will have one row per user object

-- populate the table with the last version of every object in the DB so far
INSERT INTO dbo.AdministratorLog(EventType, ObjectName, ObjectType, SQLCommand,
LoginName)
SELECT CASE SO.xType WHEN 'P' THEN 'CREATE_PROCEDURE' WHEN 'FN' THEN
'CREATE_FUNCTION' WHEN 'V' THEN 'CREATE_VIEW' END,
SO.name, CASE SO.xType WHEN 'P' THEN 'PROCEDURE' WHEN 'FN' THEN
'FUNCTION' WHEN 'V' THEN 'VIEW' END,
SC.[Text], 'user_that_created_the_version'
FROM sys.syscomments SC
INNER JOIN sys.sysobjects SO ON SO.id = SC.id
WHERE SO.xType IN('P', 'FN', 'V', 'U') -- filter Procedures, Functions, Views and
Tables
AND NOT SO.Name LIKE('dt_%') -- remove server objects from selection
AND NOT SO.Name LIKE('sys%') -- remove server objects from selection




6

4. Future nice to have upgrades
So far I havent found a way to keep blanks and tabs on the sources, the repository stores the scripts perfectly fine
but removes those chars. If you find some workaround on that, please let me know.

You might also like