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.
Download as DOCX, PDF, TXT or read online on Scribd
0 ratings0% 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.
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]
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
-- 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.