Использование типизированных связанных параметров с PHP PDO-ODBC, unixODBC и FreeTDS

Я использую следующую настройку для доступа к базе данных MS-SQL из приложения PHP

  • RedHat Enterprise Linux 5
  • PHP 5.2.14 с PDO и PDO_ODBC
  • unixODBC 2.2.11
  • FreeTDS 0.82.1.dev.20100810

Непараметризованные запросы работают нормально. Единственной проблемой является принудительное закрытие курсора на отдельных операторах результата (с PDOStatment::closeCursor), чтобы избежать ошибок "0 [FreeTDS][SQL Server] Недопустимое состояние курсора (SQLSTATE=24000)".

Но у меня есть серьезная проблема с типизированным связанным параметром. При использовании такого кода:

$stmt = $PDO->prepare('INSERT INTO table (column1, column2)  VALUES (:foo, :bar');
$stmt->bindValue(':foo', 21, PDO::PARAM_INT);
$stmt->bindValue(':bar', 42, PDO::PARAM_INT);
$stmt->execute():
if (!$stmt->execute()) {
 var_dump($stmt->errorInfo();
}

Где оба столбца INT. Я получаю сообщение об ошибке "206 [FreeTDS][SQL Server] операнд типа: текст несовместим с int [SQLSTATE=22018]" ошибка.

В журнале unixODBC я получаю что-то вроде

[ODBC][26251][SQLDescribeParam.c][175]
              Entry:
                      Statement = 0x2b73c849fb80
                      Parameter Number = 1
                      SQL Type = 0x7fff9c89e15e
                      Param Def = 0x7fff9c89e154
                      Scale = 0x7fff9c89e15c
                      Nullable = 0x7fff9c89e15a
[ODBC][26251][SQLDescribeParam.c][276]Error: IM001
[ODBC][26251][SQLBindParameter.c][193]
              Entry:
                      Statement = 0x2b73c849fb80
                      Param Number = 1
                      Param Type = 1
                      C Type = 1 SQL_C_CHAR
                      SQL Type = -1 SQL_LONGVARCHAR
                      Col Def = 4000
                      Scale = 5
                      Rgb Value = 0x2b73c941f890
                      Value Max = 0
                      StrLen Or Ind = 0x2b73c93fa1b0
[ODBC][26251][SQLBindParameter.c][339]
              Exit:[SQL_SUCCESS]

Мое понимание журнала заключается в том, что unixODBC пытается связать параметры, используя правильный тип. Но FreeTDS не поддерживает эту функцию (IM001 - "Драйвер не поддерживает эту функцию"). Так что unixODBC продолжайте без правильного набора текста.

Может кто-нибудь подтвердить этот диагноз или, что лучше, известную проблему с типизированным связанным параметром в FreeTDS? Если да, работают ли они с использованием PHP PDO, и как я могу его настроить?

3 ответа

Решение

В списке рассылки FreeTDS я получил подтверждение, что SQLDescribeParam не поддерживается во FreeTDS. Но когда SQLDescribeParam не поддерживается, PDO_ODBC виноват в использовании LONGVARCHAR (т. Е. Текст).

Тот же код работал на рабочей станции Windows с PDO ODBC (PHP версии 5.2.9, библиотека ODBC Win32)

Обходной путь для этой проблемы - обрабатывать каждый параметр как LONGVARCHAR и использовать явное преобразование типов в запросах. MS SQL Server поддерживает только преобразования LONGVARCHAR => *CHAR. Чтобы преобразовать, я должен был использовать такую ​​вещь, как CAST(CAST(:number AS varchar) AS INTEGER) или же CAST(CAST(:birthdate AS varchar) AS datetime), Это плохо, некрасиво и, вероятно, подорвать производительность, но это работает.

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

Я смог решить эту проблему, изменив мою версию TDS с 7.1 на 7.2. Я больше не испытываю никаких проблем.

Надеюсь, это поможет!

В моем случае у меня был вызов хранимой процедуры, которая возвращает записи... но с некоторыми вставками в нее... Мне удалось заставить ее работать, добавив:

SET NOCOUNT ON

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

Выполнение ролей, как вы сказали, не сработало для меня.

Пример того, как воспроизвести поведение:

create procedure sptest 
    @var int
as
begin 
    create table #tmp (var int)
    insert into #tmp values (@var)
    select * from #tmp
end

И код PHP:

$query = "exec sptest @var=:var";
$dbh = new PDO (SQLSVR_DRIVER);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$rs = $dbh->prepare($query);
$rs->bindParam('var', 1, PDO::PARAM_INT);
$rs->execute();
while ($row = $rs->fetch(PDO::FETCH_ASSOC))
    var_dump($row);

Исправление:

create procedure sptest 
    @var int
as
begin 
    set nocount on
    create table #tmp (var int)
    insert into #tmp values (@var)
    select * from #tmp
end

Это оно!

Другие вопросы по тегам