Данная статья описывает реализацию одного из инструментов тестирования и отладки. Рассматривается это на примере идентификации внутренней структуры базы данных 1С Предприятие 8.0.(SQL) для произвольной конфигурации.
Материал опубликован на сайте Softpoint.ru
Владимир СердюкДано: Произвольная конфигурация на базе 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
Начать дискуссию