Чи можливо отримати стек виклику виконання у тригері?


16

У мене є 10 збережених процедур, і кожна з них робить ВСТАВКИ в одну таблицюX.

Чи можливо в тригерному тілі tableX отримати те, що об'єкт викликає модифікацію tableX (зберігається proc1 або sp2 або ....)?

Дякую.

Відповіді:


9

Так, можна визначити запущений код, використовуючи системну функцію @@ і краще OBJECT_NAME (@@ PROCID), щоб мати повне ім'я.

Визначення: "Повертає ідентифікатор об'єкта (ID) поточного модуля Transact-SQL. Модуль Transact-SQL може бути збереженою процедурою, визначеною користувачем функцією або тригером. @@ PROCID не можна вказати в модулях CLR або в обробляти провайдер доступу до даних. "

Про це можна прочитати тут .

Іншим варіантом було б перевірити sql-план поточного spid та зберегти цю інформацію в таблиці реєстрації. Зразок запиту, який використовується в кожній процедурі для збереження даних аудиту, буде:

select sp.hostname, sp.program_name, sp.loginame,
    st.text as query_text
from sysprocesses sp
cross apply sys.dm_exec_sql_text(sp.sql_handle) as st  
where sp.spid = @@spid

Можливо, там є занадто багато деталей ... але я вважаю, що ви зрозуміли цю ідею.

Третім варіантом буде використання інформації контексту_інфо для поточного сеансу СП. І пов’язати десь інформацію про контекст, збережену там, з кожною процедурою. Наприклад, у процедурі1 ви пишете 111 у контекст, у процедурі2 ви пишете 222 .. і так далі.

Набагато більше інформації про контекстну інформацію ви можете прочитати в цьому питанні .


1
1) OBJECT_NAME (@@ PROCID) у тригері повертає ім'я тригера :(. 2) інформацію потрібно мати саме біля тригера. 3) контекст_інфо - це рішення. Спасибі.
garik

1
Так, всередині тригера OBJECT_NAME(@@PROCID)повертає ім'я тригера, а не прок.
ПрофК

Це просто неправильно. Він повертає назву тригера, а не процедури виклику, як просив ОП
інженер, що

Погодьтеся, відповідь неправильна. CONTEXT_INFO працює, якщо ви можете змінити процедуру вище.
Том

3

Я теж хотів це зробити. Дякую за відповідь. Оскільки я все ще тут, я опублікую свій тест, щоб заощадити час :)

CREATE TABLE  Test ( TestID INT )
GO

CREATE TRIGGER TestTrigger ON Test
FOR INSERT
AS

SELECT CAST(CONTEXT_INFO() AS NVARCHAR(128));
GO

CREATE PROCEDURE usp_ProcIDTest
AS

DECLARE @ProcedureName VARBINARY(MAX) = CAST(OBJECT_NAME(@@PROCID) AS VARBINARY(MAX))
SET CONTEXT_INFO @ProcedureName

INSERT INTO Test ( TestID ) VALUES ( 1 ) 

GO

EXEC usp_ProcIDTest
GO

DROP TABLE Test
GO

2

XEvents - це ще один спосіб отримати відомий стек T-SQL, хоча SQL Server 2008 може не підтримувати використовуваний тип події. Рішення складається з тригера, помилки та сеансу XEvent. Я взяв приклад Джима Брауна, щоб показати, як це працює.

Перш за все, я протестував рішення для SQL Server 2016 SP2CU2 Dev Edition. SQL Server 2008 підтримує деякий EXevent, але у мене немає жодного примірника, щоб я не міг його перевірити.

Ідея полягає в тому, щоб генерувати помилку користувача у фіксованому блоці спроб лову, а потім вловлювати помилку в сеансі XEvent з tsql_stackдією. SQLSERVER.error_reportedТип XEvent може вловлювати всі помилки, навіть якщо блок пробного захоплення захоплює їх. Зрештою, sys.dm_exec_sql_textвитягніть T-SQL запити з оброблюваних запитів, які tsql_stackдає дія.

Приклад з відповіді Джима Брауна, який я розробив, наведено нижче. Тригер викликає помилку з текстом "лови мене". Сеанс XEvent виявляє помилки лише з текстом на кшталт "зловити мене".

CREATE TABLE  Test ( TestID INT )

GO

CREATE TRIGGER TestTrigger ON Test
FOR INSERT
AS
BEGIN TRY
    SET XACT_ABORT OFF; -- REALLY IMPORTANT!
    /* make an catching a great deal more interesting */
    DECLARE @TestID NVARCHAR(MAX) ;
    SELECT TOP (1) @TestID = CAST(ins.TestID AS NVARCHAR(MAX)) FROM inserted AS ins ;
    RAISERROR (N'catch_me TestID = "%s"' , 11 , 0 , @TestID) ;
END TRY BEGIN CATCH /* NOTHING TO DO */ END CATCH

GO

CREATE PROCEDURE usp_ProcIDTest
AS
INSERT INTO Test ( TestID ) VALUES ( 1 ) 

GO

CREATE PROCEDURE usp_RootProcIDTest
AS
EXEC usp_ProcIDTest

GO

-- This XEvent session definition was kindly provided by XEvent 'New Session' wizard.
CREATE EVENT SESSION [catch_insertion_into_Test] ON SERVER 
ADD EVENT sqlserver.error_reported(
    ACTION(package0.callstack,sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_id,sqlserver.query_hash,sqlserver.query_plan_hash,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.tsql_frame,sqlserver.tsql_stack,sqlserver.username,sqlserver.context_info,sqlserver.plan_handle)
    WHERE ([message] like N'catch_me%'))
ADD TARGET package0.ring_buffer(SET max_memory=(10240))
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)

GO

Тепер, якщо ви запускаєте сеанс XEvent (SSMS, провідник об’єктів, управління, розширені події, сеанси, catch_insertion_into_Test), виконайте usp_RootProcIDTest і подивіться буфер дзвінка сеансу XEvent, ви повинні побачити XML, що складається з вузла <action name="tsql_stack" package="sqlserver">. Існує послідовність вузлів кадру. Помістіть значення handleатрибута 's у функцію системи' sys.dm_exec_sql_text 'та voilà:

-- REPLACE MY HANDLES WITH YOURS
SELECT * FROM sys.dm_exec_sql_text(0x03000800D153096910272C01A6AA000000000000000000000000000000000000000000000000000000000000);
SELECT * FROM sys.dm_exec_sql_text(0x030008000A78FD6912272C01A6AA000001000000000000000000000000000000000000000000000000000000);
SELECT * FROM sys.dm_exec_sql_text(0x03000800439CF16A13272C01A6AA000001000000000000000000000000000000000000000000000000000000);

Приклад стека виклику виконання

XEvent дозволить вам зробити набагато більше, ніж це! Не пропустіть можливості навчитися їх!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.