При организации обмена данными между 1С и СУБД PostgreSQL через механизм COM-соединения ADO (ADODB.Command и ADODB.Connection) разработчики нередко сталкиваются со сбоем при передаче логических значений. Запрос на добавление данных завершается исключением: ERROR: column "grp" is of type boolean but expression is of type "char"; Error while preparing parameters.
Проанализируем ситуацию, выясним глубинную причину такого поведения драйверов и разберем по шагам несколько проверенных вариантов решения проблемы.
Посмотрим на типовой исходный код, вызывающий исключение при выполнении метода Execute():
cmd = Новый COMОбъект("ADODB.Command");
cmd.ActiveConnection = ADOConnection;
cmd.CommandText = "INSERT INTO product (id, name, grp) VALUES (?, ?, ?);";
cmd.Prepared = True;
cmd.Parameters.Append(cmd.CreateParameter("@id", 3, 1)); // adInteger
cmd.Parameters.Append(cmd.CreateParameter("@name", 130, 1, 128)); // adVarWChar
cmd.Parameters.Append(cmd.CreateParameter("@grp", 11, 1)); // adBoolean
cmd.Parameters.Item(0).Value = 1;
cmd.Parameters.Item(1).Value = "Товар 1";
cmd.Parameters.Item(2).Value = Истина;
cmd.Execute();
Рассмотрим, почему при абсолютно корректном указании типа adBoolean (константа 11) база данных отвергает параметр:
BoolsAsChar (значение 1). При активной настройке драйвер сопоставляет логические типы не с SQL_BIT или boolean, а с внутренним типом SQL_CHAR (символьный тип "char").cmd.Prepared установлено в True, провайдер Microsoft OLE DB Provider for ODBC Drivers (MSDASQL) запрашивает типизированную подготовку запроса на сервере. Сервер PostgreSQL получает жесткое указание, что в поле передается тип char.boolean и генерирует ошибку несовместимости типов.Самый правильный и системный способ — отключить приведение булевых значений к символам на уровне самого драйвера ODBC. Разберем, как это сделать:
BoolsAsChar=0; (или короткий алиас B9=0;):
СтрокаПодключения = "Driver={PostgreSQL Unicode};Server=127.0.0.1;Port=5432;Database=mydb;Uid=postgres;Pwd=secret;BoolsAsChar=0;";
ADOConnection.Open(СтрокаПодключения);
C:\Windows\SysWOW64\odbcad32.exe). Перейдем в свойства подключения (кнопка Datasource / вкладка Page 1) и снимем флажок Bools as Char.После отключения этого флага передаем в значение параметра стандартные булевы типы 1С без преобразования их в строки:
cmd.Parameters.Item(2).Value = (Сп.Получить(5).Значение <> "0");
Если изменить параметры ODBC-драйвера или строку подключения на сервере нет возможности, используем явное приведение типов (CAST) прямо в тексте запроса. Это позволит сохранить параметризацию и защитит запрос от некорректного толкования типов СУБД:
// Используем синтаксис CAST(? AS boolean) или ?::boolean
cmd.CommandText = "INSERT INTO product (id, name, grp) VALUES (?, ?, CAST(? AS boolean));";
В этом случае PostgreSQL самостоятельно преобразует переданное драйвером значение в целевой тип колонки таблицы.
Рассмотрим вариант, при котором драйвер сам подставляет литералы в тело команды перед отправкой запроса на сервер, избегая строгой типизации серверного плана:
Ложь:
cmd.Prepared = False;
UseServerSidePrepare=0; (или C8=0;).Если параметризованный запрос через ADO продолжает выдавать ошибки из-за особенностей провайдера MSDASQL, можно сформировать текст запроса динамически, подставляя логические константы true или false напрямую в тело команды SQL:
Для Каждого СтрокаТЗ Из АДО_Запрос Цикл
Сп = ИзСтрокиСРазделителями(СтрокаТЗ.Значения);
Ид = Число(Сп.Получить(0).Значение);
Наименование = СтрЗаменить(Сп.Получить(1).Значение, "'", "''");
ПризнакГруппы = ?(Сп.Получить(5).Значение = "0", "false", "true");
ТекстSQL = "INSERT INTO product (id, name, grp) VALUES ("
+ Формат(Ид, "ЧГ=0") + ", "
+ "'" + Наименование + "', "
+ ПризнакГруппы + ");";
Попытка
ADOConnection.Execute(ТекстSQL);
Исключение
Сообщить("Ошибка вставки записи: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Выполнение одиночных команд Execute() в цикле 1С существенно замедляет обмен, так как каждая операция по умолчанию выполняется в отдельной транзакции (режим автокоммита). Применим следующие оптимизации:
ADOConnection.BeginTrans() и зафиксируем ее после окончания цикла вызовом ADOConnection.CommitTrans(). В случае ошибки откатим изменения методом ADOConnection.RollbackTrans().INSERT INTO product (...) VALUES (...), (...), (...) порциями по 500–1000 строк.COPY product FROM 'путь_к_файлу' WITH (FORMAT csv, DELIMITER ';');.