언제 무슨 뜻이야, "트리거를 사용하지 않고"라고 어떤 트리거를하거나 행 단위 트리거 테이블에?
이 때문에 부탁 할 수 는 사리 분별을 이용하여 원하는 것을 얻을 수 CONTEXT_INFO()
기능,하지만 당신은 확인해야합니다 SET CONTEXT_INFO
귀하의 작업이 수행 정확하게 전에 호출했다.
이를 위해 서버 수준 로그온 트리거 (예 : 데이터베이스 / 개체 수준 트리거가 아님)가있을 수 있습니다.
USE master
GO
CREATE TRIGGER tr_audit_login
ON ALL SERVER
WITH EXECUTE AS 'sa'
AFTER LOGON
AS BEGIN
BEGIN TRY
DECLARE @eventdata XML = EVENTDATA();
IF @eventdata IS NOT NULL BEGIN
DECLARE @spid INT;
DECLARE @client_host VARCHAR(64);
SET @client_host = @eventdata.value('(/EVENT_INSTANCE/ClientHost)[1]', 'VARCHAR(64)');
SET @spid = @eventdata.value('(/EVENT_INSTANCE/SPID)[1]', 'INT');
-- pack the required data into the context data binary
-- (spid is just an example of packing multiple data items in a single field: you would probably use @@SPID at the point of use, instead)
DECLARE @context_data VARBINARY(128);
SET @context_data = CONVERT(VARBINARY(4), @spid)
+ CONVERT(VARBINARY(64), @client_host);
-- persist the spid and host into session-level memory
SET CONTEXT_INFO @context_data;
END
END TRY
BEGIN CATCH
/* do better error handling here...
* logon trigger can lock all users out of server, so i am just swallowing everything
*/
DECLARE @msg NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR('%s', 10, 1, @msg) WITH LOG;
END CATCH
END
그런 다음 컨텍스트를 저장하기 위해 테이블에 기본 제한 조건을 추가 할 수 있습니다 (삽입 속도).
ALTER TABLE cdc.schema_table_CT
ADD ContextInfo varbinary(128) NULL DEFAULT(CONTEXT_INFO())
일단 그것을 ContextInfo
얻으면 약간의 슬라이스 앤 주사위로 해당 열을 쿼리 할 수 있습니다 .
SELECT *
,spid = CONVERT(INT, SUBSTRING(ContextInfo, 1, 4))
,client = CONVERT(VARCHAR(64), SUBSTRING(ContextInfo, 5, 64))
FROM cdc.schema_table_CT
기술적으로, 당신은 할 수 SUBSTRING
및 CONVERT
기본 제약 조건의 일환으로 물건을 그냥 IP가 클라이언트를 저장하지만, 전체 컨텍스트를 저장하기 위해 더 빨리 할 수있다 (그것은 모든에서 수행 될 때 INSERT
), 그리고 단지의 값을 추출 SELECT
필요할 때
모든 행 SUBSTRING
과 CONVERT
호출을 단일 행 인라인 테이블 반환 함수 로 래핑 하는 것이 CROSS APPLY
필요할 수 있습니다. 이렇게하면 언 패킹 논리가 한 곳에 유지됩니다.
CREATE FUNCTION fn_context (
@context_info VARBINARY(128)
)
RETURNS TABLE
AS RETURN (
SELECT
spid = CONVERT(INT, SUBSTRING(@context_info, 1, 4))
,client = CONVERT(VARCHAR(64), SUBSTRING(@context_info, 5, 64))
)
GO
SELECT *
FROM cdc.schema_table_CT s
CROSS APPLY dbo.fn_context(s.ContextInfo) c
참고 CONTEXT_INFO
만 128 바이트입니다 VARBINARY
. 128 바이트에 들어갈 수있는 것보다 많은 데이터가 필요한 경우 모든 데이터를 보유 할 테이블을 만들고 로그온 트리거의 테이블에 해당 '세션'에 대한 행으로 삽입하고 해당 테이블 CONTEXT_INFO
의 서로 게이트 키 값으로 설정 합니다.
또한 기본 제약 조건 일 뿐이므로 적절한 권한이있는 사용자가 미사용 테이블의 해당 컨텍스트 데이터를 덮어 쓰는 것은 쉽지 않습니다. 물론 '감사'스타일 테이블의 다른 모든 열에 대해서도 마찬가지입니다.
기본이 아닌 지속적인 계산 열이 될 수 있다면 좋을 것입니다. 그러나 CONTEXT_INFO()
함수는 비 결정적이므로 FUNCTION
실패 하지 않습니다 (당신은 주위에 약간의 속임수 를 사용할 수는 VIEW
있지만, 그렇지 않습니다. ).
또한 SET CONTEXT_INFO
자신 에게 전화를 걸고 하루를 엉망으로 만들 수 있는 충분한 액세스 권한을 가진 사용자에게는 사소한 일입니다 (예 : 가짜 값 또는 특수 제작 된 저장 주입으로). 잘.
호스트 이름에 관해서는의 ClientHost
요소가 EVENTDATA()
IP 주소 (또는 <local machine>
표시기) 를 제공 한다고 생각합니다 . 기술적으로 CLR을 사용하여 역방향 DNS 조회를 호스트 이름으로 다시 수행 할 수는 있지만이 방법은 매번 수행하기에는 너무 느린 경향이 있으므로 그렇게하지 않는INSERT
것이 좋습니다 .
이 경우 이 호스트 이름을 가지고, 당신은 정기적으로 해당 지역의 DHCP 서버 나 DNS 영역 파일에서 현재 임대와 별도의 테이블을 채우려면 대역 외 과정과 같은 SQL 에이전트 작업을 사용하려면, 그리고 수도 LEFT JOIN
에서와 향후 쿼리 (또는 FUNCTION
특정 시점에 대한 기본 제약 조건에 값을 제공하기 위해 스칼라 로 래핑 ).
응용 프로그램에 공용 구성 요소가 있으면 IP 주소와 호스트 이름을 신뢰할 수 없습니다 (예 : NAT로 인해). 공개 대상이 아니더라도 대부분의 IP / 호스트 이름 맵에는 특정 시간 기반 구성 요소가 있으므로 고려해야합니다.
마지막으로 로그인 트리거를 구현하기 전에 서버의 전용 관리자 연결을 설정하는 것이 좋습니다. 로그인 트리거가 중단되면 모든 사용자가 로그인하지 못하게 할 수 있습니다 (sysadmin 계정 포함).
USE master
GO
-- you may want to do this, so you have a back-out if the login trigger breaks login
EXEC sp_configure 'remote admin connections', 1
GO
RECONFIGURE
GO
잠기면 DAC를 사용하여 로그인 트리거를 삭제하거나 비활성화 할 수 있습니다.
C:\> sqlcmd -S localhost -d master -A
1> DISABLE TRIGGER tr_audit_login ON ALL SERVER
2> GO