特定のストアドプロシージャが時々消えてしまう問題があり、どのスクリプトがそれを削除するのかを調べる必要があります。このストアドプロシージャの削除に関連するイベントを提供するこのコードを見つけました。
DECLARE @path NVARCHAR(260);
SELECT
@path = REVERSE(SUBSTRING(REVERSE([path]),
CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM sys.traces
WHERE is_default = 1;
SELECT
LoginName,
HostName,
StartTime,
ObjectName,
TextData
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass = 47 -- Object:Deleted
AND EventSubClass = 1
AND ObjectName like N'%usp_GetPendingConfiguration%'
ORDER BY StartTime DESC;
このストアドプロシージャを削除したストアドプロシージャまたはイベントを見つける方法はありますか?アドバイスをお願いします。
このストアドプロシージャを削除するクエリのDDLトリガーからSQLを取得しても、非常に役立ちます。クエリがストアドプロシージャのダイナミックSQLから、またはリリーススクリプトから、または統合テスト、アプリケーションコードなどからのものである場合、_DROP PROCEDURE ...
_のみをキャプチャする可能性があり、手掛かりはあまりありませんそれがどこから実行されているかについて。
ただし、これはDDLトリガーがこれを理解する方法ではないという意味ではありません。単にSQLをキャプチャしてソースを推測するのではなく、このアクションは望ましくないため(そして、削除されるストアドプロシージャを呼び出すコードが壊れる可能性があるため)、単に許可しないでください。 DDLトリガーを使用して_DROP PROCEDURE
_イベントをキャプチャし、EVENTDATA()
関数から返されたXMLを介して削除されているプロシージャを確認できます。削除されるストアドプロシージャが問題のストアドプロシージャである場合は、次の行に沿って何かを実行します。
_RAISERROR('Ah ha! Caught you red-handed (whatever that means). No DROP for you!', 16, 1);
ROLLBACK;
_
これを行う:
以下は、万が一に備えてイベントをログに記録する機能を含む、より完全な例です。これにより、少なくともどの人またはプロセスがこれを実行しているか、およびその頻度を洞察することができます。
_CREATE TRIGGER [PreventDropGetPendingConfiguration]
ON DATABASE
FOR DROP_PROCEDURE
AS
SET NOCOUNT ON;
IF (EVENTDATA().value(N'(EVENT_INSTANCE/ObjectName/text())[1]', 'sysname')
= N'usp_GetPendingConfiguration')
BEGIN
-- store values in variables as ROLLBACK will erase EVENTDATA()
DECLARE @EventTime DATETIME,
@LoginName sysname, -- lower-case for case-sensitive servers
@UserName sysname, -- lower-case for case-sensitive servers
@CommandText NVARCHAR(MAX),
@SPID INT;
DECLARE @InputBuffer TABLE
(
EventType NVARCHAR(30),
[Parameters] SMALLINT,
EventInfo NVARCHAR(4000)
);
SELECT @EventTime =
EVENTDATA().value(N'(EVENT_INSTANCE/PostTime/text())[1]', 'DATETIME'),
@LoginName =
EVENTDATA().value(N'(EVENT_INSTANCE/LoginName/text())[1]', 'sysname'),
@UserName =
EVENTDATA().value(N'(EVENT_INSTANCE/UserName/text())[1]', 'sysname'),
@CommandText =
EVENTDATA().value(N'(EVENT_INSTANCE/TSQLCommand/CommandText/text())[1]',
'NVARCHAR(MAX)'),
@SPID = EVENTDATA().value(N'(EVENT_INSTANCE/SPID/text())[1]', 'INT');
-- RollBack now else logging will also get Rolled Back ;-)
ROLLBACK;
IF (OBJECT_ID(N'dbo.LoggyLog') IS NULL)
BEGIN
CREATE TABLE dbo.LoggyLog
(
LoggyLogID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
EventTime DATETIME NOT NULL,
LoginName sysname, -- lower-case for case-sensitive servers
UserName sysname, -- lower-case for case-sensitive servers
CommandText NVARCHAR(MAX) NOT NULL,
SPID INT NOT NULL,
EventInfo NVARCHAR(4000) NULL
);
END;
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'DBCC INPUTBUFFER ( ' + CONVERT(NVARCHAR(10), @SPID)
+ N' ) WITH NO_INFOMSGS;';
INSERT INTO @InputBuffer (EventType, [Parameters], EventInfo)
EXEC(@SQL);
INSERT INTO dbo.LoggyLog (EventTime, LoginName, UserName, CommandText,
SPID, EventInfo)
SELECT @EventTime, @LoginName, @UserName, @CommandText, @SPID, tmp.EventInfo
FROM @InputBuffer tmp;
RAISERROR('Ah ha! Caught you red-handed (whatever that means **). No DROP for you!',
16, 1);
END;
GO
_
このストアドプロシージャを削除しようとすると、次のエラーが発生します。
メッセージ50000、レベル16、状態1、手順PreventDropProcedure、行7
ああハァッ!あなたが(それが何を意味するにせよ)手ぶらで捕まった。あなたのためのドロップはありません!メッセージ3609、レベル16、状態2、行1
トランザクションはトリガーで終了しました。バッチは中止されました。
_DBCC INPUTBUFFER
_の代わりに_sys.dm_exec_sql_text
_を使用した理由は、_sys.dm_exec_sql_text
_が実行中のcurrentクエリを返すためです。 _sys.dm_exec_sql_text
_がトリガー自体の中でネイティブにクエリされる場合、最終的に_CREATE TRIGGER...
_ステートメントになります。そのDMVがダイナミックSQLまたはサブストアドプロシージャコールでクエリされる場合、それらの特定のクエリが取得され、呼び出し元の_CREATE TRIGGER
_も取得されません。それはすべて役に立たない。
対照的に、_DBCC INPUTBUFFER
_は、チェーン内の最初のバッチ(現在のクエリだけではない)を報告します。少なくとも、これを使用して、DROP
呼び出しにつながる後続の呼び出しをいくつでも追跡できます。
また、これがときどきしか発生しないことを考えると、誰かが_CREATE PROCEDURE
_が呼び出される直前に_DROP PROCEDURE
_を呼び出していたリリーススクリプトでGO
を忘れて、誤って作成されたストアドプロシージャのDROP
クエリ部分(通常、_GRANT EXECUTE
_ステートメントに続くため、これは_CREATE PROCEDURE
_ステートメントで頻繁に発生します)。これは、リリーススクリプトに次のようなものが含まれているために発生する可能性があります。
_CREATE PROCEDURE dbo.ProcName
AS
...
-- missing GO !!!!
IF (OBJECT_ID(N'dbo.ProcGettingDropped') IS NOT NULL)
BEGIN
DROP PROCEDURE dbo.ProcGettingDropped;
END;
GO -- this GO terminates the CREATE PROCEDURE statement
_
次のクエリを実行することで、削除されるストアドプロシージャを含むデータベースでこれが発生するかどうかを検索できます。
_SELECT OBJECT_NAME([object_id]) AS [ObjectName], *
FROM sys.sql_modules
WHERE [definition] LIKE N'%DROP%';
_
** 赤利きの語源 (@MartinSmithに感謝)