Как возможное решение для SQL Server: Как найти строки, которые выполняются Я предложил добавить оператор вставки после каждого оператора.
Каким был бы эффективный способ разбить код хранимой процедуры на различные операторы, чтобы после каждого оператора можно было добавить дополнительный оператор вставки с предыдущей строкой? Если одна и та же строка встречается более одного раза внутри процедуры / функции / триггера, их также необходимо сделать уникальными с некоторым числом.
Комментарии и стиль не должны приниматься во внимание. Но важно, чтобы точный поток выполнения можно было отслеживать
Пример ввода 1:
/*******************************************************************************************
description
@param wioho
*******************************************************************************************/
CREATE PROC usp_Example1
(
@param VARCHAR(MAX),
@param2 INT
)
AS
BEGIN
BEGIN TRY
-- random comment
INSERT INTO dept VALUES (@param, @param2)
IF EXISTS (
SELECT 1
FROM dept
WHERE deptno = 10
)
THROW 50001, 'Dept 10 exists', 1
ELSE
INSERT INTO dept VALUES (@param, @param2)
END TRY
BEGIN CATCH
THROW
END CATCH
END
Ожидаемый результат 1 (или функционально-эквивалентный):
/*******************************************************************************************
description
@param wioho
*******************************************************************************************/
CREATE PROC usp_Example1
(
@param VARCHAR(MAX),
@param2 INT
)
AS
BEGIN
BEGIN TRY
INSERT INTO coverageTrace VALUES ('usp_Example1', 'BEGIN TRY', 1)
-- random comment
INSERT INTO dept VALUES (@param, @param2)
INSERT INTO coverageTrace VALUES ('usp_Example1', 'INSERT INTO dept VALUES (@param, @param2)', 1)
IF EXISTS (
SELECT 1
FROM dept
WHERE deptno = 10
)
BEGIN
INSERT INTO coverageTrace VALUES ('usp_Example1', 'IF EXISTS (SELECT 1 FROM dept WHERE deptno = 10)', 1)
THROW 50001, 'Dept 10 exists', 1
END
ELSE IF 1 = 1
BEGIN
INSERT INTO dept VALUES (@param, @param2)
INSERT INTO coverageTrace VALUES ('usp_Example1', 'INSERT INTO dept VALUES (@param, @param2)', 2)
END
END TRY
BEGIN CATCH
INSERT INTO coverageTrace VALUES ('usp_Example1', 'BEGIN CATCH', 1)
THROW
END CATCH
END
Теперь, если кто-то неправильно оформляет свой код, это все равно должно работать.
Пример ввода 2:
/*******************************************************************************************
description @param wioho
*******************************************************************************************/
CREATE PROC usp_Example1(@param VARCHAR(MAX),@param2 INT) AS BEGIN
BEGIN TRY-- random comment
INSERT INTO dept VALUES (@param, @param2) IF EXISTS (
SELECT 1
FROM dept
WHERE deptno = 10
)
THROW 50001, 'Dept 10 exists', 1 ELSE
INSERT INTO dept VALUES (@param, @param2) END TRY BEGIN CATCH
THROW
END CATCH
END
Это должно дать (функционально) эквивалентный код ожидаемому результату 1
Обратите внимание, что этот код в случае операторов блока должен быть в состоянии знать, НАЧИНАЕТСЯ ЛИ БЫСТРЫЙ или КОНЕЦ, где он используется явно. Таким образом, код может добавить его явно, если это необходимо.
Имеется ли какой-либо код, который можно использовать повторно, или, возможно, я могу использовать регулярные выражения. Если возможно, я бы хотел сделать это в SQL, чтобы моя среда тестирования мутаций могла быть одним файлом на любом MS SQL Server.
Обратите внимание, что : это среда тестирования, и ручное изменение кода не опция, это имеет , который должен быть выполнен автоматически.
Обновление прогресса:
После комментария @Jeroen Mostert я начал экспериментировать с расширенной системой событий. У меня все еще есть пара проблем, как правильно фильтровать сгенерированный XML и как выполнять трассировку только в базе данных без жесткого кодирования в имени базы данных? (Исправлено генерацией кода (не было выпущено) Мне нужно было использовать широкий набор символов внутри поколения))
Текущий код:
USE master
GO
DROP DATABASE IF EXISTS testMSSQLDB
GO
CREATE DATABASE testMSSQLDB
GO
USE testMSSQLDB
GO
CREATE TYPE ID FROM INT
GO
CREATE TABLE dept (
deptno ID PRIMARY KEY
)
GO
IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='testMSSQLTrace')
DROP EVENT SESSION testMSSQLTrace ON SERVER;
DECLARE @cmd VARCHAR(MAX) = '';
SELECT @cmd = 'CREATE EVENT SESSION testMSSQLTrace
ON SERVER
ADD EVENT sqlserver.module_end
(SET collect_statement = (1)
WHERE (sqlserver.database_name = N''' + DB_NAME() + ''')),
--ADD EVENT sqlserver.rpc_completed
--(WHERE (sqlserver.database_name = N''' + DB_NAME() + ''')),
ADD EVENT sqlserver.sp_statement_completed
(WHERE (sqlserver.database_name = N''' + DB_NAME() + ''')),
--ADD EVENT sqlserver.sql_batch_completed
--(WHERE (sqlserver.database_name = N''' + DB_NAME() + ''')),
ADD EVENT sqlserver.sql_statement_completed
(WHERE (sqlserver.database_name = N''' + DB_NAME() + '''))
ADD TARGET package0.ring_buffer
WITH (
MAX_MEMORY = 2048 KB,
-- EVENT_RETENTION_MODE = NO_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 3 SECONDS,
MAX_EVENT_SIZE = 0 KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF
);'
EXEC (@cmd)
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = STOP;
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = START;
GO
CREATE OR ALTER PROC usp_temp
(
@param INT = 10
)
AS
BEGIN
IF @param = 10
BEGIN
DELETE dept
INSERT INTO dept VALUES (@param)
SELECT * FROM dept
END
ELSE
DELETE dept
END
GO
EXEC usp_temp
EXEC usp_temp 20
SELECT name, target_name, CAST(xet.target_data AS xml)
FROM sys.dm_xe_session_targets AS xet
JOIN sys.dm_xe_sessions AS xe
ON (xe.address = xet.event_session_address)
WHERE xe.name = 'testMSSQLTrace'
Создает (вырезает некоторые части):
<RingBufferTarget truncated="0" processingTime="0" totalEventsProcessed="12" eventCount="12" droppedCount="0" memoryUsed="2012">
<event name="sp_statement_completed" package="sqlserver" timestamp="2019-07-04T09:22:30.472Z">
<data name="source_database_id">
<type name="uint32" package="package0" />
<value>22</value>
</data>
<data name="object_id">
<type name="int32" package="package0" />
<value>1916742081</value>
</data>
<data name="object_type">
<type name="object_type" package="sqlserver" />
<value>8272</value>
<text>PROC</text>
</data>
<data name="duration">
<type name="int64" package="package0" />
<value>22</value>
</data>
<data name="cpu_time">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="physical_reads">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="logical_reads">
<type name="uint64" package="package0" />
<value>3</value>
</data>
<data name="writes">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="row_count">
<type name="uint64" package="package0" />
<value>1</value>
</data>
<data name="last_row_count">
<type name="uint64" package="package0" />
<value>1</value>
</data>
<data name="nest_level">
<type name="uint16" package="package0" />
<value>1</value>
</data>
<data name="line_number">
<type name="int32" package="package0" />
<value>11</value>
</data>
<data name="offset">
<type name="int32" package="package0" />
<value>214</value>
</data>
<data name="offset_end">
<type name="int32" package="package0" />
<value>276</value>
</data>
<data name="object_name">
<type name="unicode_string" package="package0" />
<value />
</data>
<data name="statement">
<type name="unicode_string" package="package0" />
<value>INSERT INTO dept VALUES (@param)</value>
</data>
</event>
<event name="sp_statement_completed" package="sqlserver" timestamp="2019-07-04T09:22:30.476Z">
<data name="source_database_id">
<type name="uint32" package="package0" />
<value>22</value>
</data>
<data name="object_id">
<type name="int32" package="package0" />
<value>1916742081</value>
</data>
<data name="object_type">
<type name="object_type" package="sqlserver" />
<value>8272</value>
<text>PROC</text>
</data>
<data name="duration">
<type name="int64" package="package0" />
<value>32</value>
</data>
<data name="cpu_time">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="physical_reads">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="logical_reads">
<type name="uint64" package="package0" />
<value>2</value>
</data>
<data name="writes">
<type name="uint64" package="package0" />
<value>0</value>
</data>
<data name="row_count">
<type name="uint64" package="package0" />
<value>1</value>
</data>
<data name="last_row_count">
<type name="uint64" package="package0" />
<value>1</value>
</data>
<data name="nest_level">
<type name="uint16" package="package0" />
<value>1</value>
</data>
<data name="line_number">
<type name="int32" package="package0" />
<value>12</value>
</data>
<data name="offset">
<type name="int32" package="package0" />
<value>286</value>
</data>
<data name="offset_end">
<type name="int32" package="package0" />
<value>320</value>
</data>
<data name="object_name">
<type name="unicode_string" package="package0" />
<value />
</data>
<data name="statement">
<type name="unicode_string" package="package0" />
<value>SELECT * FROM dept</value>
</data>
</event>
</RingBufferTarget>
Как я могу отфильтровать этот XML таким образом, чтобы остались только выполненная инструкция, тип объекта и идентификатор объекта, из которого он был выполнен? Конкретная потребность в информации заключается в том, что мне нужно знать, какие строки хранимой процедуры были выполнены, хранимая процедура может вызывать другую хранимую процедуру, в этом случае мне все еще нужно знать, какие операторы выполняла процедура и была ли она вложена в первую сохраненную процедуру. процедура. И если один и тот же оператор встречается несколько раз, мне нужно, чтобы его (относительный) номер строки
Или в предикатах:
Процедура X в хранимой процедуре верхнего уровня Y выполненная строка Z с номером белья J
Процедура X в хранимой процедуре верхнего уровня Y выполненная строка W с номером белья I
РЕДАКТИРОВАТЬ: Я провел еще несколько исследований и пришел к выводу, что мне нужны все события, которые имеют поле <data name="nest_level"><value>2</value></data>
. Где 2 - любое значение больше 1.
Эта ссылка https://www.scarydba.com/2018/09/24/extended-events-and-stored-procedure-parameter-values/ оказалась полезной для меня, чтобы получить все данные.