Как выгрузить УникальныйИдентификатор 1С в поле типа UUID в PostgreSQL через внешние источники данных?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При настройке интеграции между 1С и сторонними базами данных под управлением PostgreSQL разработчики часто сталкиваются с задачей сопоставления ссылочных типов. В 1С ссылка на любой ссылочный объект имеет УникальныйИдентификатор (GUID), а в PostgreSQL стандартом хранения таких ключей является системный тип UUID. Однако при попытке записать значение в таблицу внешнего источника данных напрямую возникает ошибка ODBC.

Разберем подробнее, почему возникает эта ошибка и какими способами можно правильно передать идентификатор из 1С в PostgreSQL.

Анализ проблемы и причин ошибки ODBC 22P02

Посмотрим на типичный пример кода, который вызывает сбой при использовании внешнего источника данных:


НоваяЗаписьSQL = ВнешниеИсточникиДанных.PostgreSQL.Таблицы.Test.СоздатьОбъект();
НоваяЗаписьSQL.name = Товар.Наименование;
НоваяЗаписьSQL.ID = Товар.УникальныйИдентификатор();
НоваяЗаписьSQL.Записать();

При выполнении этого кода платформа возвращает ошибку:

Ошибка ODBC. SQLSTATE: 22P02. Номер ошибки: 1. Описание: ОШИБКА: неверный синтаксис для типа uuid: "\xB0F3627878424FC048B8BE10D058F90D"

Выясним причину такого поведения:

  1. Поведение драйвера psqlODBC: Механизм внешних источников данных 1С работает через интерфейс ODBC. При передаче значения типа УникальныйИдентификатор платформа передает его драйверу в виде двоичного буфера фиксированной длины (16 байт). Драйвер psqlODBC сопоставляет такие бинарные данные с типом bytea.
  2. Отсутствие неявного приведения (Implicit Cast): В PostgreSQL для безопасности типов по умолчанию отключено неявное преобразование из bytea в uuid. Сервер СУБД получает байтовую последовательность в шестнадцатеричном виде с префиксом \x и не может автоматически сконвертировать её в целевую колонку uuid.
  3. Поведение строкового типа: Если в конфигураторе 1С изменить тип поля внешнего источника на Строка и передавать строковое представление идентификатора через функцию Строка(Товар.УникальныйИдентификатор()), то при генерации параметризованного запроса СУБД также может выдать ошибку синтаксиса или несоответствия типов, так как драйвер не выполняет явного приведения ::uuid для параметров команды INSERT.
  4. Порядок байтов в 1С: Если попытаться вручную разобрать 16 байт из bytea в PostgreSQL, важно помнить, что 1С хранит байты идентификатора со смещением групп по сравнению со стандартом RFC 4122 (для оптимизации индексирования по времени). Поэтому простое преобразование бинарного представления без перестановки байтов приведет к искажению оригинального строкового GUID.

Проанализируем практические способы решения этой проблемы.

Способ 1. Использование промежуточного текстового поля и вычисляемой колонки в PostgreSQL

Наиболее надежный и производительный способ решения задачи на стороне базы данных PostgreSQL — принимать идентификатор из 1С в виде обычной текстовой строки, а итоговый UUID формировать автоматически средствами самой СУБД.

Начиная с версии PostgreSQL 12, в таблицах поддерживаются вычисляемые столбцы (GENERATED ALWAYS AS ... STORED). Разберем шаги настройки:

  1. В целевой таблице PostgreSQL создадим или изменим поля: сделаем строковое поле для приема данных из 1С и вычисляемое поле с типом uuid для связей и индексов:
    
    ALTER TABLE test ADD COLUMN id_text varchar(36);
    ALTER TABLE test ADD COLUMN id uuid GENERATED ALWAYS AS (id_text::uuid) STORED;
    
  2. В конфигураторе 1С в свойствах таблицы внешнего источника данных сопоставим колонку id_text с типом Строка (длина 36).
  3. Заполним данные в коде 1С, передавая строковое представление идентификатора:
    
    НоваяЗаписьSQL = ВнешниеИсточникиДанных.PostgreSQL.Таблицы.Test.СоздатьОбъект();
    НоваяЗаписьSQL.name = Товар.Наименование;
    НоваяЗаписьSQL.id_text = Строка(Товар.УникальныйИдентификатор());
    НоваяЗаписьSQL.Записать();
    

Если используется более ранняя версия PostgreSQL, вместо вычисляемого столбца можно применить классический триггер BEFORE INSERT OR UPDATE, который перед сохранением строки будет выполнять присвоение NEW.id := NEW.id_text::uuid;.

Способ 2. Использование представления (VIEW) и триггера INSTEAD OF INSERT

Если структура физической таблицы в PostgreSQL уже зафиксирована и менять её нельзя (поле id уже объявлено как uuid и используется внешними системами), настроим работу через представление.

  1. Создадим представление над целевой таблицей в PostgreSQL, где колонка с идентификатором принудительно приводится к тексту:
    
    CREATE OR REPLACE VIEW v_test AS 
    SELECT 
        id::text AS id_str,
        name
    FROM test;
    
  2. Создадим триггерную функцию и триггер INSTEAD OF INSERT на это представление:
    
    CREATE OR REPLACE FUNCTION trg_v_test_insert()
    RETURNS TRIGGER AS $$
    BEGIN
        INSERT INTO test (id, name)
        VALUES (NEW.id_str::uuid, NEW.name);
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER trg_v_test_instead_of_insert
    INSTEAD OF INSERT ON v_test
    FOR EACH ROW
    EXECUTE FUNCTION trg_v_test_insert();
    
  3. В 1С подключим через внешний источник данных представление v_test. Поле id_str настраиваем как строку длиной 36 символов. Запись производим привычным объектным способом через вызов метода СоздатьОбъект().

Способ 3. Вызов хранимой процедуры или функции PostgreSQL из 1С

Механизм внешних источников данных 1С позволяет не только подключать таблицы, но и импортировать функции СУБД (раздел «Функции» внешнего источника данных в конфигураторе).

Рассмотрим реализацию через функцию:

  1. В PostgreSQL создадим функцию добавления записи, которая принимает строковый параметр и выполняет явное приведение к типу uuid:
    
    CREATE OR REPLACE FUNCTION add_test_record(p_id text, p_name text)
    RETURNS void AS $$
    BEGIN
        INSERT INTO test (id, name)
        VALUES (p_id::uuid, p_name);
    END;
    $$ LANGUAGE plpgsql;
    
  2. В дереве конфигурации 1С добавим эту функцию во внешний источник данных. Все параметры на стороне 1С будут иметь тип Строка.
  3. Вызовем функцию из кода 1С:
    
    ИдентификаторСтрокой = Строка(Товар.УникальныйИдентификатор());
    НаименованиеТовара = Товар.Наименование;
    
    ВнешниеИсточникиДанных.PostgreSQL.Функции.add_test_record(
        ИдентификаторСтрокой, 
        НаименованиеТовара
    );
    

Способ 4. Прямой SQL-запрос через COMОбъект (ADODB)

Если сервер 1С работает под управлением операционной системы Windows, а архитектура не требует строгого использования объектов внешнего источника данных, можно выполнить прямой SQL-запрос к PostgreSQL через ADO.

Посмотрим на пример выполнения вставки:


Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.ConnectionString = "Driver={PostgreSQL Unicode};Server=127.0.0.1;Port=5432;Database=my_db;Uid=postgres;Pwd=secret;";
Соединение.Open();

ИдентификаторСтрокой = Строка(Товар.УникальныйИдентификатор());
НаименованиеТовара = СтрЗаменить(Товар.Наименование, "'", "''");

ТекстЗапроса = "INSERT INTO test (id, name) VALUES ('" + ИдентификаторСтрокой + "'::uuid, '" + НаименованиеТовара + "');";
Соединение.Execute(ТекстЗапроса);

Соединение.Close();

При таком подходе приведение типов '...'::uuid указывается прямо в теле запроса, благодаря чему PostgreSQL корректно валидирует и сохраняет строковый идентификатор из 1С в системный тип UUID.

← На главную