Как перехватить запрос из 1С к СУБД Oracle и исправить ошибку PLS-00306

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

При интеграции систем 1С:Предприятие с внешними базами данных под управлением СУБД Oracle разработчики часто используют технологию ADO (COM-объект ADODB.Command). Это гибкий и быстрый инструмент, однако при его использовании нередко возникают трудночитаемые ошибки взаимодействия. Одна из наиболее распространенных ошибок — ORA-06550 / PLS-00306: ошибочно число или типы аргументов при обращении к хранимой процедуре.

В этой статье мы подробно разберем, почему возникает эта проблема, как перехватить «невидимый» запрос, который 1С отправляет в СУБД Oracle, и как правильно настроить типы данных в коде 1С, чтобы интеграция работала стабильно.

Почему возникает ошибка PLS-00306 и почему ораклисты просят «текст запроса»

Когда в коде 1С свойству CommandType COM-объекта ADODB.Command присваивается значение 4 (что соответствует константе adCmdStoredProc), это указывает драйверу на вызов хранимой процедуры напрямую. В этом случае драйвер не формирует классический текстовый запрос типа SELECT ... FROM ....

Вместо этого драйвер ODBC/OLE DB упаковывает вызов в удаленный вызов процедуры (RPC) или в анонимный блок PL/SQL, который выглядит примерно так:


begin
    MMAST.F1CPK.PAYMENTFM(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10);
end;

Администраторы и разработчики СУБД Oracle не могут увидеть исходный код на стороне 1С и зачастую не могут понять, какие именно данные и в каком формате туда приходят. Ошибка PLS-00306 однозначно говорит о том, что сигнатура вызова со стороны клиента (количество параметров, их последовательность или типы данных) не соответствует описанию процедуры в самой базе данных Oracle. Чтобы разобраться, на каком именно параметре происходит сбой, нам необходимо перехватить передаваемый пакет.

---

Методики перехвата запросов от 1С к Oracle

Рассмотрим несколько эффективных способов, которые помогут получить точный текст запроса и значения параметров, передаваемых из 1С в СУБД.

Способ 1. Трассировка ODBC на стороне клиента Windows

Это самый простой способ, который не требует от вас прав администратора в СУБД Oracle и выполняется непосредственно на рабочем компьютере или сервере, где запущена 1С.

  1. Откройте утилиту «Администратор источников данных ODBC». Обратите внимание на разрядность вашей платформы 1С: если у вас 64-разрядный клиент/сервер 1С, запустите 64-битную версию утилиты (odbcad32.exe из папки System32). Если 32-разрядный — запустите 32-битную версию из папки SysWOW64.
  2. Перейдите на вкладку «Трассировка» (Tracing).
  3. В поле «Путь к файлу журнала трассировки» укажите удобный путь (например, C:\temp\SQL.LOG). Убедитесь, что у пользователя, под которым запущена 1С, есть права на запись в этот каталог.
  4. Нажмите кнопку «Начать трассировку» (Start Tracing Now).
  5. Запустите выгрузку данных в 1С до момента появления ошибки.
  6. Вернитесь в администратор ODBC и нажмите «Остановить трассировку» (Stop Tracing Now).

В созданном лог-файле SQL.LOG будут построчно записаны все обращения к драйверу. Проанализировав его, мы увидим вызовы функций SQLBindParameter и SQLExecute, в которых будут четко видны передаваемые типы и значения параметров.

Способ 2. Клиентская трассировка SQL*Net (Oracle Client)

Если для подключения к базе данных используется полноценный Oracle Client (или Instant Client), мы можем настроить логирование на уровне сетевого уровня Oracle.

Для этого найдите файл конфигурации sqlnet.ora (обычно он находится в каталоге %ORACLE_HOME%\network\admin) и добавьте в него следующие строки:


TRACE_LEVEL_CLIENT = 16
TRACE_FILE_CLIENT = sql_client.trc
TRACE_DIRECTORY_CLIENT = C:\oracle_trace

После выполнения интеграционного кода в указанной папке появится подробный лог сетевого взаимодействия. В нем можно будет найти точный текст анонимного блока PL/SQL, который драйвер отправляет на сервер.

Способ 3. Серверная трассировка сессии (для администраторов СУБД)

Если разработчики Oracle утверждают, что не могут перехватить запрос, они ошибаются. В СУБД Oracle есть штатные инструменты для отслеживания сессий. Мы можем попросить их включить трассировку конкретно для нашей сессии подключения.

Для этого администратор Oracle должен выполнить следующую последовательность действий:

  1. Найти идентификатор сессии 1С (параметры SID и SERIAL#) в системном представлении V$SESSION.
  2. Активировать детальную трассировку сессии с записью значений связанных переменных (bind variables):
    
    EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE(session_id => [Ваш_SID], serial_num => [Ваш_SERIAL], binds => TRUE, waits => TRUE);
    
  3. После того как 1С выполнит код и получит ошибку, администратор отключает трассировку и анализирует полученный файл формата .trc на сервере СУБД с помощью утилиты tkprof. Там будет виден и текст вызова, и все переданные 1С значения.

---

Анализ кода 1С и исправление ошибок несовместимости типов

Давайте подробно проанализируем исходный код выгрузки и выясним, почему СУБД Oracle возвращает ошибку PLS-00306 при работе с типами данных.

Важный нюанс: Порядок параметров в ADO

При вызове хранимых процедур через ADODB параметры связываются строго по порядку (позиционно), а не по их именам. Имя параметра, передаваемое первым аргументом в метод CreateParameter, драйвером Oracle игнорируется. Если в СУБД процедура PAYMENTFM ожидает параметры в одном порядке, а в 1С мы добавляем их методом Append в другом — СУБД вернет ошибку типов данных. Необходимо запросить у разработчиков Oracle точную спецификацию процедуры и проверить последовательность параметров.

Сопоставление типов данных: ADO против Oracle

Проанализируем конкретные строки создания параметров в исходном коде 1С:

  1. Параметры дат (Param5): В коде написано: Param5 = Command.CreateParameter("", 7, 1, 4);.

    Здесь тип 7 соответствует константе adDate. Однако СУБД Oracle хранит в типе DATE также и время (часы, минуты, секунды). При передаче типа adDate драйвер может некорректно преобразовать дату в строку или передать ее без временной части, что вызывает конфликт. Правильным решением для передачи даты и времени в Oracle является использование типа 135 (adDBTimeStamp).

  2. Параметры чисел (Param1, Param7, Param8):

    Для Param1 (идентификатор счета) в коде указан тип 3 (adInteger). Если в Oracle данное поле имеет тип NUMBER с большой разрядностью, стандартный 32-битный Integer может вызвать переполнение или ошибку приведения. Безопаснее использовать тип 131 (adNumeric).

    Для сумм (Param7, Param8) указан тип 5 (adDouble). Этот тип представляет числа с плавающей запятой двойной точности. В финансовых операциях СУБД Oracle обычно использует точный тип NUMBER(15,2). Передача double-чисел часто приводит к ошибке PLS-00306 из-за неявного преобразования. Здесь также настоятельно рекомендуется использовать тип 131 (adNumeric).

  3. Строковые параметры и Юникод (Param2, Param3, Param4, Param6, Param10):

    В коде используется тип 200 (adVarChar) — это строка в кодировке ANSI. Платформа 1С работает исключительно с кодировкой Unicode. Если СУБД Oracle настроена на хранение национальных символов (типы параметров NVARCHAR2), передача ANSI-строки вызовет ошибку. Рекомендуется заменить тип 200 на 202 (adVarWChar), предназначенный для Юникод-строк.

---

Рефакторинг кода 1С

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


// Определим константы типов данных ADO для наглядности
adInteger = 3;
adDouble = 5;
adDate = 7;
adNumeric = 131;
adDBTimeStamp = 135;
adVarChar = 200;
adVarWChar = 202;
adParamInput = 1;

DataBaseConnection = ПодключениеКОракл();
Command = Новый COMObject("ADODB.Command"); 

Command.CommandText = "MMAST.F1CPK.PAYMENTFM"; 
Command.ActiveConnection = DataBaseConnection;
Command.CommandType = 4; // adCmdStoredProc

// Создаем параметры с корректными типами данных
// Важно: порядок добавления должен строго соответствовать порядку аргументов в СУБД Oracle!

Param1 = Command.CreateParameter("p_id_schet", adNumeric, adParamInput); // NUMBER в Oracle
Param2 = Command.CreateParameter("p_doc_uid", adVarWChar, adParamInput, 36); // VARCHAR2(36)
Param3 = Command.CreateParameter("p_doc_num", adVarWChar, adParamInput, 20); // VARCHAR2(20)
Param4 = Command.CreateParameter("p_source", adVarWChar, adParamInput, 25); // VARCHAR2(25)
Param5 = Command.CreateParameter("p_pay_date", adDBTimeStamp, adParamInput); // DATE в Oracle (с временем)
Param6 = Command.CreateParameter("p_currency", adVarWChar, adParamInput, 20); // VARCHAR2(20)
Param7 = Command.CreateParameter("p_sum_cur", adNumeric, adParamInput); // NUMBER (валютная сумма)
Param8 = Command.CreateParameter("p_sum_rub", adNumeric, adParamInput); // NUMBER (рублевая сумма)
Param9 = Command.CreateParameter("p_clear_flag", adInteger, adParamInput); // NUMBER / INTEGER
Param10 = Command.CreateParameter("p_invoice_uid", adVarWChar, adParamInput, 36); // VARCHAR2(36)

// Настраиваем точность для числовых параметров (для типа adNumeric это критично)
Param1.Precision = 15;
Param1.NumericScale = 0;
Param7.Precision = 15;
Param7.NumericScale = 2; // 2 знака после запятой
Param8.Precision = 15;
Param8.NumericScale = 2; // 2 знака после запятой

// Добавляем параметры в коллекцию команды
Command.Parameters.Append(Param1);
Command.Parameters.Append(Param2);
Command.Parameters.Append(Param3);
Command.Parameters.Append(Param4);
Command.Parameters.Append(Param5);
Command.Parameters.Append(Param6);
Command.Parameters.Append(Param7);
Command.Parameters.Append(Param8);
Command.Parameters.Append(Param9);
Command.Parameters.Append(Param10);

// Заполняем значениями с предварительной проверкой типов
Param1.Value = ?(ЗначениеЗаполнено(ИдСчета), Число(ИдСчета), NULL);
Param2.Value = Строка(ДокументОплаты.УникальныйИдентификатор());
Param3.Value = ?(ЭтоБанк, СокрЛП(ДокументОплаты.НомерВходящегоДокумента), СокрЛП(ДокументОплаты.Номер));
Param4.Value = СокрЛП(ИсточникПоступления);

// Работа с пустой датой 1С (01.01.0001 вызывает ошибку в Oracle)
Если ЗначениеЗаполнено(ДатаОплаты) Тогда
    Param5.Value = ДатаОплаты;
Иначе
    Param5.Value = NULL;
КонецЕсли;

Param6.Value = СокрЛП(КодВалюты);
Param7.Value = Число(СуммаВВалюте);
Param8.Value = Число(Сумма);
Param9.Value = Число(Очищать);
Param10.Value = Строка(ДокументСчет.УникальныйИдентификатор());

// Выполняем процедуру внутри безопасного блока транзакции
Попытка
    Command.Execute();
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение "Ошибка выполнения операции в СУБД Oracle: " + ОписаниеОшибки();
КонецПопытки;

Подведем итоги

Для успешной интеграции 1С и СУБД Oracle с использованием COM-технологии ADO всегда придерживайтесь трех главных правил:

← На главную