В практике разработчика и администратора 1С нередко возникают задачи "детективного" характера. Например, когда необходимо определить, в какой момент была создана уникальная самописная конфигурация, когда в неё вносились последние изменения или когда именно сторонний специалист обновил информационную базу. Ситуация усложняется, если в базе очищен журнал регистрации, отсутствуют резервные копии (бэкапы), а разработка велась без подключения к хранилищу конфигурации.
В этой статье мы подробно разберем, как решить эту задачу с помощью низкоуровневого анализа платформы 1С:Предприятие 8, декодирования уникальных идентификаторов (UUID) метаданных и выполнения запросов к системным таблицам СУБД.
Многие разработчики знают, что из уникального идентификатора (UUID) ссылки любого элемента справочника или документа в 1С можно извлечь точную дату и время его создания. Однако этот метод не всегда работает для определения даты изменения конфигурации: если данные переносились обработками (например, через XML или "Выгрузку/загрузку данных XML"), то в новой базе создаются новые элементы с новыми UUID, соответствующими моменту переноса данных.
Но у самих объектов метаданных (справочников, документов, реквизитов, регистров), созданных в Конфигураторе, также есть свои внутренние UUID. Они генерируются платформой один раз в момент добавления объекта в конфигурацию и никогда не изменяются при переносе конфигурации через файлы выгрузки (.cf), файлы данных (.dt) или при обновлении базы.
Платформа 1С использует стандарт генерации идентификаторов UUID Version 1 (Time-based). Это значит, что в теле самого идентификатора зашифрован MAC-адрес компьютера разработчика и точное время создания объекта с точностью до 100 наносекунд, отсчитываемое от начала Григорианского календаря (15 октября 1582 года).
Проанализируем этот метод по шагам:
Catalog.Номенклатура.xml).<MetaDataObject uuid="bdb62d89-cede-11e4-b12b-d4ae52b5e909">.Обратите внимание на третью группу цифр в UUID: 11e4. Первая цифра 1 указывает на то, что это UUID версии 1 (на основе времени). Если третья группа начинается с любой другой цифры (например, 4), этот UUID сгенерирован случайным образом, и извлечь дату из него невозможно. Платформа 1С:Предприятие генерирует UUID версии 1 для всех создаваемых вручную объектов метаданных.
Рассмотрим алгоритм декодирования даты на языке 1С. Чтобы получить дату из UUID версии 1, нам нужно переставить группы байт времени в правильном порядке (от младших к старшим), перевести полученное шестнадцатеричное число в десятичное и прибавить его к стартовой дате григорианского календаря.
Ниже представлен готовый код функции, которая преобразует UUID метаданных в точную дату создания объекта:
Функция ПолучитьДатуИзUUIDМетаданных(Знач СтрUUID) Экспорт
// Убираем дефисы и приводим к нижнему регистру
СтрUUID = НРег(СтрЗаменить(СтрUUID, "-", ""));
Если СтрДлина(СтрUUID) <> 32 Тогда
Возврат Неопределено;
КонецЕсли;
// UUID v1 состоит из частей времени: low (1-8), mid (9-12), high_and_version (13-16)
// Извлекаем и переставляем части времени в правильном порядке: high + mid + low
ЧастьHigh = Сред(СтрUUID, 14, 3); // Исключаем первую цифру версии '1'
ЧастьMid = Сред(СтрUUID, 9, 4);
ЧастьLow = Лев(СтрUUID, 8);
HexВремя = ЧастьHigh + ЧастьMid + ЧастьLow;
// Переводим из Hex в Dec
ЧислоИнтервалов = 0;
Для Позиция = 1 По СтрДлина(HexВремя) Цикл
Символ = Сред(HexВремя, Позиция, 1);
ЗначениеСимвола = Найти("0123456789abcdef", Символ) - 1;
ЧислоИнтервалов = ЧислоИнтервалов * 16 + ЗначениеСимвола;
КонецЦикла;
// Число интервалов по 100 наносекунд переводим в секунды
ВсегоСекунд = ЧислоИнтервалов / 10000000;
// Разница во времени между 15.10.1582 и 01.01.0001 в секундах
РазницаКалендарей = 49916304000;
// Получаем итоговую дату в формате UTC
ДатаUTC = Дата(1, 1, 1) + (ВсегоСекунд - РазницаКалендарей);
// Приводим к локальному времени (например, +3 часа для Москвы)
ЛокальнаяДата = ДатаUTC + СмещениеЛетнегоВремени() + СмещениеСтандартногоВремени();
Возврат ЛокальнаяДата;
КонецФункции
Используя этот метод для самого первого (базового) объекта, созданного разработчиком, мы сможем с точностью до секунды узнать, когда именно началась разработка данной конфигурации.
Если информационная база работает на клиент-серверном варианте под управлением СУБД, у нас появляется мощный инструмент для анализа структуры таблиц. При каждом обновлении конфигурации базы данных (нажатии клавиши F7 в Конфигураторе), платформа 1С выполняет реструктуризацию таблиц в СУБД. При добавлении новых реквизитов или объектов СУБД физически пересоздает или модифицирует соответствующие таблицы.
Даже если журнал регистрации полностью очищен, сама СУБД сохраняет метаданные о времени создания и изменения физических таблиц в системных каталогах.
Рассмотрим пример для MS SQL Server. Мы можем написать SQL-запрос, который выберет все таблицы, созданные платформой 1С (они начинаются с префиксов _Reference для справочников, _Document для документов и т.д.), и отсортирует их по дате изменения.
Выполним следующий запрос в SQL Server Management Studio (SSMS):
SELECT
name AS [Имя Таблицы СУБД],
create_date AS [Дата создания таблицы],
modify_date AS [Дата последнего изменения схемы]
FROM
sys.tables
WHERE
name LIKE '_Reference%'
OR name LIKE '_Document%'
OR name LIKE '_InfoReg%'
ORDER BY
modify_date DESC;
Этот запрос вернет список таблиц СУБД с точными датами их последнего изменения. Чтобы понять, какому объекту 1С соответствует конкретное физическое имя таблицы СУБД (например, _Reference45), мы можем воспользоваться встроенным методом платформы 1С. Сопоставить полученное имя таблицы, например _Reference45, с объектом 1С можно через просмотр физических таблиц и метаданных. Обработка не определяет даты изменения конфигурации; сведения о времени необходимо исследовать отдельно в СУБД.
Запустим следующую обработку в режиме "1С:Предприятие":
ТаблицаСтруктуры = ПолучитьСтруктуруХраненияБазыДанных(, Истина);
Для Каждого СтрокаТаблицы Из ТаблицаСтруктуры Цикл
Если СтрокаТаблицы.ИмяТаблицыХранения = "_Reference45" Тогда
Сообщить("Таблица _Reference45 соответствует объекту: " + СтрокаТаблицы.ИмяТаблицы);
КонецЕсли;
КонецЦикла;
Сопоставив данные системного каталога СУБД и структуры хранения 1С, мы получим точную хронологию изменения объектов конфигурации. Самая поздняя дата изменения схемы среди всех таблиц укажет нам на момент проведения последнего обновления конфигурации базы данных.
Вся конфигурация 1С хранится в СУБД в бинарном виде в таблице с именем Config. При внесении изменений в Конфигураторе и их сохранении (динамическом или монопольном) платформа записывает новые данные в эту таблицу.
В MS SQL Server мы можем обратиться к динамическим административным представлениям (DMV), чтобы узнать, когда в последний раз производилась запись (обновление данных) в таблицу Config с момента последнего запуска службы SQL-сервера.
Выполним следующий SQL-запрос:
SELECT
last_user_update AS [Дата последнего изменения конфигурации]
FROM
sys.dm_db_index_usage_stats
WHERE
database_id = DB_ID()
AND object_id = OBJECT_ID('Config');
Важное ограничение: этот метод показывает корректные данные только в том случае, если служба SQL Server не перезапускалась с момента последнего изменения конфигурации, так как статистика динамических представлений сбрасывается при перезапуске СУБД.
Если нам нужно понять, когда начали активно работать в базе данных после внедрения конфигурации (или когда были созданы первые учетные записи), мы можем проанализировать UUID самих ссылочных данных в базе.
Этот метод похож на Метод 1, но применяется к конкретным ссылкам (например, к первому созданному элементу справочника "Организации" или первому документу "Реализация товаров и услуг").
Напишем универсальный код функции 1С, которая извлекает дату создания непосредственно из любой ссылки объекта:
Функция ПолучитьДатуСозданияОбъектаПоСсылке(СсылкаНаОбъект) Экспорт
UUID = СсылкаНаОбъект.УникальныйИдентификатор();
СтрUUID = НРег(СтрЗаменить(Строка(UUID), "-", ""));
// Проверяем версию UUID (должна быть 1)
Если Сред(СтрUUID, 13, 1) <> "1" Тогда
ВызватьИсключение "Уникальный идентификатор ссылки не является UUID версии 1!";
КонецЕсли;
// Извлекаем временные метки
ЧастьHigh = Сред(СтрUUID, 14, 3);
ЧастьMid = Сред(СтрUUID, 9, 4);
ЧастьLow = Лев(СтрUUID, 8);
HexВремя = ЧастьHigh + ЧастьMid + ЧастьLow;
// Декодируем шестнадцатеричное число
Интервалы = 0;
Для Шаг = 1 По СтрДлина(HexВремя) Цикл
Символ = Сред(HexВремя, Шаг, 1);
Значение = Найти("0123456789abcdef", Символ) - 1;
Интервалы = Интервалы * 16 + Значение;
КонецЦикла;
Секунды = Интервалы / 10000000;
РазницаКалендарей = 49916304000;
Возврат Дата(1, 1, 1) + (Секунды - РазницаКалендарей) + СмещениеЛетнегоВремени() + СмещениеСтандартногоВремени();
КонецФункции
Применив эту функцию к предопределенным элементам справочников или первым введенным вручную документам, мы сможем установить хронологическую точку старта реального учета в информационной базе.
Если исследуемая конфигурация построена на базе типовых решений фирмы "1С" (например, 1С:Бухгалтерия, 1С:ERP, 1С:Управление торговлей) или использует Библиотеку стандартных подсистем (БСП), в ней обязательно присутствует встроенный механизм ведения истории обновлений.
При каждом обновлении конфигурации и последующем запуске системы в режиме "1С:Предприятие" срабатывают обработчики обновления БСП. Они записывают историю версий программного продукта в специализированные структуры хранения данных.
Проанализируем следующие объекты в режиме "1С:Предприятие":
Использование совокупности этих пяти методов гарантированно позволит вам установить точные временные рамки разработки, внедрения и модификации любой конфигурации 1С, даже в условиях полного отсутствия сопутствующей технической документации и логов.