February 4, 2011 at 12:22 pm
When I put this code into the trigger section:
ALTER TRIGGER [dbo].[UPDATE_EMAIL_IN_HEAT] ON [dbo].[CONTSUPP]
FOR DELETE
AS
DECLARE @ci VARBINARY(128) = CAST(DB_NAME() AS VARBINARY(128));
-- Check if anything has already set the CONTEXT_INFO and if not set it to my current database,
IF CONTEXT_INFO() IS NULL
SET CONTEXT_INFO @ci;
-- Check CONTEXT_INFO against my database and check the TRIGGER_NESTLEVEL
-- IF CONTEXT_INFO does not equal my database and TRIGGER_NESTLEVEL>1 just return
IF CONVERT(VARCHAR(50),CONTEXT_INFO()) <> DB_NAME() AND trigger_nestlevel () > 1 RETURN;
I get the messages:
Msg 139, Level 15, State 1, Procedure UPDATE_EMAIL_IN_HEAT, Line 6
Cannot assign a default value to a local variable.
Msg 195, Level 15, State 10, Procedure UPDATE_EMAIL_IN_HEAT, Line 10
'CONTEXT_INFO' is not a recognized function name.
Msg 137, Level 15, State 2, Procedure UPDATE_EMAIL_IN_HEAT, Line 11
Must declare the variable '@ci'.
Msg 195, Level 15, State 10, Procedure UPDATE_EMAIL_IN_HEAT, Line 16
'CONTEXT_INFO' is not a recognized function name.
and I cannot commit the alter statement.
Can you tell me what I need to change? It seems I am close to having something that will work but it is still not working. Thanks for your help!
February 4, 2011 at 12:48 pm
My mistake - I hadn't realised you were on an older version, SQL2000 didn't have CONTEXT_INFO() as a function.
You can still do this though:
Anywhere we used CONTEXT_INFO() replace with
(SELECT CONTEXT_INFO
FROM master.dbo.SYSPROCESSES
WHERE SPID = @@SPID )
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 4, 2011 at 12:49 pm
BTW: I just found this article on MSDN that gives a fuller discussion of this topic:
http://code.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=AuditOrBypassTriggerExecution
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 4, 2011 at 2:21 pm
I was able to get a working set of code with this entry:
ALTER TRIGGER [dbo].[UPDATE_EMAIL_IN_HEAT] ON [dbo].[CONTSUPP]
FOR DELETE
AS
DECLARE @ci VARBINARY(128)
--next line had to be a separate statement,
--you cannot set a default value for a local variable at least in SQL 2K
SET @ci = CAST(DB_NAME() AS VARBINARY(128));
-- Check if anything has already set the CONTEXT_INFO and if not set it to my current database,
IF (SELECT CONTEXT_INFO
FROM master.dbo.SYSPROCESSES
WHERE SPID = @@SPID ) is null
--rewrote this with pointer to system table
UPDATE master.dbo.SYSPROCESSES
set context_info = @ci
where SPID = @@SPID
-- Check CONTEXT_INFO against my database and check the TRIGGER_NESTLEVEL
-- IF CONTEXT_INFO does not equal my database and TRIGGER_NESTLEVEL>1 just return
IF CONVERT(VARCHAR(50),(SELECT CONTEXT_INFO
FROM master.dbo.SYSPROCESSES
WHERE SPID = @@SPID ) ) <> DB_NAME() AND trigger_nestlevel () > 1 RETURN;
/*
declare @linkid varchar(20)
declare @recid varchar(15)*/
However when I try to compile i get the error
Msg 259, Level 16, State 2, Procedure UPDATE_EMAIL_IN_HEAT, Line 14
Ad hoc updates to system catalogs are not enabled. The system administrator must reconfigure SQL Server to allow this.
From what I read in http://msdn.microsoft.com/en-us/library/aa196704(SQL.80).aspx and other more recent posts, ad hoc updates of system tables are generally not a good idea. Is there a way to make this work without the ad hoc update?
February 4, 2011 at 4:01 pm
Argh! No!
I said replace CONTEXT_INFO() with that, not CONTEXT_INFO. they are different!
ALTER TRIGGER [dbo].[UPDATE_EMAIL_IN_HEAT] ON [dbo].[CONTSUPP]
FOR DELETE
AS
DECLARE @ci VARBINARY(128)
--next line had to be a separate statement,
--you cannot set a default value for a local variable at least in SQL 2K
SET @ci = CAST(DB_NAME() AS VARBINARY(128));
-- Check if anything has already set the CONTEXT_INFO and if not set it to my current database,
IF (SELECT CONTEXT_INFO
FROM master.dbo.SYSPROCESSES
WHERE SPID = @@SPID ) is null
--rewrote this with pointer to system table
SET CONTEXT_INFO @ci;
-- Check CONTEXT_INFO against my database and check the TRIGGER_NESTLEVEL
-- IF CONTEXT_INFO does not equal my database and TRIGGER_NESTLEVEL>1 just return
IF CONVERT(VARCHAR(50),(SELECT CONTEXT_INFO
FROM master.dbo.SYSPROCESSES
WHERE SPID = @@SPID ) ) <> DB_NAME() AND trigger_nestlevel () > 1 RETURN;
/*
declare @linkid varchar(20)
declare @recid varchar(15)*/
From what I read in http://msdn.microsoft.com/en-us/library/aa196704(SQL.80).aspx and other more recent posts, ad hoc updates of system tables are generally not a good idea. Is there a way to make this work without the ad hoc update?
They are definitely not a good idea and you should not be doing it... I have amended the code above.
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 4, 2011 at 4:23 pm
This compiles OK but I confess that I don't really understand what it is doing. I appreciate your help - I will try this out, probably over the weekend, and see how it goes. Thanks again!
February 4, 2011 at 4:33 pm
Ok, let me try to explain.
CONTEXT_INFO is a place to store a value in your connected session specific to your SPID.
You can think of it like a note stuck on your computer screen.
You update a table in Database1 which has a trigger on it.
You look at the note on your screen (select context_info from sysprocesses where spid=@@spid) to see if you have already started work.
If the note is empty (null) you write on it "Database1" (SET CONTEXT_INFO @ci).
The trigger then tells you to switch to Database2 and delete a row from a table with another trigger on it.
After you delete the row in Database2 you look at the note on your screen (select context_info from sysprocesses where spid=@@spid) to see if you have already written on it.
You have! - it says "Database1", so you check that against your current database "Database2" and stop right there ( RETURN ) - ignoring any further code in the trigger in Database2.
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 7, 2011 at 2:13 pm
I tried the new code but unfortunately it did not work. I got a message in my application that 'the record could not be modified because it had been changed by another user' . The delete did not complete in the second database. I did not get any SQL specific messages with more clues.
Would you suggest I run a trace?
One thing I am still unclear on: I can see that context_info is a local session variable - a note as you described. And I can see how you would set it to the db name if it was null (meaning work had not yet begun to populate it. But then in the third statement, aren't you going back to sysprocesses to retrieve the value of context_info from there? I don't see where the local variable gets compared to the db name.
Please excuse my ignorance - I feel I am getting closer to this but still not there. Any further suggestions are welcome. Thank you.
February 7, 2011 at 5:00 pm
Ok, maybe CONTEXT_INFO is confusing the matter....
So, I have created a test script that creates two databases, two tables with two triggers and a third table to act as the control.
On a test system, run this script to see a working demo of the concept.
It drops everything right at the end, so if you want to examine the objects it creates, just remove the DROP DATABASE statements from the very end.
CREATE DATABASE [TrigTest1];
GO
USE [TrigTest1];
GO
/****** Object: Table [dbo].[testrig] Script Date: 02/07/2011 23:15:42 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[testrig1](
[id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[other] [varchar](10) NULL,
);
GO
SET ANSI_PADDING OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
--= This is the CONTEXT store.
CREATE TABLE [dbo].[TriggerContext] (spid int primary key not null,calling_db sysname not null);
GO
SET ANSI_PADDING OFF
GO
CREATE DATABASE [TrigTest2];
GO
USE [TrigTest2];
GO
/****** Object: Table [dbo].[testrig2] Script Date: 02/07/2011 23:15:42 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[testrig2](
[id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[other] [varchar](10) NULL,
);
GO
SET ANSI_PADDING OFF
GO
USE [TrigTest2];
GO
/****** Object: Trigger [dbo].[trig2] Script Date: 02/07/2011 23:06:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trig2] ON [dbo].[testrig2] AFTER DELETE AS
--= Check for an entry in the TriggerContext table for this SPID
DECLARE @calling_db sysname ;
SELECT @calling_db = calling_db FROM [TrigTest1].[dbo].[TriggerContext] WHERE spid=@@SPID ;
--= IF there was an entry and it is not for this database, just RETURN
IF @calling_db IS NOT NULL
AND DB_NAME()<>@calling_db RETURN ;
ELSE
--= If there was nothing in TriggerContext for this SPID, add something
INSERT [TrigTest1].[dbo].[TriggerContext](spid,calling_db) VALUES(@@SPID,db_name());
--= Perform normal trigger operations...here I perform a cross-database DELETE
DELETE FROM [TrigTest1].[dbo].[testrig1]
WHERE EXISTS(SELECT 1 FROM deleted WHERE deleted.id = [TrigTest1].[dbo].[testrig1].id) ;
--= Clean up the trigger context only where I created it.
IF (@calling_db IS NULL)
DELETE [TrigTest1].[dbo].[TriggerContext] WHERE spid=@@SPID ;
GO
USE [TrigTest1];
GO
/****** Object: Trigger [dbo].[trig1] Script Date: 02/07/2011 23:06:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trig1] ON [dbo].[testrig1] AFTER DELETE AS
--= Check for an entry in the TriggerContext table for this SPID
DECLARE @calling_db sysname ;
SELECT @calling_db = calling_db FROM [dbo].[TriggerContext] WHERE spid=@@SPID ;
--= IF there was an entry and it is not for this database, just RETURN
IF @calling_db IS NOT NULL
AND DB_NAME()<>@calling_db RETURN ;
ELSE
--= If there was nothing in TriggerContext for this SPID, add something
INSERT [dbo].[TriggerContext](spid,calling_db) VALUES(@@SPID,db_name());
--= Perform normal trigger operations...here I perform a cross-database DELETE
DELETE FROM [TrigTest2].[dbo].[testrig2]
WHERE EXISTS(SELECT 1 FROM deleted WHERE deleted.id = [TrigTest2].[dbo].[testrig2].id) ;
--= Clean up the trigger context only where I created it.
IF (@calling_db IS NULL)
DELETE [TrigTest1].[dbo].[TriggerContext] WHERE spid=@@SPID ;
GO
--= Create some test data
INSERT [TrigTest1].[dbo].[testrig1] (other) SELECT left(newid(),10) FROM (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) row(source) ;
INSERT [TrigTest2].[dbo].[testrig2] (other) SELECT left(newid(),10) FROM (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) row(source) ;
--= View the test data
SELECT id, other FROM [TrigTest1].[dbo].[testrig1] ORDER BY id ;
SELECT id, other FROM [TrigTest2].[dbo].[testrig2] ORDER BY id ;
GO
--= Test delete from database TrigTest1
DELETE [TrigTest1].[dbo].[testrig1] WHERE [id] = 2;
SELECT 'Delete ID 2 from database1',id,other from [TrigTest1].[dbo].[testrig1]
SELECT 'Trigger Deletes ID 2 from database2',id,other from [TrigTest2].[dbo].[testrig2]
GO
--= Test delete from database TrigTest2
DELETE [TrigTest2].[dbo].[testrig2] WHERE [id] = 4;
SELECT 'Delete ID 4 from database2',id,other from [TrigTest2].[dbo].[testrig2]
SELECT 'Trigger Deletes ID 4 from database1',id,other from [TrigTest1].[dbo].[testrig1]
GO
USE master;
GO
DROP DATABASE [TrigTest1];
GO
DROP DATABASE [TrigTest2];
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 8, 2011 at 9:03 am
Thanks - I have tried this and it seems to work exactly as intended!
Would you suggest I use the approach of creating the third table to act as the control? I had actually started down this road already and it would be fairly easy to rework the control table I have already created so it serves this purpose.
February 8, 2011 at 10:06 am
Yes, it is probably the best way to go.
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
February 8, 2011 at 11:55 am
Thanks again - much appreciated!
Viewing 12 posts - 16 through 26 (of 26 total)
You must be logged in to reply to this topic. Login to reply