Est-il possible d'obtenir la pile d'appels d'exécution dans un déclencheur?

16

J'ai 10 procédures stockées et chacune d'elles insère dans une tableX.

Est-il possible dans un corps déclencheur de tableX d'obtenir quel objet provoque la modification de tableX (stocké proc1 ou sp2 ou ....)?

Je vous remercie.

garik
la source

Réponses:

9

Oui, il est possible d'identifier le code en cours, en utilisant la fonction système @@ procid , et mieux OBJECT_NAME (@@ PROCID) pour avoir le nom complet.

Définition: "Renvoie l'identificateur d'objet (ID) du module Transact-SQL actuel. Un module Transact-SQL peut être une procédure stockée, une fonction définie par l'utilisateur ou un déclencheur. @@ PROCID ne peut pas être spécifié dans les modules CLR ou traiter le fournisseur d'accès aux données. "

Vous pouvez en lire plus ici .

Une autre option serait de vérifier le plan sql du spid actuel et d'enregistrer ces informations dans une table de journalisation. Un exemple de requête à utiliser dans chaque procédure pour enregistrer les données d'audit serait:

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

Peut-être qu'il y a trop de détails là-bas ... mais je crois que vous avez compris l'idée.

Une troisième option serait d' utiliser les informations context_info pour la session actuelle du SP. Et associez quelque part les informations de contexte qui y sont enregistrées à chaque procédure. Par exemple, dans procedure1, vous écrivez 111 dans le contexte, dans procedure2, vous écrivez 222 .. et ainsi de suite.

Vous pouvez lire beaucoup plus d'informations sur context_info dans cette question SO .

Marian
la source
1
1) OBJECT_NAME (@@ PROCID) dans le déclencheur renvoie le nom du déclencheur :(. 2) il est nécessaire d'avoir des informations juste au déclencheur. 3) context_info est une solution. Merci.
garik
1
Oui, à l'intérieur d'un déclencheur OBJECT_NAME(@@PROCID)renvoie le nom du déclencheur, pas le proc appelant.
ProfK
Ceci est tout simplement faux. Il renvoie le nom du déclencheur, et non de la procédure d'appel comme l'OP l'a demandé
Reversed Engineer
D'accord, la réponse est fausse. CONTEXT_INFO fonctionne si vous pouvez modifier la procédure en amont.
Tom Warfield
3

Je voulais aussi faire ça. Merci d'avoir répondu. Comme je suis toujours là, je posterai mon test pour gagner du temps aux autres :)

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
Jim Brown
la source
2

XEvents fournit un autre moyen de faire connaître une pile T-SQL bien que SQL Server 2008 ne prenne pas en charge un type d'événement utilisé. La solution se compose d'un déclencheur, d'une erreur et d'une session XEvent. J'ai pris l'exemple de Jim Brown pour montrer comment cela fonctionne.

Tout d'abord, j'ai testé la solution pour SQL Server 2016 SP2CU2 Dev Edition. SQL Server 2008 prend en charge certains EXevent, mais je n'ai aucune instance et je n'ai donc pas pu le tester.

L'idée est de générer une erreur utilisateur dans un bloc try-catch factice, puis de capturer l'erreur dans une session XEvent avec tsql_stackaction. SQLSERVER.error_reportedLe type XEvent peut intercepter toutes les erreurs même si un bloc try-catch les intercepte. Au final, sys.dm_exec_sql_textextrayez les requêtes T-SQL des poignées de requête que tsql_stackdonne l'action.

Un exemple de la réponse de Jim Brown, que j'ai développée, est présenté ci-dessous. Un déclencheur déclenche l'erreur avec le texte «catch me». La session XEvent ne détecte les erreurs qu'avec le texte comme «attrape-moi».

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

Maintenant, si vous lancez la session XEvent (SSMS, Explorateur d'objets, Gestion, Événements étendus, Sessions, catch_insertion_into_Test), exécutez usp_RootProcIDTest et regardez le tampon en anneau de la session XEvent, vous devriez voir le XML qui constitue le nœud <action name="tsql_stack" package="sqlserver">. Il existe une séquence de nœuds de trame. Mettez les valeurs de handlel'attribut 's dans la fonction système' sys.dm_exec_sql_text ', et 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 exemple de pile d'appels d'exécution

XEvent vous permet de faire bien plus que cela! Ne manquez pas les occasions de les apprendre!

Basil Kisel
la source