Хранилище значения. Заметки

06.12.23

Разработка - Групповая разработка (Git, хранилище)

Некоторые подробности про общеизвестный инструмент.

Объект "ХранилищеЗначения" известен в 1С, начиная с 8.0 и, казалось бы, прост, понятен, изучен вдоль и поперёк. Но иногда возникают вопросы, касающиеся работы с хранилищем, и данная публикация (по сути, краткий антисклерозник) призвана зафиксировать ответы на некоторые из них.


Хранилище значения (далее ХЗ) представляет собой данные, полученные кодированием и, при необходимости, сжатием входных данных с помощью 32-х битного алгоритма Deflate, он же Deflation (о размерах сжатия см.ниже).
 

Хранение

Данные типа ХЗ могут храниться и обрабатываться в таблицах СУБД, в рамках модели данных 1С являясь реквизитами шапки, табличных частей, регистров, бизнес-процессов, задач; ресурсами регистров сведений, параметрами сеанса, общими реквизитами, функциональными опциями (на основе хранимых данных типа ХЗ). То есть, ХЗ может быть типом только для поля, хранящегося в базе (т.е. в отчётах, обработках, реквизитах/параметрах форм этого нет).

С точки зрения наиболее распространённых СУБД ситуация следующая.

SQL

ХЗ хранятся в полях IMAGE (тип данных SQL2003: BLOB), это традиционный тип LOB-хранилищ для SQL Server, в котором можно хранить как текстовые, так и двоичные данные. Хранит двоичное значение переменной длины до 2 147 483 647 байт (т.е. 2 Гбайт или 2^31 байт), механика выделения места под страницу таблицы и таблицу вообще варьируется в разных версиях SQL и разных настройках. Столбцы типа IMAGE (как и родственная ей TEXT) имеют множество ограничений на способы использования - например, нельзя создавать индекс по столбцу типа IMAGE, есть особенности при работе с хранимыми процедурами. При этом, значениями типа IMAGE можно манипулировать при помощи функций DATALENGTH, PATINDEX, SUBSTRING, TEXTPTR и ТЕХTVALID, а также команд READTEXT, SET TEXTSIZE, UPDATETEXT и WRITETEXT. Преобразования типа implicit conversion (а именно на них опирается 1С) возможны из IMAGE в: binary, varbinary, timestamp (логично, сплошь двоичное по сути), в него ещё можно из char и  varchar. Сам этот тип усердно НЕ рекомендуется к использованию в скуле, но до сих пор есть. Последние несколько версий платформ для хранения ХЗ в MS SQL используют тип VARBINARY(MAX). IMAGE является устаревшим и используется по накатанной из старых версий платформ.

PostgreSQL

ХЗ хранятся в полях bytea (ссылка стандарта на тот же тип данных SQL2003: BLOB). Для хранения нужны 4 байта плюс реальный размер битовой строки (т.е. 4 бита сверх трёхбитового заголовка самого потока Deflate). В двоичных строках можно хранить байты с кодом 0 и другими «непечатаемыми» значениями (обычно это значения вне десятичного диапазона 32..126). Обрабатываются они как двоичные в чистом виде (не как текстовые, не по языковым стандартам). Тип bytea поддерживает два формата ввода и вывода: "шестнадцатеричный" и традиционный для PostgreSQL формат "спецпоследовательностей". Входные данные принимаются в обоих форматах, а формат выходных данных зависит от параметра конфигурации bytea_output; по умолчанию выбран 16-ричный. Кстати, известно, что оный формат был введён в PostgreSQL 9.0; в ранних версиях он может и не работать - интересно, учитывает ли это платформа? По утверждению ИТС, содержит значение, если его тип "Хранилище значения", или последовательность байт нулевой длины в противном случае.

Кстати, если говорить об интеграции и внутренних передачах таких значений между системами - в Visual Basic, например, эти значения преобразуются в одномерные массивы Byte(). Этот массив имеет диапазон Byte (от 0 до length 1), где length — число байтов в значениях SQL Server binary, varbinary или image.

ХЗ не может фигурировать в составных типах. Однако, есть несколько способов обойти декларативный запрет и даже использовать в функционале: хранилище может быть определяемым типом, но только одиночным значением во всём типе (т.е. не составным), и может фигурировать как тип ПВХ (через определяемый), но тоже только одиночным. Возможно, именно так проявляется необходимость объявления требуемого места в таблицах СУБД.


Переменные и формы

Данные типа ХЗ могут храниться в переменной языка 1С в контексте выполнения , т.е. размещаться в поименованной области памяти. Ограничений на использование нет ни по типам модулей (общие, глобальные, модули объектов, менеджеров, форм, команд и т.д.), ни по исполнению на клиенте/сервере. Конструируется на сервере, а как переменная живёт и на клиенте. Является одним из традиционных способов рабоче-служебной сериализации данных, в т.ч. безотносительно степени сжатия, и передачи в сжатом виде. Само понятие "сериализация" в СП 1С привязано именно к ХЗ, т.е. возможность помещения значения в ХЗ терминологически и было долгое время сериализацией. В ХЗ может быть помещено любое значение, про которое это указано в СП, и любая универсальная либо обменная коллекция, состоящая только из сериализуемых значений на любую глубину вложенности.

Программное использование ХЗ не вполне прозрачно и очевидно. Так, чтение реквизита объекта типа ХЗ выполняют традиционно в "ПриЧтенииНаСервере", а запись - в "ПередЗаписьюНаСервере", потому что в данных формы ХЗ официально вроде как нет. На самом деле, возможны варианты. ХЗ может быть присвоено реквизиту формы (в т.ч. табличному реквизиту коллекции) с типом "Произвольный". Ряд ситуаций работы с ХЗ в клиентском контексте, несмотря на утверждения СП, не компилируется, т.е. для уверенности в результате лучше всё делать на сервере. Очевидно, что не стоит работать с ХЗ как с переменной модуля формы.

Находясь в коллекциях формы, НЕ может обрабатываться на клиенте (чтение/запись), но не теряется при передаче формы на сервер и обратно, сбросе кэша, переинициализации контекста. Может передаваться как параметр формы без его явного объявления в т.ч. в другую форму; может быть ключевым параметром формы - всё это корректно работает.

В обычных формах реквизит недоступен к выводу в список и на форму, но может фигурировать как значение реквизита формы произвольного типа или через определяемый (вообще в реквизитах только через него и можно "обернуть"), и как значение колонки таблицы/дерева значений произвольного типа.
В управляемых формах аналогично. Формально, выводятся сообщения, что "в данных формы нельзя", но если обёрнуто в произвольный реквизит, то хранит, обрабатывает и показывает.

ХЗ может фигурировать как аргумент в методе обычной и упр.формы Закрыть(хз), и корректно передаётся; может и фигурировать в ОписанииОповещения как результат закрытия формы, ранее открытой с указанием такового.
Важно: между сеансами для формы с авто (а равно и не-авто) сохранением значений произвольный реквизит с таким значением НЕ сохраняет!

Современное использование ХЗ поражает нетривиальностью - так, например, в БСП значения типа ХЗ хранятся и обрабатываются в параметрах СКД отчётов, и на этом основана целая механика работы. Вопросы трансляции и исполнения СКД, в чьих полях так или иначе фигурируют ХЗ или заявлена типизация ХЗ - тема отдельного исследования.
 

Кодинг

Может возвращаться на клиент как результат функции, параметр процедуры, может передаваться как параметр с клиента и вообще как аргумент, в т.ч. с ключевым "Знач", кроме, конечно, модулей повторного использования.

Помимо сериализации, ХЗ предлагает в конструкторе управление сжатием (это нелинейно эквивалентно степеням сжатия, т.е. "быстрое", "обычное", "максимальное", а не статике/динамике по Хоффману).

Поскольку нет возможности напрямую замерить объём, занимаемый переменной 1С в памяти, рассматривать приходится строки и двоичные данные, полученные из них. ХЗ удобно обрабатывать через xml

// из строки в ХЗ
хз=XMLЗначение(Тип("ХранилищеЗначения"),ИсходнаяСтрокаBase64);
// из ХЗ в строку
ИтоговаяСтрокаBase64=XMLСтрока(ХЗ);
стро1=XMLСтрока(хз);
// эквивалентно
стро2=СериализаторXDTO.XMLСтрока(хз);
// тогда как, например, такая сериализация гораздо больше по размеру
стро3=ЗначениеВСтрокуВнутр(хз);

Важно понимать, что ХЗ это не значение, а указатель на область памяти. Они всегда разные, поэтому ни сравнение, ни поиск, ни отбор (в т.ч. в запросах) применять нельзя. Указатель равен сам себе лишь в рамках конкретного оператора-конструктора (и, кстати, в этом случае по нему работает, например, поиск в универсальных коллекциях). Так, например, если "А=хз; Б=А;", то А будет равно Б. А для сравнения именно значений следует действовать так (причём это не будет распаковкой как "расжатием"):

СодержимыеВнутриХЗРавны=(XMLСтрока(хз1)=XMLСтрока(хз2));

и это не зависит от наличия и степени сжатия в их конструкторах. Кстати, после применения конструктора определить степень сжатия можно лишь преобразованием в двоичные данные и анализом первых битов потока.

Так вот, размер "как есть" мы можем узнать, либо создав объект метаданных (например, справочник с единственным реквизитом типа ХЗ) и получив размер таблицы методом ПолучитьРазмерДанныхБазыДанных, либо опосредованно, через строку и двоичные данные (учитывая неизбежно прибавляемое), так: 

Размер=ПолучитьДвоичныеДанныеИзСтроки(XMLСтрока(хз)).Размер();
// или так
Размер=Base64Значение(XMLСтрока(хз)).Размер();

Это не годится для абсолютных замеров (так, один символ латиницы, т.е. по идее 1 байт, и символ кириллицы, 2 байта, дают одинаковые 32 байта на выходе), но годится для относительных прикидок. Так, вызов без прямого указания "СжатиеДанных" всегда даёт значение большего (и иногда существенно большего, в разы) размера, чем с любым сжатием. На малых размерах сжатие не даёт заметного эффекта при любом аргументе. В СП сказано, что аргумент по умолчанию для объекта "СжатиеДанных" равен 1, но сжатие без его указания и с явным указанием даёт разные размеры, до десятков и сотен байт. На средних объектах (например, строки 500-1000 символов) сжатие со степенью выше 3-4 даёт примерно одинаковые итоговые размеры, т.е. эффективность одинаковая. Наряду с этим, в ряде старых релизов конструктор без сжатия даёт меньший итоговый размер.

Деструкция этого указателя тоже имеет нюансы. 

// правильные способы
хз="";
хз=Неопределено;
хз=Null;
хз=Новый ХранилищеЗначения(Неопределено);
хз=Новый ХранилищеЗначения(Null);
// неправильный способ
хз=Новый ХранилищеЗначения(""); // занимает как минимум на 3 байта больше, чем остальные

В таблицах/деревьях значений с явно объявленной колонкой может использоваться везде, кроме запроса (не помещается в ВТ), не воспринимается как аргумент запроса, даже как &ПараметрТипаХЗ в секции "ВЫБРАТЬ". Что, учитывая известное про СУБД, не вполне логично.

При копировании объекта с реквизитами, содержащими ХЗ, ни интерфейсно, ни программно, эти значения НЕ наследуются, не копируются.

Если некое значение, упакованное в ХЗ, подпадает под RLS, то логика следующая - платформа всегда вначале выполняет неявную распаковку, либо рассматривает сделанную в коде явную; а затем накладывает ограничение, так что RLS применится именно для уже распакованного значения, спровоцировать тут ошибку ни разу не удалось.

Из интересных публикаций рекомендую эту.

Поскольку в современных метаданных конфигураций реквизит типа ХЗ где-нибудь точно наличествует, а также фигурирует в коде, то вопрос занимаемого им места актуален. В связи с этим было бы интересно исследовать рост места, занимаемого таблицей, состоящей исключительно из ХЗ, а также сравнить эффективность работы "СжатиеДанных" и основанного на том же Deflation объекта "ЗаписьZipФайла", пробуя разные значения УровеньСжатияZIP (естественно, при МетодСжатияZIP = "Сжатие").

 

Следует учитывать, что ряд объектов и данных 1С не подлежит сериализации в ХЗ. Ни в режиме сжатия данных, ни без него. Таковым, например, может быть компоновщик настроек СКД или иные служебные объекты СКД. В таком случае будет выдано сообщение "Переданное значение не может быть помещено в ХранилищеЗначения, поскольку не сериализуется или содержит вложенный несериализуемый элемент".

 

Возможно, этот привычный и вроде бы простой объект, Хранилище значения, преподнесёт (особенно на больших данных и нагрузках) ещё некоторые удивительные сюрпризы.

P.S. Опыты проводились на 8.3.6.2237 и 8.3.16.1224.

Хранилище значения СУБД размер данных

См. также

Системы контроля версий для 1С-разработчиков.

1С-программирование DevOps и автоматизация разработки Групповая разработка (Git, хранилище) DevOps для 1С Платформа 1С v8.3 Платные (руб)

Основы командной разработки на 1С. Использование систем контроля версий при разработке на платформе 1С:Предприятие 8

4900 руб.

29.06.2022    9372    78    4    

112

Обновляемый список последних статей Инфостарт для профиля Github

Групповая разработка (Git, хранилище) Бесплатно (free)

Не знаете, чем бы таким заполнить свой профиль Github? Заполните его своими статьями на Инфостарт! Этот простой workflow сам соберет список ваших последних статей и будет периодически обновлять его для актуализации данных.

08.04.2024    906    bayselonarrend    2    

30

Процесс разработки с использованием GIT и расширений для 1С:ERP. Без EDT

Групповая разработка (Git, хранилище) Платформа 1С v8.3 1С:ERP Управление предприятием 2 Бесплатно (free)

Доработки 1С:ERP на крупных проектах можно организовать, не внося изменения в саму типовую конфигурацию, а используя только расширения и отдельные «микроконфигурации». Расскажем о том, как это сделать без EDT, используя процесс разработки GitHub Flow.

02.04.2024    4479    Begemoth80    24    

45

Особенности национального Workflow: Github Actions и OneScript

Групповая разработка (Git, хранилище) OneScript Бесплатно (free)

Сегодня мы посмотрим на Github Actions - встроенный инструментарий Github для автоматизации рабочих процессов. Разберем, что это такое, зачем и причем тут OneScript.

25.03.2024    1558    bayselonarrend    3    

38

Автоматизация процесса разработки с помощью сервиса GitFlic

Групповая разработка (Git, хранилище) Бесплатно (free)

GitFlic – первая в России полностью самостоятельная реализация сервиса для хранения репозиториев с исходным кодом. За три года разработки сервис GitFlic стал полноценным инструментом, которым можно заменить GitLab, GitHub и BitBucket. Расскажем о том, как выстроить в GitFlic процесс автоматического тестирования, статического анализа кода и сборки приложений.

05.03.2024    2066    user1989937    6    

16

OpenYellow - рейтинг открытых GitHub репозиториев для платформы 1С:Предприятие

Групповая разработка (Git, хранилище) Бесплатно (free)

Обновляемый топ GitHub репозиториев для 1С по всем языкам программирования и еще немного рассуждений про open-source.

05.02.2024    3994    bayselonarrend    15    

62

Насколько глубок 1С-ный GitHub?

Групповая разработка (Git, хранилище) Бесплатно (free)

Open-source проекты - важная часть мира программного обеспечения. 1С привычно держится немного в стороне от глобальных трендов, но бросить холодный статистический взгляд на положение дел мне показалось небезынтересным.

22.01.2024    8040    bayselonarrend    50    

87

TCP прокси-сервер хранилища конфигурации 1С

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) OneScript Платформа 1С v8.3 Бесплатно (free)

Продолжение истории с прокси хранилища, но уже не на HTTP, а на TCP и без падений по памяти веб-сервера. Проверяем комментарии хранилища, вызываем веб-хуки, старты пайплайнов, gitsync по событию помещения версии в хранилище. И все это полностью на знакомом и понятном OneScript.

17.01.2024    2999    kamisov    17    

59
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SlavaKron 03.11.20 09:20 Сейчас в теме
РазмерХЗПлюс=ПолучитьДвоичныеДанныеИзСтроки(XMLСтрока(хз)).Размер();
Это явно не правильно. Отсюда и проблемы, о которых вы говорите.
Размер ХЗ без учета размера описания ХЗ или просто размер сжатых данных:
Размер = Base64Значение(XMLСтрока(ХЗ)).Размер();
+
2. Yashazz 4722 03.11.20 10:06 Сейчас в теме
(1) Этот вариант тоже не даёт правильные данные, проверял. Но да, чего-то не внёс в статью; ща внесу.
+
3. Yashazz 4722 03.11.20 10:10 Сейчас в теме
Вообще очень не хватает нормального sizeof, применяемого как к пустой типизированной, так и к наполненной переменной... Много интересного узнали бы.
FatPanzer; +1
4. papa_harlo 154 05.11.20 14:17 Сейчас в теме
Эх... мне бы Вашу статью неделю назад увидеть)))
может подскажите:
сохраняю картинку jpg в хранилище значений без сжатия.
селектом в MS SQL получаю запись, значение моей картинки содержит сигнатуру вида "0x0101........"
посмотрел здесь https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D1%81%D0%B8%D­0%B3%D0%BD%D0%B0%D1%82%D1%83%D1%80_%D1%84%D0%B0%D0%B9%D0%BB%­D0%BE%D0%B2
такой сигнатуры не существует. так и не понял как не используя 1С "вытянуть" данные из записи((((

(3)
Прикрепленные файлы:
+
5. papa_harlo 154 05.11.20 14:18 Сейчас в теме
7. SlavaKron 05.11.20 15:04 Сейчас в теме
(4)
не используя 1С "вытянуть" данные из записи

Если версия MS SQL 2016 и выше, можно использовать функцию DECOMPRESS. Для хранилища значений:
SEL ECT
    DECOMPRESS(0x1F8B0800000000000400 + SUBSTRING(DataFieldName, 19, LEN(DataFieldName) - 18)) as Data
FR OM
    MyTable

Еще можно тут почитать: https://infostart.ru/public/1125887/
dmarenin; NittenRenegade; YPermitin; DrAku1a; Drivingblind; Yashazz; papa_harlo; +7
8. Yashazz 4722 05.11.20 16:45 Сейчас в теме
(7) Во! Спасибо, мил человек. Теоретически я это знал, а вот пристреляться по позициям всё времени не было. Спасибище!
+
6. aspirator23 339 05.11.20 14:29 Сейчас в теме
Интересные раскопки.
+
9. asved.ru 36 06.11.20 07:31 Сейчас в теме
ХЗ не может фигурировать в составных типах. Однако, есть несколько способов обойти


Увижу в коде - убью.
Если со стороны платформы что-то запрещено и причины этого вам не понятны, то это не необоснованный запрет, а недостаток знаний у Вас.
Поручик; +1
11. Yashazz 4722 06.11.20 17:24 Сейчас в теме
(9) Ну лично мне-то причины понятны) И да, соглашусь - я за такое в реальном коде без прям жёсткой необходимости - тоже убивал бы.
Поручик; +1
10. SerVer1C 750 06.11.20 08:57 Сейчас в теме
Последние несколько версий платформ для хранения ХЗ в MS SQL используют тип VARBINARY(MAX). IMAGE является устаревшим и используется по накатанной из старых версий платформ.
+
12. PythonJ 117 24.11.20 00:03 Сейчас в теме
В СП сказано, что аргумент по умолчанию для объекта "СжатиеДанных" равен 1, но сжатие без его указания и с явным указанием даёт разные размеры, до десятков и сотен байт.

В СП сказано:
Целое число в диапазоне -1...9. -1 - степень сжатия по умолчанию. 0 - никакого сжатия, 9 - максимальная степень сжатия.
Значение по умолчанию: -1.

-1 - не степень сжатия как таковая, а "степень сжатия по умолчанию для алгоритма Deflation", она не обязана быть равной 1 или любой другой из диапазона 1..9.
+
13. PlatonStepan 38 11.12.20 08:38 Сейчас в теме
(12) Вроде как по-умолчанию было 6, а сейчас -1.

(0)
хз=Новый ХранилищеЗначения(Значение, Новый СжатиеДанных()) - насколько я понял, содержимое предваряется BOM (EF BB BF) - byte order mark, а уже потом сжимается.
Из-за этого разобрать содержимое в сторонних программах не всегда получается, даже если избавиться от описания сжатия данных обрезав первые 18 байт.

или что-то не так делаю?

хз = Новый ХранилищеЗначения(ДанныеДляСжатия, Новый СжатиеДанных(9));
//Через сериализацию получим строку Base64
Base64Строка_ = XMLСтрока(хз);

БуферДвоичныхДанных_ = ПолучитьБуферДвоичныхДанныхИзBase64Строки(Base64Строка_);

//отсекаем описание сжатия данных 1с
БуферДвоичныхДанных_ = БуферДвоичныхДанных_.ПолучитьСрез(18);

Base64Строка_Deflate = ПолучитьBase64СтрокуИзБуфераДвоичныхДанных(БуферДвоичныхДанных_);
Показать
+
14. Al-X 03.02.22 15:13 Сейчас в теме
Добрый день ! А подскажите, если есть у меня регистр сведений, и у него в реквизит типа ХранилищеЗначений. Будет ли автоматически удаляться данные ХЗ из базы данных, если удалить запись регистра значений ?
+
15. Yashazz 4722 03.02.22 15:24 Сейчас в теме
(14) Если там хранится ссылка на некое значение, то, конечно, ничего ниоткуда не удалится. Для ХЗ ссылочная целостность не поддерживается, так же, как и для любых сериализованных данных.
Поручик; +1
16. Serggray 14 28.03.23 15:39 Сейчас в теме
(15) Хорошо, если не автоматически , то как можно очистить этот реквизит , чтобы очистилось ХранилищеЗначений занимаемое им.
К примеру такая конструкция не срабатывает
	zn = ЗаписьРегистра.ХранимыйФайл.Получить() ;
	zn = Новый ХранилищеЗначения(Неопределено) ;
	ЗаписьРегистра.ХранимыйФайл = zn ;
+
Оставьте свое сообщение