È possibile ottenere uno stack di chiamate di esecuzione in un trigger?


16

Ho 10 procedure memorizzate e ognuna di esse inserisce INSERT in una tabellaX.

È possibile in un corpo trigger di tableX ottenere quale oggetto provoca la modifica di tableX (memorizzato proc1 o sp2 o ....)?

Grazie.

Risposte:


9

Sì, è possibile identificare il codice in esecuzione, utilizzando la funzione di sistema @@ procid e OBJECT_NAME migliore (@@ PROCID) per avere il nome completo.

Definizione: "Restituisce l'identificatore di oggetto (ID) dell'attuale modulo Transact-SQL. Un modulo Transact-SQL può essere una procedura memorizzata, una funzione definita dall'utente o un trigger. @@ PROCID non può essere specificato nei moduli CLR o in fornitore di accesso ai dati di processo ".

Puoi leggerlo qui .

Un'altra opzione sarebbe quella di controllare il piano sql dello spid corrente e salvare tali informazioni in una tabella di registrazione. Una query di esempio da utilizzare in ciascuna procedura per salvare i dati di controllo sarebbe:

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

Forse ci sono troppi dettagli lì..ma credo che tu abbia avuto l'idea.

Una terza opzione sarebbe quella di utilizzare le informazioni context_info per la sessione del SP corrente. E associare da qualche parte le informazioni di contesto salvate lì con ogni procedura. Ad esempio nella procedura1 si scrive 111 nel contesto, nella procedura2 si scrive 222 .. e così via.

Molte altre informazioni su context_info sono disponibili in questa domanda SO .


1
1) OBJECT_NAME (@@ PROCID) nel trigger restituisce il nome del trigger :(. 2) è necessario disporre di informazioni solo sul trigger. 3) context_info è una soluzione. Grazie.
Garik,

1
Sì, all'interno di un trigger OBJECT_NAME(@@PROCID)restituisce il nome del trigger, non il proc chiamante.
ProfK

Questo è semplicemente sbagliato. Restituisce il nome del trigger, non della procedura di chiamata come richiesto dall'OP
Ingegnere invertito

D'accordo, la risposta è sbagliata. CONTEXT_INFO funziona se è possibile modificare la procedura a monte.
Tom Warfield,

3

Volevo fare anche questo. Grazie per la risposta. Dato che sono ancora qui, posterò il mio test per risparmiare tempo agli altri :)

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 offre un altro modo per far conoscere uno stack T-SQL sebbene SQL Server 2008 potrebbe non supportare un tipo di evento utilizzato. La soluzione consiste in un trigger, un errore e una sessione XEvent. Ho preso l'esempio di Jim Brown per mostrare come funziona.

Prima di tutto, ho testato la soluzione per SQL Server 2016 SP2CU2 Dev Edition. SQL Server 2008 supporta alcuni EXevent, ma non ho alcuna istanza in modo da non poterlo testare.

L'idea è di generare un errore utente in un fittizio blocco try-catch, quindi rilevare l'errore all'interno di una sessione XEvent con tsql_stackazione. SQLSERVER.error_reportedIl tipo XEvent può rilevare tutti gli errori anche se un blocco try-catch li intercetta. Alla fine, sys.dm_exec_sql_textestrarre le query T-SQL dagli handle di query che tsql_stackazione fornisce.

Un esempio della risposta di Jim Brown, che ho sviluppato, è mostrato di seguito. Un trigger genera l'errore con il testo "catch me". La sessione XEvent rileva errori solo con il testo come "catch me".

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

Ora, se avvii la sessione XEvent (SSMS, Esplora oggetti, Gestione, Eventi estesi, Sessioni, catch_insertion_into_Test), esegui usp_RootProcIDTest e guardi il buffer dell'anello della sessione XEvent, dovresti vedere l'XML che costituisce il nodo <action name="tsql_stack" package="sqlserver">. Esiste una sequenza di nodi di frame. Inserisci i valori dell'attributo handle's nella funzione di sistema' sys.dm_exec_sql_text 'e 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);

Un esempio di stack di chiamate di esecuzione

XEvent ti consente di fare molto di più! Non perdere le occasioni per impararli!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.