Реализация одного из инструментов отладки 1С Предприятие 8.0

Реализация одного из инструментов отладки на примере автоматизации процесса определения внутренней струтуры 1С Предприятие 8.0

Данная статья описывает реализацию одного из инструментов тестирования и отладки. Рассматривается это на примере идентификации внутренней структуры базы данных 1С Предприятие 8.0.(SQL) для произвольной конфигурации.

Дано : Произвольная конфигурация на базе 1С Предприятие 8.0. (SQL)

Цель : Максимально автоматизировать процесс идентификации внутренней структуры к объектам на уровне приложения.

Реализация :Для начала необходимо понять, как это можно было бы сделать вручную. Очевидно, брать в руки профайлер настраивать трейсы. Изменяя объекты приложения сопоставлять данные в трейсах с соответствующими изменениями объектов. В общем, этот процесс не сложный, но довольно таки трудоемкий и рутинный. На помощь в автоматизации этого процесса нам приходят триггера MSSQL и возможность их динамического создания. Кроме этого, имея возможность определения структуры метаданных как в 1С, так и в MS SQL(системных объектов) мы получаем весь необходимый инструментарий.

Итак, алгоритм автоматизации заключается в выполнении следующих пунктов:

1) Создаем триггера на все объекты MSSQL базы. В реализации триггера указывается запись в отдельную таблицу следующих значений : ИмяТаблицы, ИмяИзмененногоПоля, PK или уникальный индекс измененной записи, ЗначениеИзВременнойТаблицы. В переменную ЗначениеИзВременнойТаблицы будет передаваться из 1С соответствующее изменению в объекте 1С. Собственно говоря, система этих триггеров это есть фактически одна из систем логирования в БД.

2) Реализуем функцию, которая будет передавать в MSSQL значение изменения в объекте 1С. Передавать мы должны таким образом что бы затем можно было получить это значение и присвоить переменной ЗначениеИзВременнойТаблицы в вышеописанной системе триггеров.

3) Организуем циклом перебор метаданных в 1С. Производим последовательное изменение объектов 1С.Передаем в переменную ЗначениеИзВременнойТаблицы конкретное изменение объекта. Передать значение в ЗначениеИзВременнойТаблицы необходимо до выполнения команды изменяющей объект для того что бы триггер получил информацию о том какой объект 1С его вызвал и какие изменения в этом объекте произошли. Собственно говоря, в этом пункте и заключается одна из сложностей не позволяющая этот алгоритм сделать полностью универсальным. Дело в том, что 1С использует агрегацию данных и кроме этого логика изменений зависит от текущего состояния объекта. Если мы изменим, значение одного реквизита в случае, когда документ уже существует в базе и когда этот документ создается впервые в результате получим различный набор команд к SQL серверу. С агрегацией аналогичная ситуация. Предположим у нас есть два одинаковых документа с разницей по дате в год, осуществляющих линейное движение по регистрам(для упрощения без обработки всякой логики). Так вот в случае проведения первого документа мы получим одно количество изменений по агрегационным записям, а случае проведения второго другое количество.

Реализовав подобную систему на выходе, мы получим соответствие объектов 1С к объектам MS SQL. Конечно, нужно понимать, что информация в полученной таблице довольно таки сыра и потребует некоторой обработки. Кроме этого можно составить последовательность операций с метаданными и соответствующую обработку результирующей таблицы, что бы определить автоматически для этой конфигурации внутреннюю структуру данных. Неважно будем мы заново создавать или удалять эту конфигурацию, в какой последовательности создавать в конфигураторе объекты метаданных - подобная система однозначно определит соответствие. Но важно понимать следующее - для любой произвольно взятой конфигурации эта система работать не будет. Точнее она будет работать, но если для этой конфигурации придумать свою систему изменения объектов на основании метаданных и обработки результирующей таблицы. Но этого по большому счету и не нужно. Подобная система это инструмент. С помощью этого инструмента специалист довольно быстро разберется с любой интересующей его структурой.

Ниже я приведу несложный код для автоматической генерации триггеров на БД.

	declare @str char(8000)
declare @Table_name char(100)
declare @id int
declare @Column_name char(100)

declare TableCur cursor local fast_forward for -- Декларация курсора
select [name],[id] from sysobjects
where xtype='U' and status>0 and name<>'Log1CforSQL' 
-- Запрос по всем таблица в текущей БД кроме той куда пишется лог
open TableCur   -- Открытие крсора
fetch TableCur into @Table_name,@id
while (@@fetch_status<>-1)  -- Цикл по курсору
begin 
set @str='if exists (select * from dbo.sysobjects where id = 
object_id(N''[dbo].[TR_U_'+rtrim(@Table_name)+']'') 
and OBJECTPROPERTY(id, N''IsTrigger'') = 1)
drop trigger [dbo].[TR_U_'+rtrim(@Table_name)+']' -- удаляем если он уже существует
exec (@str) 
select @str='CREATE TRIGGER [TR_U_'+rtrim(@Table_name)+'] ON [dbo].['+rtrim(@Table_name)+'] 
			FOR Update
			AS    
			set nocount on
			declare @Izm1CObject char(1000)
			select @Izm1CObject=Izm1CObject from BufferIzm1CObject where spid=@@spid '

declare ColumnCur cursor local fast_forward for 
select [name] from syscolumns  -- Запрос по списку колонок конкретной таблицы
where [id] = @id 
open ColumnCur   -- Открытие курсора
fetch ColumnCur into @Column_name
while (@@fetch_status<>-1)  
begin 
select @str =rtrim(@str)+' if update('+@Column_name+')
	begin	insert into Log1CforSQL(TableName,ColumnName,Izm1CObject,Type)
	values('''+rtrim(@Table_name)+''','''+rtrim(@Column_name)+''',@Izm1CObject,''U'') end'
--Вообще то признак update не гарантирует того что поле изменилось(в 7.7. так и происходило)
--поэтому здесь необходимо сравнивать deleted и inserted , данный вариант - некоторое упрощение
fetch ColumnCur into @Column_name
end
select @str =rtrim(@str)+' set nocount off'
close ColumnCur
deallocate ColumnCur
--Создание триггеров на удаление и вставку. 
--Если текст для создания триггера будет более 8000 символо то этот алгоритм работать не бдет
--Вообще то для этих случаев необходимо создавать текст триггера через временную таблицу например
-- В данном случае так реализованно для простоты понимания
exec (@str)
set @str='if exists (select * from dbo.sysobjects where id = 
object_id(N''[dbo].[TR_D_'+rtrim(@Table_name)+']'') 
and OBJECTPROPERTY(id, N''IsTrigger'') = 1)
drop trigger [dbo].[TR_D_'+rtrim(@Table_name)+']' -- удаляем если он уже существует
exec (@str)
select @str= 'CREATE TRIGGER [TR_D_'+rtrim(@Table_name)+'] ON [dbo].['+rtrim(@Table_name)+'] 
			FOR DELETE
			AS    
			set nocount on
			declare @Izm1CObject char(1000)
			select @Izm1CObject=Izm1CObject from BufferIzm1CObject where spid=@@spid
			insert into Log1CforSQL(TableName,ColumnName,Izm1CObject,Type) 
			values('''+rtrim(@Table_name)+''',''ALL'',@Izm1CObject,''D'')         
			set nocount off'
exec (@str) -- создаем триггер
set @str='if exists (select * from dbo.sysobjects where id = 
object_id(N''[dbo].[TR_I_'+rtrim(@Table_name)+']'') 
and OBJECTPROPERTY(id, N''IsTrigger'') = 1)
drop trigger [dbo].[TR_I_'+rtrim(@Table_name)+']' -- удаляем если он уже существует
exec (@str) 
select @str= 'CREATE TRIGGER [TR_I_'+rtrim(@Table_name)+'] ON [dbo].['+rtrim(@Table_name)+'] 
			FOR INSERT
			AS    
			set nocount on
			declare @Izm1CObject char(1000)
			select @Izm1CObject=Izm1CObject from BufferIzm1CObject where spid=@@spid
			insert into Log1CforSQL(TableName,ColumnName,Izm1CObject,Type) 
			values('''+rtrim(@Table_name)+''',''ALL'',@Izm1CObject,''I'')         
			set nocount off'
exec (@str)-- создаем триггер

fetch TableCur into @Table_name,@id -- Передача названия таблицы из курсора в переменную @Table_name
end
close TableCur
deallocate TableCur

 

Автор статьи - Сердюк В.И.
http://www.serduk.ru/

Начать дискуссию