При настройке интеграции между 1С и сторонними базами данных под управлением PostgreSQL разработчики часто сталкиваются с задачей сопоставления ссылочных типов. В 1С ссылка на любой ссылочный объект имеет УникальныйИдентификатор (GUID), а в PostgreSQL стандартом хранения таких ключей является системный тип UUID. Однако при попытке записать значение в таблицу внешнего источника данных напрямую возникает ошибка ODBC.
Разберем подробнее, почему возникает эта ошибка и какими способами можно правильно передать идентификатор из 1С в PostgreSQL.
Посмотрим на типичный пример кода, который вызывает сбой при использовании внешнего источника данных:
НоваяЗаписьSQL = ВнешниеИсточникиДанных.PostgreSQL.Таблицы.Test.СоздатьОбъект();
НоваяЗаписьSQL.name = Товар.Наименование;
НоваяЗаписьSQL.ID = Товар.УникальныйИдентификатор();
НоваяЗаписьSQL.Записать();
При выполнении этого кода платформа возвращает ошибку:
Ошибка ODBC. SQLSTATE: 22P02. Номер ошибки: 1. Описание: ОШИБКА: неверный синтаксис для типа uuid: "\xB0F3627878424FC048B8BE10D058F90D"
Выясним причину такого поведения:
УникальныйИдентификатор платформа передает его драйверу в виде двоичного буфера фиксированной длины (16 байт). Драйвер psqlODBC сопоставляет такие бинарные данные с типом bytea.bytea в uuid. Сервер СУБД получает байтовую последовательность в шестнадцатеричном виде с префиксом \x и не может автоматически сконвертировать её в целевую колонку uuid.Строка и передавать строковое представление идентификатора через функцию Строка(Товар.УникальныйИдентификатор()), то при генерации параметризованного запроса СУБД также может выдать ошибку синтаксиса или несоответствия типов, так как драйвер не выполняет явного приведения ::uuid для параметров команды INSERT.bytea в PostgreSQL, важно помнить, что 1С хранит байты идентификатора со смещением групп по сравнению со стандартом RFC 4122 (для оптимизации индексирования по времени). Поэтому простое преобразование бинарного представления без перестановки байтов приведет к искажению оригинального строкового GUID.Проанализируем практические способы решения этой проблемы.
Наиболее надежный и производительный способ решения задачи на стороне базы данных PostgreSQL — принимать идентификатор из 1С в виде обычной текстовой строки, а итоговый UUID формировать автоматически средствами самой СУБД.
Начиная с версии PostgreSQL 12, в таблицах поддерживаются вычисляемые столбцы (GENERATED ALWAYS AS ... STORED). Разберем шаги настройки:
uuid для связей и индексов:
ALTER TABLE test ADD COLUMN id_text varchar(36);
ALTER TABLE test ADD COLUMN id uuid GENERATED ALWAYS AS (id_text::uuid) STORED;
id_text с типом Строка (длина 36).
НоваяЗаписьSQL = ВнешниеИсточникиДанных.PostgreSQL.Таблицы.Test.СоздатьОбъект();
НоваяЗаписьSQL.name = Товар.Наименование;
НоваяЗаписьSQL.id_text = Строка(Товар.УникальныйИдентификатор());
НоваяЗаписьSQL.Записать();
Если используется более ранняя версия PostgreSQL, вместо вычисляемого столбца можно применить классический триггер BEFORE INSERT OR UPDATE, который перед сохранением строки будет выполнять присвоение NEW.id := NEW.id_text::uuid;.
Если структура физической таблицы в PostgreSQL уже зафиксирована и менять её нельзя (поле id уже объявлено как uuid и используется внешними системами), настроим работу через представление.
CREATE OR REPLACE VIEW v_test AS
SELECT
id::text AS id_str,
name
FROM test;
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();
v_test. Поле id_str настраиваем как строку длиной 36 символов. Запись производим привычным объектным способом через вызов метода СоздатьОбъект().Механизм внешних источников данных 1С позволяет не только подключать таблицы, но и импортировать функции СУБД (раздел «Функции» внешнего источника данных в конфигураторе).
Рассмотрим реализацию через функцию:
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;
Строка.
ИдентификаторСтрокой = Строка(Товар.УникальныйИдентификатор());
НаименованиеТовара = Товар.Наименование;
ВнешниеИсточникиДанных.PostgreSQL.Функции.add_test_record(
ИдентификаторСтрокой,
НаименованиеТовара
);
Если сервер 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.