Подтвердить что ты не робот

"PDOException" с сообщением "SQLSTATE [22001]: данные строки, усеченные справа: 0

ПРИМЕЧАНИЕ: Я сузил эту проблему до конкретного PDO, потому что я могу успешно подготовить и выполнить инструкции, используя odbc_ *.

Почему я не могу привязать этот параметр к подготовленному оператору PDO?

Это работает:

$mssqldriver = 'ODBC Driver 13 for SQL Server';
$pdoDB = new PDO("odbc:Driver=$mssqldriver;Server=$hostname;Database=$dbname", $username, $password);
$pdoDB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

$sql = "SELECT 'value' AS col where 'this' = 'this'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [col] => value [0] => value )

Не работает:

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['this'];
$stmt->execute($params);
print_r($stmt->fetch());

Веб-сервер работает под управлением PHP 5.5.9 на Linux Ubuntu 14.04 с помощью драйвера ODBC 13 для SQL Server и подключения к Microsoft SQL Server 2012 на Windows Server 2012

Здесь полная ошибка:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[22001]:
String data, right truncated: 0
[Microsoft][ODBC Driver 13 for SQL Server]
String data, right truncation
(SQLExecute[0] at /build/buildd/php5-5.5.9+dfsg/ext/pdo_odbc/odbc_stmt.c:254)' in /var/www/scratch.php:46
Stack trace:
#0 /var/www/scratch.php(46): PDOStatement->execute(Array)
#1 {main} thrown in /var/www/scratch.php on line 46

Я также попробовал установку:

$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );

И используя именованные параметры:

$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = ['myVal' => 'this'];
$stmt->execute($params);
print_r($stmt->fetch());

Даже с явным двоеточием:

$params = [':myVal' => 'this'];

Я также попытался использовать bindParam, как показано в этом ответе

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());

Как и с именованными параметрами:

$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(':myVal', $param, PDO::PARAM_STR);
$stmt->execute();
print_r($stmt->fetch());

Если я попытаюсь явно задать длину:

$stmt->bindParam(':myVal', $param, PDO::PARAM_STR, 4);

Я получаю бонусную ошибку:

Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[42000]: Syntax error or access violation: 102
[Microsoft][ODBC Driver 13 for SQL Server][SQL Server]
Incorrect syntax near 'OUTPUT'.

И да, все это тривиализованный пример без таблиц, чтобы вы могли легко воспроизвести его, но, чтобы быть уверенным, я действительно пробовал это с реальной таблицей.

CREATE TABLE myTable (
    id INT IDENTITY PRIMARY KEY,
    val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES ('hello world');

Работает:

$sql = "SELECT * FROM myTable WHERE val = 'hello world'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [id] => 1 [0] => 1 [val] => hello world [1] => hello world )

Не работает:

$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['hello world'];
$stmt->execute($params);
print_r($stmt->fetch());

Все пути приводят к одной и той же ошибке:

Строковые данные, усеченные справа

4b9b3361

Ответ 1

К сожалению,

Это a PDO_ODBC проблема 64-разрядной несовместимости (# 61777, # 64824) и без каких-либо сомнений вы находитесь в 64-битной сборке, которая не позволяет вам связывать параметры.

К счастью,

У него есть патч, который был впервые включен в релиз 5.6:

Эта ошибка также упоминается в # 61777 и по-прежнему присутствует в последнем стабильном выпуске ветки 5.5. Я вижу два билета для этой проблемы уже есть, и я просто отправляю эти изменения через github как напоминание о том, что это серьезная проблема для любого используя PDO_ODBC в сборках x64.

Что не так с вашим PHP отправленным PDO_ODBC?

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

diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index 8b0ccf3..1d275cd 100644
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -551,7 +551,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
    struct pdo_column_data *col = &stmt->columns[colno];
    RETCODE rc;
    SWORD   colnamelen;
-   SDWORD  colsize;
+   SQLULEN colsize;
    SQLLEN displaysize;

Мы видим, что единственное, что изменилось, это SDWORD (16-разрядное целое число со знаком), которое заменяется новым типом ODBC SQLULEN, который 64 бит в 64-разрядном приложении ODBC и 32 бита в 32-разрядном приложении ODBC.

Я считаю, что коммиттер не знал о типе данных colsize только потому, что в самой следующей строке SQLLEN правильно определено.

Что мне теперь делать?

  • Обновление до версии PHP >= 5.6
  • Придерживайтесь odbc_* функций в качестве рабочего решения.
  • Скомпилируйте PHP v5.5.9 с предоставленными патчами.
  • Создайте собственную оболочку PDO в соответствии с рекомендациями @GordonM

Ответ 2

Это, вероятно, не то, что вы хотите услышать, но у этого есть все признаки ошибки в PHP ODBC-драйвере для PDO (который не используется в основном как программисты PHP, как правило, предпочитают базы данных с открытым исходным кодом, такие как MySQL/SQLite/Postgres over коммерческие предложения) или в базовом драйвере SQL-сервера (который плохо поддерживается в Linux по аналогичным причинам), хотя, если odbc_ * работает, то это, вероятно, не основной драйвер.

Если вы попытаетесь выполнить одни и те же задачи, кроме использования "sqlite::memory:" в качестве DSN, все ваши примеры будут работать. Это делает крайне маловероятным, что вы делаете что-то неправильно (если у MS Server нет действительно странного несоответствующего синтаксиса SQL, о котором я не знаю). Ваши примеры отлично подходят для меня с SQLite, когда ATTR_EMULATE_PREPARES включен и отключен.

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

Что касается практических решений вашей проблемы, то ваши варианты: a) переход на СУБД, поддерживаемый PHP, или b) использование строковой строкой SQL вместо подготовленных операторов и готовность принять бремя избежания SQL-инъекции самостоятельно. Это следует считать последним средством! Использование PDO:: quote() может помочь, но я также убедился бы, что ваши данные очень тщательно проверены. Я знаю, что это не идеальное решение, но если вы должны использовать MS SQL и не можете дождаться, когда команда PHP будет исправлена, то я действительно не вижу, что у вас есть большой выбор.

Или там опция c), которая вместо этого использует функции odbc_ *, если они работают. Конечно, если вы хотите использовать стиль ООП, тогда вам придется реализовать свой собственный класс, который обертывает процедурные функции odbc в OO-методах, поэтому потенциально может быть много работы.

РЕДАКТИРОВАТЬ: я нашел еще один вопрос о переполнении стека, где, похоже, проблема с аналогичным вопросом. Его решение заключалось в том, чтобы вырезать "официального" MS-драйвера в пользу FreeTDS. Это может быть что-то вроде выстрела в темноте, но это может стоить попробовать.