miércoles, 14 de abril de 2010

Meet to DDL Triggers

 

 

 

 

 

En el día a día de la administración de las bases de datos, los cambios en el schema son demasiados, por lo que se vuelve sumamente necesario contar con un mecanismo de auditoria de dichos cambios, que permita dar respuesta a las preguntas ¿qué? ¿como? ¿cuando? ¿quien? WTF?, y que eventualmente también te permitirá echarle la culpa al verdadero responsable de una catástrofe en las BD.

Bueno en este y otros escenarios más o menos parecidos tienen significado los DDL (Data Definition Language) Triggers, los cuales al igual que los DML Triggers se disparan al generarseciertos eventos en ciertos objetos, es decir, nosotros crearemos un Trigger que realize "algo" en el momento que tenga lugar un suceso determinado en el objeto de la BD que nos interese.

Estos Trigger (a partir de este momento TR) tienen 2 ambitos: Servidor y Base de Datos, y como es de esperarse responden a diferentes eventos.

 

Tabla de Eventos para la BD. (MSDN)

 

CREATE_APPLICATION_ROLE (Applies to CREATE APPLICATION ROLE statement andsp_addapprole. If a new schema is created, this event also triggers a CREATE_SCHEMA event.)

ALTER_APPLICATION_ROLE (Applies to ALTER APPLICATION ROLE statement andsp_approlepassword.)

DROP_APPLICATION_ROLE (Applies to DROP APPLICATION ROLE statement andsp_dropapprole.)

CREATE_ASSEMBLY

ALTER_ASSEMBLY

DROP_ASSEMBLY

ALTER_AUTHORIZATION_DATABASE (Applies to ALTER AUTHORIZATION statement when ON DATABASE is specified, and sp_changedbowner.)

   

CREATE_CERTIFICATE

ALTER_CERTIFICATE

DROP_CERTIFICATE

CREATE_CONTRACT

DROP_CONTRACT

 

GRANT_DATABASE

DENY_DATABASE

REVOKE_DATABASE

CREATE_EVENT_NOTIFICATION

DROP_EVENT_NOTIFICATION

 

CREATE_FUNCTION

ALTER_FUNCTION

DROP_FUNCTION

CREATE_INDEX

ALTER_INDEX

DROP_INDEX

CREATE_MESSAGE_TYPE

ALTER_MESSAGE_TYPE

DROP_MESSAGE_TYPE

CREATE_PARTITION_FUNCTION

ALTER_PARTITION_FUNCTION

DROP_PARTITION_FUNCTION

CREATE_PARTITION_SCHEME

ALTER_PARTITION_SCHEME

DROP_PARTITION_SCHEME

CREATE_PROCEDURE

ALTER_PROCEDURE

DROP_PROCEDURE

CREATE_QUEUE

ALTER_QUEUE

DROP_QUEUE

CREATE_REMOTE_SERVICE_BINDING

ALTER_REMOTE_SERVICE_BINDING

DROP_REMOTE_SERVICE_BINDING

CREATE_ROLE (Applies to CREATE ROLE statement, sp_addrole, and sp_addgroup.)

ALTER_ROLE

DROP_ROLE (Applies to DROP ROLE statement,sp_droprole, and sp_dropgroup.)

CREATE_ROUTE

ALTER_ROUTE

DROP_ROUTE

CREATE_SCHEMA (Applies to CREATE SCHEMA statement, sp_addrole, sp_adduser,sp_addgroup, and sp_grantdbaccess.)

ALTER_SCHEMA (Applies to ALTER SCHEMA statement and sp_changeobjectowner.)

DROP_SCHEMA

CREATE_SERVICE

ALTER_SERVICE

DROP_SERVICE

CREATE_STATISTICS

DROP_STATISTICS

UPDATE_STATISTICS

CREATE_SYNONYM

DROP_SYNONYM

 

CREATE_TABLE

ALTER_TABLE

DROP_TABLE

CREATE_TRIGGER

ALTER_TRIGGER

DROP_TRIGGER

CREATE_TYPE (Applies to CREATE TYPE statement and sp_addtype.)

DROP_TYPE (Applies to DROP TYPE statement andsp_droptype.)

 

CREATE_USER (Applies to CREATE USER statement, sp_adduser, andsp_grantdbaccess.)

ALTER_USER

DROP_USER (Applies to DROP USER statement,sp_dropuser, and sp_revokedbaccess.)

CREATE_VIEW

ALTER_VIEW

DROP_VIEW

CREATE_XML_SCHEMA_COLLECTION

ALTER_XML_SCHEMA_COLLECTION

DROP_XML_SCHEMA_COLLECTION

 

Tabla de Eventos a nivel del Servidor. (MSDN)

ALTER_AUTHORIZATION_SERVER

   

CREATE_DATABASE

ALTER_DATABASE

DROP_DATABASE

CREATE_ENDPOINT

ALTER_ENDPOINT

DROP_ENDPOINT

CREATE_LOGIN (Applies to CREATE LOGIN statement, sp_addlogin,sp_grantlogin, xp_grantlogin, and sp_denylogin when used on a nonexistent login that must be implicitly created.)

ALTER_LOGIN (Applies to ALTER LOGIN statement, sp_defaultdb,sp_defaultlanguage, sp_password, and sp_change_users_loginwhen Auto_Fix is specified.)

DROP_LOGIN (Applies to DROP LOGIN statement, sp_droplogin, sp_revokelogin, and xp_revokelogin.)

GRANT_SERVER

DENY_SERVER

REVOKE_SERVER

 

 

EVENTDATA()

 

Bien, cuando los eventos mencionados se disparan, la información que detalla lo ocurrido es almacenada en un archivo XML y que es accesible mediante la función EVENTDATA(), a partir de ahí podemos ocupar XQUERY, para consultar la información que necesitemos de forma especifica.

 

A continuación les muestro un ejemplo de la información proporcionada por EVENTDATA(). (Fuente)

 

 

<EVENT_INSTANCE>
<EventType>CREATE_TABLE</EventType>
<PostTime>2005-07-30T10:48:52.537</PostTime>
<SPID>55</SPID>
<ServerName>PSE-TEST-JON1</ServerName>
<LoginName>PSE-TEST-JON1\Administrator</LoginName>
<UserName>dbo</UserName>
<DatabaseName>AdventureWorks</DatabaseName>
<SchemaName>Person</SchemaName>
<ObjectName>Address</ObjectName>
<ObjectType>TABLE</ObjectType>
<TSQLCommand>
<SetOptions ANSI_NULLS="ON"
ANSI_NULL_DEFAULT="ON"
ANSI_PADDING="ON"
QUOTED_IDENTIFIER="ON"
ENCRYPTED="FALSE" />
<CommandText>CREATE TABLE [Person].[Address](
[AddressID] [int]
IDENTITY (1, 1)
NOT FOR REPLICATION NOT NULL,
[AddressLine1] [nvarchar](60) NOT NULL,
[AddressLine2] [nvarchar](60) NULL,
[City] [nvarchar](30) NOT NULL,
[StateProvinceID] [int] NOT NULL,
[PostalCode] [nvarchar](15) NOT NULL,
[rowguid] uniqueidentifier ROWGUIDCOL NOT NULL CONSTRAINT
[DF_Address_rowguid] DEFAULT (NEWID()),
[ModifiedDate] [datetime] NOT NULL CONSTRAINT
[DF_Address_ModifiedDate] DEFAULT (GETDATE())
) ON [PRIMARY];</CommandText>
</TSQLCommand>
</EVENT_INSTANCE>



 



Como podemos ver, la información presentada es bastante útil e importante, aunque en lo personal me gustaría que cuando realizaras una operación de modificación o de eliminación de un objeto, guardara un copia de la definición anterior. Para aquellos que crear valido recurrir a las Vistas de Sistema y buscar la definición les comento ya lo intente y desafortunadamente el trigger se ejecuta posteriormente al evento de tal forma que la definición de los objetos ya ha cambiado en las vistas de sistema.



 



Bien a continuación pongo un ejemplo propio del trigger que he implementado y que de momento me funciona correctamente, seguramente podrán mejorarlo y adaptarlo a sus necesidades así que ojala lo podamos comentar.



 



/**

* Trigger DDL para auditoria de cambios en el schema de las bd


* Version 1, Alamacena el detalle de los cambios en el schema de


*            las bd, evita que se borren tablas.


* 12 de Abril de 2010.


* I.S.C. Juan Carlos Armenta Bautista


**/


GO



IF  EXISTS (SELECT * FROM sys.triggers WHERE parent_class_desc = 'DATABASE' AND name = N'tr_chge_log_Db')

BEGIN


    DISABLE TRIGGER [tr_chge_log_Db] ON DATABASE;


    SELECT '[tr_chge_log_Db] Deshabilitado.....' AS msg;


END


GO



/****** Object:  DdlTrigger [tr_chge_log_Db]    Script Date: 04/12/2010 14:45:55 ******/

IF  EXISTS (SELECT * FROM sys.triggers WHERE parent_class_desc = 'DATABASE' AND name = N'tr_chge_log_Db')


BEGIN


    DROP TRIGGER [tr_chge_log_Db] ON DATABASE


    SELECT '[tr_chge_log_Db] Borrado.....' AS msg;


END


GO



create TRIGGER tr_chge_log_Db

ON DATABASE


FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE


    ,CREATE_VIEW, ALTER_VIEW, DROP_VIEW


    ,CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE


    ,CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION


    ,CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER


    ,CREATE_INDEX, ALTER_INDEX, DROP_INDEX


AS


BEGIN


    SET NOCOUNT ON;


    SET ARITHABORT ON;



BEGIN TRY

    DECLARE @evento as XML;


    SET @evento=EVENTDATA();


    INSERT INTO msdb.dbo.change_db_sch_log(sch_obj,nom_bd,tip_evt,nom_obj,tip_obj,tsql_cmd,nom_login)


    SELECT @evento.value('(/EVENT_INSTANCE/SchemaName)[1]','varchar(256)'),


           @evento.value('(/EVENT_INSTANCE/DatabaseName)[1]','varchar(256)'),


           @evento.value('(/EVENT_INSTANCE/EventType)[1]','varchar(50)'),


           @evento.value('(/EVENT_INSTANCE/ObjectName)[1]','varchar(256)'),


           @evento.value('(/EVENT_INSTANCE/ObjectType)[1]','varchar(25)'),


           @evento.value('(/EVENT_INSTANCE/TSQLCommand)[1]','varchar(max)'),


           @evento.value('(/EVENT_INSTANCE/LoginName)[1]','varchar(256)')


    -- Validacion del tipo de evento, si es un drop_table, se anula la operacion


    IF @evento.value('(/EVENT_INSTANCE/EventType)[1]','varchar(50)')='DROP_TABLE'


    BEGIN


        RAISERROR ('No se permite la eliminacion de tablas en la BD.',16,1);


        ROLLBACK;


    END


END TRY


BEGIN CATCH


    DECLARE @ErrorMessage NVARCHAR(4000);


    DECLARE @ErrorSeverity INT;


    DECLARE @ErrorState INT;



    SELECT  @ErrorMessage = ERROR_MESSAGE(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE();



    RAISERROR (@ErrorMessage,@ErrorSeverity,@ErrorState);

    ROLLBACK;


END CATCH   
END


GO



SELECT '[tr_chge_log_Db] Creado.....' AS msg;





 



Notas finales.



Como verán para poder borrar o modificar el trigger primero es necesario deshabilitarlo. Asi pues 2 instrucciones que seguro les serviran mucho son:



 



ENABLE TRIGGER tg_name ON DATABASE;



 



DISABLE TRIGGER tg_name ON DATABASE;



 



que creo no requieren mayor explicación.



 



Sale pues hasta aquí no más, ojala que les sea de utilidad mi primer post.



 



k@rloz.@rb@