Setting up object locking
Published 16 July 2015
About the setup script
The script adds the trigger RG_SQLSourceControl_DDLTrigger to the server. The trigger detects when somebody tries to edit a locked object.
The script also creates the database Redgate with the schema SQLSourceControl. This contains:
- the table SQLSourceControl.LockedObjects, to store locks on objects
- the function SQLSourceControl.GetLockedObjects, to retrieve locked objects
- the function SQLSourceControl.IsObjectLocked, to check if objects are locked
- the stored procedure SQLSourceControl.LockObject, to lock objects
- the stored procedure SQLSourceControl.UnlockObject, to unlock objects
To view the script, click Expand source:
Object locking setup script
-- This script sets up object locking on the server. After you run it, you can lock and unlock objects in all databases on the server. -- Before you run the script, close any other query windows that use the RedGate database. -- Object locking should only be used for your development environments, and not your production server. -- For more information about object locking, including a summary of what this script does, see http://www.red-gate.com/SOC-object-locking --////////////////////////////////////// USE master IF DB_ID(N'RedGate') IS NULL CREATE DATABASE RedGate ALTER DATABASE RedGate SET RECOVERY SIMPLE ALTER DATABASE RedGate SET ALLOW_SNAPSHOT_ISOLATION ON ALTER DATABASE RedGate SET READ_COMMITTED_SNAPSHOT ON GO USE [RedGate] --////////////////////////////////////// --// SQLSourceControl Schema --////////////////////////////////////// IF (SCHEMA_ID('SQLSourceControl') IS NULL) EXEC sp_executesql N'CREATE SCHEMA [SQLSourceControl] AUTHORIZATION [dbo]' GO --////////////////////////////////////// --// DDL Trigger --////////////////////////////////////// IF EXISTS (SELECT 1 FROM sys.server_triggers WHERE name = 'RG_SQLSourceControl_DDLTrigger') DROP TRIGGER RG_SQLSourceControl_DDLTrigger ON ALL SERVER GO CREATE TRIGGER [RG_SQLSourceControl_DDLTrigger] ON ALL SERVER WITH EXECUTE AS SELF FOR DDL_DATABASE_LEVEL_EVENTS AS SET NOCOUNT ON DECLARE @event_type NVARCHAR(MAX) SET @event_type = EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(MAX)') DECLARE @db_name NVARCHAR(MAX) SET @db_name = EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(MAX)') DECLARE @schema_name NVARCHAR(MAX) SET @schema_name = EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(MAX)') DECLARE @object_name NVARCHAR(MAX) SET @object_name = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(MAX)') DECLARE @login_name NVARCHAR(MAX) SET @login_name = EVENTDATA().value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(MAX)') IF OBJECT_ID('[RedGate].[SQLSourceControl].[LockedObjects]') IS NOT NULL BEGIN IF EXISTS (SELECT 1 FROM [RedGate].[SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name AND LO.[login_name] != @login_name) BEGIN DECLARE @locker NVARCHAR(MAX) DECLARE @reason NVARCHAR(MAX) SELECT @locker = [login_name] FROM [RedGate].[SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name DECLARE @NewLine AS CHAR(2) = CHAR(13) + CHAR(10) DECLARE @message NVARCHAR(MAX) SET @message = @NewLine + 'SQL Source Control' + @NewLine + @NewLine + 'The object [' + @db_name + '].[' + @schema_name + '].[' + @object_name + '] is locked by ' + @locker + @NewLine + @NewLine + 'To unlock the object, in SQL Source Control, go to the object locking tab. If you don''t have SQL Source Control, talk to the user who locked it.' + @NewLine + @NewLine + 'For more information about object locking, see www.red-gate.com/SOC-object-locking' + @NewLine RAISERROR (@message, 16, 1) ROLLBACK END END GO --////////////////////////////////////// --// Locked Objects Table --////////////////////////////////////// IF OBJECT_ID('[SQLSourceControl].[LockedObjects]') IS NOT NULL DROP TABLE [SQLSourceControl].[LockedObjects] GO CREATE TABLE [SQLSourceControl].[LockedObjects] ( [db_name] [NVARCHAR](128) NOT NULL, [schema_name] [NVARCHAR](128) NOT NULL, [object_name] [NVARCHAR](128) NOT NULL, [object_type] [NVARCHAR](128) NOT NULL, [login_name] [NVARCHAR](MAX) NOT NULL, [time_locked] [DATETIMEOFFSET] NOT NULL ) GO ALTER TABLE [SQLSourceControl].[LockedObjects] ADD CONSTRAINT [PK_LockedObjects] PRIMARY KEY CLUSTERED ( [db_name] ASC, [schema_name] ASC, [object_name] ASC ) GO --////////////////////////////////////// --// LockObject Stored Procedure --////////////////////////////////////// IF OBJECT_ID('[SQLSourceControl].[LockObject]') IS NOT NULL DROP PROCEDURE [SQLSourceControl].[LockObject] GO CREATE PROCEDURE [SQLSourceControl].[LockObject] @db_name AS NVARCHAR(128), @schema_name AS NVARCHAR(128), @object_name AS NVARCHAR(128), @object_type AS NVARCHAR(128), @login_name AS NVARCHAR(MAX) = NULL AS IF @login_name IS NULL SET @login_name = SUSER_SNAME() IF EXISTS (SELECT 1 FROM [SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name) BEGIN DECLARE @existing_locker NVARCHAR(MAX) SELECT @existing_locker = [login_name] FROM [SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name IF @existing_locker != @login_name BEGIN DECLARE @message NVARCHAR(MAX) SET @message = 'Cannot lock the object [' + @db_name + '].[' + @schema_name + '].[' + @object_name + '] ' + 'as it is already locked by ''' + @existing_locker RAISERROR (@message, 16, 1) END END ELSE BEGIN INSERT [SQLSourceControl].[LockedObjects] ([db_name], [schema_name], [object_name], [object_type], [login_name], [time_locked]) VALUES (@db_name, @schema_name, @object_name, @object_type, @login_name, SYSDATETIMEOFFSET()) END GO --////////////////////////////////////// --// UnlockObject Stored Procedure --////////////////////////////////////// IF OBJECT_ID('[SQLSourceControl].[UnlockObject]') IS NOT NULL DROP PROCEDURE [SQLSourceControl].[UnlockObject] GO CREATE PROCEDURE [SQLSourceControl].[UnlockObject] @db_name AS NVARCHAR(128), @schema_name AS NVARCHAR(128), @object_name AS NVARCHAR(128) AS IF EXISTS (SELECT 1 FROM [SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name) BEGIN DELETE FROM [SQLSourceControl].[LockedObjects] WHERE [db_name] = @db_name AND [schema_name] = @schema_name AND [object_name] = @object_name END GO --////////////////////////////////////// --// GetLockedObjects Function --////////////////////////////////////// IF OBJECT_ID('[SQLSourceControl].[GetLockedObjects]') IS NOT NULL DROP FUNCTION [SQLSourceControl].[GetLockedObjects] GO CREATE FUNCTION [SQLSourceControl].[GetLockedObjects] () RETURNS TABLE AS RETURN ( SELECT [db_name], [schema_name], [object_name], [object_type], [login_name], [time_locked] FROM [SQLSourceControl].[LockedObjects] ) GO --////////////////////////////////////// --// IsObjectLocked Function --////////////////////////////////////// IF OBJECT_ID('[SQLSourceControl].[IsObjectLocked]') IS NOT NULL DROP FUNCTION [SQLSourceControl].[IsObjectLocked] GO CREATE FUNCTION [SQLSourceControl].[IsObjectLocked] ( @db_name AS NVARCHAR(128), @schema_name AS NVARCHAR(128), @object_name AS NVARCHAR(128) ) RETURNS BIT AS BEGIN IF EXISTS (SELECT 1 FROM [SQLSourceControl].[LockedObjects] AS LO WHERE LO.[db_name] = @db_name AND LO.[schema_name] = @schema_name AND LO.[object_name] = @object_name) RETURN 1 RETURN 0 END GO
Required privileges
CREATE USER
CREATE ROLE
GRANT ANY PRIVILEGE
CREATE ANY TABLE
CREATE ANY PROCEDURE
You don't need the privileges to lock and unlock objects.
To set up object locking
- In the Lock objects tab, click Open setup script.
- Click Run script.
The script opens in a new Management Studio query window. - Close any other query windows that use the RedGate database.
- Run the script.
Object locking is set up for the server.