При реализации механизма ограничения прав доступа на уровне записей (RLS) разработчики часто сталкиваются с задачей отфильтровать документы на основе состава их табличных частей. Например, необходимо запретить пользователю чтение документов ПоступлениеТоваровУслуг, если в них заполнена табличная часть Товары, но при этом разрешить просмотр документов, содержащих только услуги.
Разберем, почему привычные обращения через точку к реквизитам табличной части в RLS не работают так, как ожидается, и проанализируем решения этой задачи с точки зрения корректности и производительности.
Попытка написать условие RLS следующим образом приводит к тому, что из списка полностью пропадают все документы:
ПоступлениеТоваровУслуг ГДЕ ПоступлениеТоваровУслуг.Товары.Номенклатура ЕСТЬ NULL
Выясним причину такого поведения платформы 1С. При обращении через точку к реквизитам табличной части (например, ПоступлениеТоваровУслуг.Товары.Номенклатура) транслятор запросов 1С автоматически преобразует данное выражение во внутреннее соединение (INNER JOIN) таблицы шапки документа с таблицей его табличной части.
Если табличная часть документа пуста, в правой таблице соединительных данных нет ни одной строки. Из-за особенностей внутреннего соединения вся запись шапки документа исключается из выборки ещё до того, как СУБД начнет проверять условия в секции ГДЕ. В результате условия ЕСТЬ NULL или равенство пустой ссылке возвращают пустой результат выборки.
Самым правильным и производительным решением задачи ограничения доступа является отказ от прямой проверки табличной части внутри RLS. Любая проверка табличных частей в RLS создает существенную нагрузку на СУБД при формировании динамических списков и отчетов.
Рассмотрим пошагово реализацию этого подхода:
ПоступлениеТоваровУслуг реквизит с типом Булево (например, ЕстьТовары).ПередЗаписью или ПриЗаписи документа заполняем этот реквизит автоматически:
СодержитТовары = (Товары.Количество() > 0);
ПоступлениеТоваровУслуг ГДЕ НЕ ПоступлениеТоваровУслуг.СодержитТовары
Преимущество метода: СУБД выполняет выборку исключительно по одной таблице шапки документа. Это полностью исключает тяжелые операции соединения таблиц при каждом отображении списка документов пользователю.
Если дорабатывать структуру документа или добавлять реквизиты нельзя, можно применить явное ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) с ограничением по первой строке табличной части.
Проанализируем пример условия RLS:
ТекущаяТаблица ИЗ Документ.ПоступлениеТоваровУслуг КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ПО (ТекущаяТаблица.Ссылка = ПоступлениеТоваровУслугТовары.Ссылка
И ПоступлениеТоваровУслугТовары.НомерСтроки = 1)
ГДЕ ПоступлениеТоваровУслугТовары.Ссылка ЕСТЬ NULL
В данном случае используется левое соединение, поэтому документы с пустой табличной частью не отсекаются на этапе соединения. Если строка с номером 1 отсутствует, поле ПоступлениеТоваровУслугТовары.Ссылка будет иметь значение NULL, и документ попадет в итоговую выборку.
Еще одним рабочим вариантом без изменения структуры конфигурации является использование отрицания с проверкой номера строки:
ПоступлениеТоваровУслуг ГДЕ НЕ ПоступлениеТоваровУслуг.Товары.НомерСтроки В (1)
При использовании конструкции вида НЕ ТабличнаяЧасть.Реквизит В (...) платформа 1С генерирует запрос к СУБД с использованием подзапроса проверки отсутствия записей (NOT EXISTS / NOT IN), а не внутреннего соединения. По этой причине документы без строк в табличной части Товары успешно проходят фильтрацию.
Выбирая метод реализации ограничения прав, следует учитывать особенности работы СУБД: