CREATE PROC pAutoAudit (
   @SchemaName SYSNAME = 'dbo',
   @TableName SYSNAME,
   @StrictUserContext BIT = 1,  -- 2.00 if 0 then permits DML setting of created, createdby, modified, modifiedby
   @LogSQL BIT = 0,
   @BaseTableDDL BIT = 1,
   @LogInsert TINYINT = 1
) 
AS 
SET NoCount ON

DECLARE @version VARCHAR(5) 
  SET @version = '2.00h'
  
IF @StrictUserContext = 0 AND @BaseTableDDL = 0 
  BEGIN 
    RAISERROR('@StrictUserContext = 0 requires  @BaseTableDDL = 1. Cannot apply AutoAudit. ' , 16,1)
    RETURN 
  END   

-- script to create autoAudit triggers
DECLARE 
   @SQL NVARCHAR(max),
   @ColumnName  sysname,
   @PKColumnName sysname, 
   @PKDataType sysname
   
--Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id where o.name =  
--= 'Department' and s.name = 'HumanResources'


-- drop existing insert trigger
SET @SQL = 'If EXISTS (Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id  '
       + ' where s.name = ''' + @SchemaName + ''''
       + '   and o.name = ''' + @TableName + '_Audit_Insert' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Insert'
EXEC (@SQL)

-- drop existing update trigger
SET @SQL = 'If EXISTS (Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id  '
       + ' where s.name = ''' + @SchemaName + ''''
       + '   and o.name = ''' + @TableName + '_Audit_Update' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Update'
EXEC (@SQL)

-- drop existing delete trigger
SET @SQL = 'If EXISTS (Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id  '
       + ' where s.name = ''' + @SchemaName + ''''
       + '   and o.name = ''' + @TableName + '_Audit_Delete' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Delete'
EXEC (@SQL)

-- drop existing _deleted view
SET @SQL = 'If EXISTS (Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id  '
       + ' where s.name = ''' + @SchemaName + ''''
       + '   and o.name = ''v' + @TableName + '_Deleted' + ''' )'
       + ' DROP VIEW ' + @SchemaName + '.v' + @TableName + '_Deleted'
EXEC (@SQL)

-- drop existing _RowHistory UDF
SET @SQL = 'If EXISTS (Select * from sys.objects o join sys.schemas s on o.schema_id = s.schema_id  '
       + ' where s.name = ''' + @SchemaName + ''''
       + '   and o.name = ''' + @TableName + '_RowHistory' + ''' )'
       + ' DROP FUNCTION ' + @SchemaName + '.' + @TableName + '_RowHistory'
EXEC (@SQL)


IF @BaseTableDDL = 1 
  BEGIN 
  
    -- add created column 
    IF not exists (select *
			      from sys.tables t
				    join sys.schemas s
				      on s.schema_id = t.schema_id
				    join sys.columns c
				      on t.object_id = c.object_id
			      where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Created')
      BEGIN -- is this default causing an issue? 
        IF @StrictUserContext = 1                                                                                        
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD Created DateTime NOT NULL Constraint ' + @TableName + '_Created_df Default GetDate()'
        ELSE   
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD Created DateTime NULL Constraint ' + @TableName + '_Created_df Default GetDate()'
        EXEC (@SQL)
      END

    -- add createdBy column 
    IF not exists (select *
			      from sys.tables t
				    join sys.schemas s
				      on s.schema_id = t.schema_id
				    join sys.columns c
				      on t.object_id = c.object_id
			      where  t.name = @TableName AND s.name = @SchemaName and c.name = 'CreatedBy')
      BEGIN 
        IF @StrictUserContext = 1                                                                                        
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD CreatedBy NVARCHAR(128) NOT NULL Constraint ' + @TableName + '_CreatedBy_df Default(Suser_SName())'
        ELSE   
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD CreatedBy NVARCHAR(128) NULL Constraint ' + @TableName + '_CreatedBy_df Default(Suser_SName())'
        EXEC (@SQL)
      END

    -- add Modified column 
    IF not exists( select *
			      from sys.tables t
				    join sys.schemas s
				      on s.schema_id = t.schema_id
				    join sys.columns c
				      on t.object_id = c.object_id
			      where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Modified')
      BEGIN                                                                                               
        IF @StrictUserContext = 1                                                                                        
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD Modified DateTime NOT NULL Constraint ' + @TableName + '_Modified_df Default GetDate()'
        ELSE   
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD Modified DateTime NULL Constraint ' + @TableName + '_Modified_df Default GetDate()'
        EXEC (@SQL)
      END
      
    -- add createdBy column 
    IF not exists (select *
			      from sys.tables t
				    join sys.schemas s
				      on s.schema_id = t.schema_id
				    join sys.columns c
				      on t.object_id = c.object_id
			      where  t.name = @TableName AND s.name = @SchemaName and c.name = 'ModifiedBy')
      BEGIN 
        IF @StrictUserContext = 1                                                                                        
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD ModifiedBy NVARCHAR(128) NOT NULL Constraint ' + @TableName + '_ModifiedBy_df Default(Suser_SName())'
        ELSE  
          SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD ModifiedBy NVARCHAR(128) NULL Constraint ' + @TableName + '_ModifiedBy_df Default(Suser_SName())'
        EXEC (@SQL)
      END  

    -- add RowVersion column 
    IF not exists( select *
			      from sys.tables t
				    join sys.schemas s
				      on s.schema_id = t.schema_id
				    join sys.columns c
				      on t.object_id = c.object_id
			      where  t.name = @TableName AND s.name = @SchemaName and c.name = 'RowVersion')
      BEGIN   
        SET @SQL = 'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ADD RowVersion INT NULL Constraint ' + @TableName + '_RowVersion_df Default 1'
        EXEC (@SQL)
      END
      
 END -- @BaseTableDDL = 1     

-- get PK Column (1)  
select @PKColumnName = c.name, @PKDataType = ty.name
  from sys.tables t
    join sys.schemas s
      on s.schema_id = t.schema_id
    join sys.indexes i
      on t.object_id = i.object_id
    join sys.index_columns ic
  	  on i.object_id = ic.object_id
      and i.index_id = ic.index_id
    join sys.columns c
      on ic.object_id = c.object_id
      and ic.column_id = c.column_id
	join sys.types as ty
	  on ty.user_type_id = c.user_type_id
  where is_primary_key = 1 AND t.name = @TableName AND s.name = @SchemaName AND ic.index_column_id = 1

-- Table no-PK Check  
  IF @PKColumnName IS NULL 
  BEGIN 
    PRINT '*** ' + @SchemaName + '.' + @TableName + ' invalid Table - no Primary Key. No triggers created.'
    RETURN
  END  
  
-- Table HierarchyID-PK Check  
  IF @PKDataType = 'HierarchyID'
  BEGIN 
    PRINT '*** ' + @SchemaName + '.' + @TableName + ' HierarchyID PK. No triggers created.'
    RETURN
  END  

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- pull column information into temporary tables
declare @columnData table
(
	ColumnId int,
	ColumnName sysname,
	TypeName sysname,
	IsDDLColumn bit,
	NullValue nvarchar(128)
)
insert into @columnData
select c.column_id, c.name, ty.name,
	CASE
		WHEN c.name in ('Created', 'Modified','CreatedBy', 'ModifiedBy','RowVersion') THEN 1
		ELSE 0
	END,
	CASE ty.name
		WHEN 'HierarchyID' THEN '0x999999'
		WHEN 'uniqueidentifier' THEN '''00000000-0000-0000-0000-000000000000'''
		WHEN 'DECIMAL' THEN '0'
		WHEN 'NUMERIC' THEN '0'
		ELSE ''''''
	END   
from sys.tables as t
  join sys.columns as c
    on t.object_id = c.object_id
  join sys.schemas as s
    on s.schema_id = t.schema_id
  join sys.types as ty
    on ty.user_type_id = c.user_type_id
where t.name = @TableName AND s.name = @SchemaName 
  AND c.is_computed = 0
  -- version 1.09 modified list of data types
  -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
  AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'binary', 'varbinary', 'timestamp')
order by c.column_id

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- build insert trigger 
  
SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Insert' + ' ON ['+ @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + ' AFTER Insert' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' SET ARITHABORT ON ' + Char(13) + Char(10)
      
       + ' -- generated by AutoAudit Version ' + @Version + ' on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10)
       + ' -- autoaudit.codeplex.com ' + Char(13) + Char(10) + Char(13) + Char(10)

       + ' -- Options: ' + Char(13) + Char(10)
       + ' --   StrictUserContext  :' + CAST(@StrictUserContext as CHAR(1)) + Char(13) + Char(10)
       + ' --   LogSQL             :' + CAST(@LogSQL as CHAR(1)) + Char(13) + Char(10)
       + ' --   BaseTableDDL       :' + CAST(@BaseTableDDL as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
       + ' --   LogInsert          :' + CAST(@LogInsert as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
       
       + 'DECLARE ' + Char(13) + Char(10)
       + '  @AuditTime DATETIME, ' + Char(13) + Char(10)
       + '  @IsDirty BIT' + Char(13) + Char(10)
       
       -- keep the variable initialization separate for SQL Server 2005
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'SET @IsDirty = 0' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' Begin Try ' + Char(13) + Char(10)
       
       
   IF @LogSQL = 1 
     BEGIN  
     	 select @SQL = @SQL
         + ' -- capture SQL Statement' + Char(13) + Char(10)
         + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
         + ' DECLARE  @inputbuffer TABLE' + Char(13) + Char(10) 
         + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
         + ' SET @ExecStr = ''DBCC INPUTBUFFER(@@SPID) with no_infomsgs''' + Char(13) + Char(10)
         + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
         + '   EXEC (@ExecStr)' + Char(13) + Char(10)
         + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
         + Char(13) + Char(10) 
     END   
            
IF @LogInsert >= 1 -- log the PK row to the Audit Trail table as the insert event 
-- for PK column
	select @SQL = @SQL
          + Char(13) + Char(10)
		      + '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, NewValue, RowVersion)' + Char(13) + Char(10)
		      + '   SELECT ' 
		      
		      -- StrictUserOption
		      + CASE @StrictUserContext
		          WHEN 0 -- allow DML setting of created/modified user and datetimes
		            THEN ' COALESCE(Inserted.Created, @AuditTime), COALESCE(Inserted.CreatedBy, Suser_SName()),'
		          ELSE -- block DML setting of user context 
		             ' @AuditTime, Suser_SName(),'
		        END 
		      
		      + ' APP_NAME(), Host_Name(), ' 
          + '''' + @SchemaName + '.' + @TableName + ''', ''i'','
          
          -- if @LogSQL is off then the @UserSQL variable has not been declared
          + CASE @LogSQL
              WHEN 1 THEN ' @UserSQL, '
              ELSE ' NULL, ' 
           END  
          + Char(13) + Char(10)  
          
          + ' Inserted.[' + @PKColumnName + '],' + Char(13) + Char(10) 
          + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
          + '        NULL,     -- Secondary Row Value (e.g. Order Number for an Order Detail Line)' + Char(13) + Char(10)
          + '        ''[' + ColumnName + ']'','   
          + ' Cast(Inserted.[' + ColumnName + '] as VARCHAR(50)), 1' + Char(13) + Char(10)
          + '          FROM Inserted' + Char(13) + Char(10)
          + '          WHERE Inserted.['+ ColumnName + '] is not null' + Char(13) + Char(10)+ Char(13) + Char(10)
	  from @columnData
        where ColumnName = @PKColumnName
	  order by ColumnId


	  
IF @LogInsert = 2 -- log every column to the Audit Trail table 
----------------------------------------------------------------------------------
-- BEGIN FOR EACH COLUMN
----------------------------------------------------------------------------------	  
	select @SQL = @SQL  

	        --    1.10b Cautious that the overuse of IF branching may cause recompiles or bad query execution plans
	        --    +  ' IF UPDATE([' + c.name + '])' + Char(13) + Char(10)     

         + '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, NewValue '
         
         + CASE @BaseTableDDL  
             WHEN 1 THEN ', Rowversion) '
             ELSE ') ' -- gotta figure out a way to increment the RowVersion in the Audit table from the Audit table alone without killing perf !
           END  + Char(13) + Char(10)  

         + '     SELECT '

         -- StrictUserOption
   	     + CASE 
   	         WHEN @StrictUserContext = 1 THEN ' @AuditTime, SUSER_SNAME(),'
   	         ELSE 'COALESCE(Inserted.Modified, @AuditTime), COALESCE(Inserted.ModifiedBy, Suser_Sname()),'
           END

         + ' APP_NAME(), Host_Name(), ' 
         + '''' + @SchemaName + '.' + @TableName + ''', ''i'','  

         -- if @LogSQL is off then the @UserSQL variable has not been declared
         + CASE @LogSQL
             WHEN 1 THEN ' @UserSQL, '
             ELSE ' NULL, ' 
           END  
       
       -- 1.09 handle HierarchyID PK  
       + ' Convert(VARCHAR(50), Inserted.[' + @PKColumnName + ']),' + Char(13) + Char(10) 
       ----------------
       + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
       + '        NULL,     -- Secondary Row Value (e.g. Order Number for an Order Detail Line)' + Char(13) + Char(10)
       + '        ''[' + ColumnName + ']'',' 
         
       -- 1.09 changed from cast to convert to handle HierarchyID conversion 
       + ' Convert(VARCHAR(50), Inserted.[' + ColumnName + '])' + Char(13) + Char(10)
    
       + CASE @BaseTableDDL  
           WHEN 1 THEN ', 1'
           ELSE '' -- gotta figure out a way to increment the RowVersion in the Audit table !
         END  + Char(13) + Char(10)  
       
       + '          FROM Inserted' + Char(13) + Char(10)
       
       + '   IF @@RowCount > 0 SET @IsDirty = 1' + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	  from @columnData
        where IsDDLColumn = 0
           AND ColumnName <> @PKColumnName  -- The PK should not be updatable
	  order by ColumnId
	  
----------------------------------------------------------------------------------
-- END FOR EACH COLUMN
----------------------------------------------------------------------------------	  
  
	  
	  
	IF @StrictUserContext = 1 AND @BaseTableDDL = 1
	select @SQL = @SQL 
	
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET Created  = @AuditTime, ' + Char(13) + Char(10)
       + '         CreatedBy  = Suser_SName(), ' + Char(13) + Char(10)
       + '         Modified = @AuditTime, ' + Char(13) + Char(10)
       + '         ModifiedBy  = Suser_SName(), ' + Char(13) + Char(10)
       + '        [RowVersion] =  1' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	IF @StrictUserContext = 0 
	select @SQL = @SQL 
	
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET Created  = COALESCE(Inserted.Created, @AuditTime), ' + Char(13) + Char(10)
       + '         CreatedBy  = COALESCE(Inserted.CreatedBy, Suser_SName()), ' + Char(13) + Char(10)
       + '         Modified = COALESCE(Inserted.Modified, Inserted.Created, @AuditTime), ' + Char(13) + Char(10)
       + '         ModifiedBy  = COALESCE(Inserted.ModifiedBy, Inserted.CreatedBy, Suser_SName()), ' + Char(13) + Char(10)
       + '        [RowVersion] =  1' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_insert] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch '
EXEC (@SQL)

SET @SQL = '[' + @SchemaName + '].[' + @TableName + '_Audit_Insert]'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'INSERT';

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- build update trigger 

-- If a table has only an id column and both LogSQL and BaseTableDDL are false
-- the update trigger is superflous.
IF ((select count(*) from @columnData where ColumnName <> @PKColumnName) > 0) OR (@LogSQL = 1) OR (@BaseTableDDL = 1)
BEGIN -- Begin: Update Trigger

SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Update' + ' ON ['+ @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + ' AFTER Update' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' -- generated by AutoAudit Version ' + @Version + ' on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10)
       + ' -- autoaudit.codeplex.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' -- Options: ' + Char(13) + Char(10)
       + ' --   StrictUserContext : ' + CAST(@StrictUserContext as CHAR(1)) + Char(13) + Char(10)
       + ' --   LogSQL            : ' + CAST(@LogSQL as CHAR(1)) + Char(13) + Char(10)
       + ' --   BaseTableDDL      : ' + CAST(@BaseTableDDL as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
       + ' --   LogInsert         : ' + CAST(@LogInsert as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
       
       + 'DECLARE ' + Char(13) + Char(10)
       + '  @AuditTime DATETIME, ' + Char(13) + Char(10)
       + '  @IsDirty BIT' + Char(13) + Char(10)
       
       -- keep the variable initialization separate for SQL Server 2005
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'SET @IsDirty = 0' + Char(13) + Char(10) + Char(13) + Char(10)

       + '  Begin Try' + Char(13) + Char(10)
           
   	        /* -------------------------------------------------------------------------------
   	        -- enable this code to force a rollback of attempt to set user context when StrictUserContext is on
   	        IF @StrictUserContext = 1
              SELECT @SQL = @SQL
                   -- StrictUserContext so always set to system user function        
                 + '    SET @CreateUserName = SUser_SName()' + Char(13) + Char(10)
                 + '    SET @ModifyUserName = SUser_SName()' + Char(13) + Char(10) + Char(13) + Char(10)
                   -- StrictUserContext so updating audit column not permitted 
                 + '    IF @@NestLevel = 1 AND (UPDATE(Created) OR UPDATE(CreatedBy) OR UPDATE(Modified) OR UPDATE(ModifiedBy) OR UPDATE(RowVersion))' + Char(13) + Char(10)
                 + '      BEGIN ' + Char(13) + Char(10)
                 + '        RAISERROR(''Update of Created, Createdby, Modified, ModifiedBy, or RowVersion not permitted by AutoAudit when StrictUserContext is enabled.'', 16,1)' + Char(13) + Char(10)
                 + '        ROLLBACK' + Char(13) + Char(10)
                 + '      END ' + Char(13) + Char(10)   + Char(13) + Char(10)  
             */ -------------------------------------------------------------------------------
   
   IF @LogSQL = 1 
     BEGIN  
     	 select @SQL = @SQL
         + ' -- capture SQL Statement' + Char(13) + Char(10)
         + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
         + ' DECLARE @inputbuffer TABLE' + Char(13) + Char(10) 
         + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
         + ' SET @ExecStr = ''DBCC INPUTBUFFER(@@SPID) with no_infomsgs''' + Char(13) + Char(10)
         + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
         + '   EXEC (@ExecStr)' + Char(13) + Char(10)
         + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
         + Char(13) + Char(10) 
     END   
     
----------------------------------------------------------------------------------
-- BEGIN FOR EACH COLUMN
----------------------------------------------------------------------------------	  

	select @SQL = @SQL  

	        --    1.10b Cautious that the overuse of IF branching may cause recompiles or bad query execution plans
	        --    +  ' IF UPDATE([' + c.name + '])' + Char(13) + Char(10)     

         + '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, OldValue, NewValue '
         
         + CASE @BaseTableDDL  
             WHEN 1 THEN ', Rowversion) '
             ELSE ') ' -- gotta figure out a way to increment the RowVersion in the Audit table from the Audit table alone without killing perf !
           END  + Char(13) + Char(10)  

         + '     SELECT '

         -- StrictUserOption
   	     + CASE 
   	         WHEN @StrictUserContext = 1 THEN ' @AuditTime, SUSER_SNAME(),'
   	         ELSE 'COALESCE(Inserted.Modified, @AuditTime), COALESCE(Inserted.ModifiedBy, Suser_Sname()),'
           END

         + ' APP_NAME(), Host_Name(), ' 
         + '''' + @SchemaName + '.' + @TableName + ''', ''u'','  

         -- if @LogSQL is off then the @UserSQL variable has not been declared
         + CASE @LogSQL
             WHEN 1 THEN ' @UserSQL, '
             ELSE ' NULL, ' 
           END  
       
       -- 1.09 handle HierarchyID PK  
       + ' Convert(VARCHAR(50), Inserted.[' + @PKColumnName + ']),' + Char(13) + Char(10) 
       ----------------
       + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
       + '        NULL,     -- Secondary Row Value (e.g. Order Number for an Order Detail Line)' + Char(13) + Char(10)
       + '        ''[' + ColumnName + ']'',' 
         
       -- 1.09 changed from cast to convert to handle HierarchyID conversion 
       + ' Convert(VARCHAR(50), Deleted.[' + ColumnName + ']), ' 
       + ' Convert(VARCHAR(50), Inserted.[' + ColumnName + '])' + Char(13) + Char(10)
    
       + CASE @BaseTableDDL  
           WHEN 1 THEN ', deleted.Rowversion + 1'
           ELSE '' -- gotta figure out a way to increment the RowVersion in the Audit table !
         END  + Char(13) + Char(10)  
       
       + '          FROM Inserted' + Char(13) + Char(10)
       + '             JOIN Deleted' + Char(13) + Char(10)
       + '               ON Inserted.[' + @PKColumnName + '] = Deleted.[' + @PKColumnName + ']' + Char(13) + Char(10)
	+ '               AND ISNULL(Inserted.[' + ColumnName + '],' + NullValue + ') <> ISNULL(Deleted.[' + ColumnName + '],' + NullValue + ')'  + Char(13) + Char(10) 
       
       + '   IF @@RowCount > 0 SET @IsDirty = 1' + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	  from @columnData
        where IsDDLColumn = 0
           AND ColumnName <> @PKColumnName  -- The PK should not be updatable
	  order by ColumnId
	  
----------------------------------------------------------------------------------
-- END FOR EACH COLUMN
----------------------------------------------------------------------------------	  

-- uniqueidentifier ??

  -- Update the created, createdby, modified, modifiedby columns 
	IF @StrictUserContext = 1 AND @BaseTableDDL = 1
	SELECT @SQL = @SQL 
       -- force the created and createdby to stay the same as it was (in the deleted table) 
       -- set the modified and modifiedby to the current @AuditTime and Suser_Sname()
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
 	     + ' IF @IsDirty = 1 AND @@NestLevel = 1' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET Created  = Deleted.Created, ' + Char(13) + Char(10)
       + '         CreatedBy = Deleted.CreatedBy, ' + Char(13) + Char(10)
       + '         Modified = @AuditTime, ' + Char(13) + Char(10)
       + '         ModifiedBy  = SUser_SName(), ' + Char(13) + Char(10)
       + '        [RowVersion] = [' + @TableName + '].[RowVersion] + 1 ' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']' + Char(13) + Char(10)
       + '       JOIN Deleted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Deleted.[' + @PKColumnName + ']'  +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	IF @StrictUserContext = 0  -- (For StrictUserContext to be set to 0, AutoAudit requires BaseTableDDL set to 1)
	SELECT @SQL = @SQL 
	     -- allow the DML to set the created, createdby columns, but default to no change
	     -- allow the DML to set the modified, modifiedby column, but default to @audittime and Suser_Sname()
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
 	     + ' IF @@NestLevel = 1' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
    --   + '     SET Created  = COALESCE(Inserted.Created, Deleted.Created), ' + Char(13) + Char(10)
    --   + '         CreatedBy = COALESCE(Inserted.Createdby, Deleted.Createdby), ' + Char(13) + Char(10)
       + '     SET Modified = COALESCE(Inserted.Modified, @AuditTime), ' + Char(13) + Char(10)
       + '         ModifiedBy  = COALESCE(Inserted.ModifiedBy, Suser_Sname()) ' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       + '       JOIN Deleted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Deleted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
       
 	IF @StrictUserContext = 0  -- (For StrictUserContext to be set to 0, AutoAudit requires BaseTableDDL set to 1)
	SELECT @SQL = @SQL 
	     -- only update RowVersion if a user column is dirty (not just modified or modifiedby - for pre-delete touch)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
 	     + ' IF @IsDirty = 1 AND @@NestLevel = 1' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET  [RowVersion] = [' + @TableName + '].[RowVersion] + 1 ' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       + '       JOIN Deleted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Deleted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_update] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch ' 

EXEC (@SQL)

SET @SQL = '[' + @SchemaName + '].[' + @TableName + '_Audit_Update]'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'UPDATE';
  
END -- End: Update Trigger

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- build delete trigger 
SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Delete' + ' ON ['+ @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + ' AFTER Delete' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' -- generated by AutoAudit Version ' + @Version + ' on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10)
       + ' -- autoaudit.codeplex.com ' + Char(13) + Char(10) + Char(13) + Char(10)

       + ' -- Options: ' + Char(13) + Char(10)
       + ' --   StrictUserContext : ' + CAST(@StrictUserContext as CHAR(1)) + Char(13) + Char(10)
       + ' --   LogSQL            : ' + CAST(@LogSQL as CHAR(1)) + Char(13) + Char(10)
       + ' --   BaseTableDDL      : ' + CAST(@BaseTableDDL as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
       + ' --   LogInsert         : ' + CAST(@LogInsert as CHAR(1)) + Char(13) + Char(10) + Char(13) + Char(10)
      
       + 'DECLARE @AuditTime DATETIME' + Char(13) + Char(10)
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' Begin Try ' + Char(13) + Char(10)
       
   IF @LogSQL = 1 
     BEGIN  
     	 select @SQL = @SQL
         + ' -- capture SQL Statement' + Char(13) + Char(10)
         + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
         + ' DECLARE  @inputbuffer TABLE' + Char(13) + Char(10) 
         + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
         + ' SET @ExecStr = ''DBCC INPUTBUFFER(@@SPID) with no_infomsgs''' + Char(13) + Char(10)
         + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
         + '   EXEC (@ExecStr)' + Char(13) + Char(10)
         + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
         + Char(13) + Char(10) 
     END   

-- for each column
	select @SQL = @SQL + 
		     '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, OldValue, Rowversion)' + Char(13) + Char(10)
       + '   SELECT '
         		      
		      -- StrictUserOption
		      + CASE @StrictUserContext
		          WHEN 0 -- allow DML setting of created/modified user and datetimes
		            THEN ' COALESCE(Deleted.Modified, @AuditTime), COALESCE(Deleted.ModifiedBy, Suser_SName()),'
		          ELSE -- block DML setting of user context 
		             ' @AuditTime, Suser_SName(),'
		        END 
		   
          + ' APP_NAME(), Host_Name(), ' 
          + '''' + @SchemaName + '.' + @TableName + ''', ''d'','
          
          -- if @LogSQL is off then the @UserSQL variable has not been declared
          + CASE @LogSQL
             WHEN 1 THEN ' @UserSQL, '
             ELSE ' NULL, ' 
           END  
       
          + ' deleted.[' + @PKColumnName + '],' + Char(13) + Char(10) 
          + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
          + '        NULL,     -- Secondary Row Value (e.g. Oder Number for an Order Detail Line)' + Char(13) + Char(10)
          + '        ''[' + ColumnName + ']'','   

          + ' Convert(VARCHAR(50), Deleted.[' + ColumnName + ']), ' 
  
         + CASE @BaseTableDDL  
             WHEN 1 THEN ' deleted.Rowversion '
             ELSE ' 0'
           END   
             
          + '          FROM deleted' + Char(13) + Char(10)
          + '          WHERE deleted.['+ ColumnName + '] is not null' + Char(13) + Char(10)+ Char(13) + Char(10)
	  from @columnData
	  order by ColumnId

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_delete] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch ' 

EXEC (@SQL)

SET @SQL = '[' + @SchemaName + '].[' + @TableName + '_Audit_Delete]'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'DELETE';

--------------------------------------------------------------------------------------------
-- build _Deleted view

/* Sample: 
CREATE VIEW Production.vCulture_Deleted
as
	SELECT 
		Max(Case ColumnName WHEN '[CultureID]' THEN OldValue ELSE '' END) AS [CultureID],
		Max(Case ColumnName WHEN '[Name]' THEN OldValue ELSE '' END) AS [Name],
		Max(Case ColumnName WHEN '[Created]' THEN OldValue ELSE '' END) AS [Created],
		Max(Case ColumnName WHEN '[Modified]' THEN OldValue ELSE '' END) AS Modified,
		Max(Case ColumnName WHEN '[Rowversion]' THEN OldValue ELSE '' END) AS [Rowversion],
        MAX(AuditDate) AS 'Deleted'
	FROM Audit 
	Where TableName = 'Production.Culture' AND Operation = 'd'
	GROUP BY PrimaryKey 
*/

SET @SQL = 'CREATE VIEW ' + @SchemaName + '.v' + @TableName + '_Deleted' + Char(13) + Char(10)
       + 'AS ' + Char(13) + Char(10) 
       + ' -- generated by AutoAudit Version ' + @Version + ' on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10)
       + ' -- autoaudit.codeplex.com ' + Char(13) + Char(10) + Char(13) + Char(10)

       + 'SELECT ' + Char(13) + Char(10)

-- for each column
SELECT @SQL = @SQL +
		  '     Max(Case ColumnName WHEN ''[' + ColumnName + ']'' THEN OldValue ELSE '''' END) AS [' + ColumnName +'],'  + Char(13) + Char(10)
	  from @columnData
	  order by ColumnId

SET @SQL = @SQL
        + '      MAX(AuditDate) AS ''Deleted'''  + Char(13) + Char(10)
	    + '  FROM dbo.Audit'   + Char(13) + Char(10)
	    + '  Where TableName = ''' +@SchemaName + '.' + @TableName + ''' AND Operation = ''d'''  + Char(13) + Char(10)
	    + '  GROUP BY PrimaryKey' 

EXEC (@SQL)

--------------------------------------------------------------------------------------------
-- build _RowHistory Table-Valued UDF

/* sample:
CREATE FUNCTION HumanResources.Department_RowHistory (@PK INT) 
RETURNS TABLE
RETURN
(
-- Initial Row Values
Select '1999-01-01' as AuditDate, --Created
  'i' as Operation, 
    -- Name 
    (SELECT Coalesce( 
        (Select top 1 OldValue 
          FROM dbo.Audit 
          Where TableName = 'HumanResources.Department' 
            and PrimaryKey = @PK
            and Operation = 'u' 
            and ColumnName = '[Name]'
          order by AuditID),
      Name)
    FROM HumanResources.Department
      WHERE DepartmentID = @PK
  ) as Name,
  
    -- GroupName 
   ( SELECT Coalesce( 
        (Select top 1 OldValue 
          FROM dbo.Audit 
          Where TableName = 'HumanResources.Department' 
            and PrimaryKey = @PK 
            and Operation = 'u' 
            and ColumnName = '[GroupName]'
          order by AuditID),
      GroupName)
    FROM HumanResources.Department
      WHERE DepartmentID = @PK
  ) as GroupName
  FROM HumanResources.Department
  where DepartmentID = @PK

UNION ALL

SELECT AuditDate, 'u'
     ,Max(Case ColumnName WHEN '[Name]' THEN NewValue ELSE '' END) AS [Name]
     ,Max(Case ColumnName WHEN '[GroupName]' THEN NewValue ELSE '' END) AS [GroupName]
  FROM Audit
  Where TableName = 'HumanResources.Department' 
    AND PrimaryKey = @PK
    and Operation = 'u' 
  GROUP BY PrimaryKey, AuditDate
);

*/

SET @SQL = 'CREATE FUNCTION ' + @SchemaName + '.' + @TableName + '_RowHistory (@PK INT)' + Char(13) + Char(10)
       + 'RETURNS TABLE ' + Char(13) + Char(10) 
       + ' -- generated by AutoAudit Version ' + @Version + ' on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10)
       + ' -- autoaudit.codeplex.com ' + Char(13) + Char(10) + Char(13) + Char(10)

       + 'RETURN ' + Char(13) + Char(10)
       + '( ' + Char(13) + Char(10)
       + '-- Initial Row Values ' + Char(13) + Char(10)
       + 'Select '
       
       IF @BaseTableDDL = 1 
         SET @SQL = @SQL + ' Created as AuditDate '
       ELSE 
         BEGIN 
           SET @SQL = @SQL
            + '    (Select AuditDate' + Char(13) + Char(10)
            + '       FROM dbo.Audit' + Char(13) + Char(10)
            + '       WHERE TableName = ''' + @SchemaName + '.' + @TableName + '''' + Char(13) + Char(10)
            + '         and PrimaryKey = @PK' + Char(13) + Char(10)
            + '         and Operation = ''i'''  + Char(13) + Char(10)
            + '     ) as AuditDate' + Char(13) + Char(10)  + Char(13) + Char(10)
         END   
       
      SET @SQL = @SQL + ', ''i'' as Operation, 1 as RowVersion' 

-- for each column
SELECT @SQL = @SQL 
      + ', ' + Char(13) + Char(10)
      + '-- ' + ColumnName + Char(13) + Char(10)
      + '(SELECT Coalesce(' + Char(13) + Char(10)
      + '    (Select top 1 OldValue' + Char(13) + Char(10)
      + '       FROM dbo.Audit' + Char(13) + Char(10)
      + '       WHERE TableName = ''' + @SchemaName + '.' + @TableName + '''' + Char(13) + Char(10)
      + '         and PrimaryKey = @PK' + Char(13) + Char(10)
      + '         and Operation = ''u'''  + Char(13) + Char(10)
      + '         and ColumnName = ''[' + ColumnName + ']''' + Char(13) + Char(10)
      + '       ORDER BY AuditID),' + Char(13) + Char(10)
      + '     [' + ColumnName + '])' + Char(13) + Char(10)
      + '  FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
      + '  WHERE [' + @PKColumnName + '] = @PK '  + Char(13) + Char(10)
      + ' ) as [' + ColumnName + ']' + Char(13) + Char(10)
	  from @columnData
      where IsDDLColumn = 0
	  order by ColumnId

SELECT @SQL = @SQL 
      + '  FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
      + '    WHERE [' + @PKColumnName +  '] = @PK )' + Char(13) + Char(10) + Char(13) + Char(10)
      
      + 'UNION ALL' + Char(13) + Char(10) + Char(13) + Char(10)
      
 -- Updated values from Audit Trail     
      + 'SELECT AuditDate, ''u'', RowVersion' + Char(13) + Char(10)

-- for each column
SELECT @SQL = @SQL 
      + '     ,Max(Case ColumnName WHEN ''[' + ColumnName + ']'' THEN NewValue ELSE null END) AS [' + ColumnName + ']' + Char(13) + Char(10)
	  from @columnData
      where IsDDLColumn = 0
	  order by ColumnId

SELECT @SQL = @SQL 
      + '  FROM dbo.Audit'  + Char(13) + Char(10)
      + '    Where TableName = '''  +@SchemaName + '.' + @TableName + ''''   + Char(13) + Char(10)
      + '      AND PrimaryKey = @PK' + Char(13) + Char(10)
      + '      and Operation = ''u'''  + Char(13) + Char(10)
      + '    GROUP BY PrimaryKey, AuditDate, RowVersion'  + Char(13) + Char(10)
     -- + ' ORDER BY RowVersion'
     
EXEC (@SQL)

RETURN -- END OF SPROC

go