web-dev-qa-db-ja.com

'PDOException'とメッセージ 'SQLSTATE [22001]:文字列データ、右切り捨て:0

注:odbc _ * を使用してステートメントを正常に準備および実行できるため、この問題を具体的にPDOに絞り込みました。関数。

このパラメーターを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());

WebサーバーはPHP 5.5.9 onLinux Ubuntu 14.04with ODBC SQL Server用のドライバー13およびWindowsServer 2012上のMicrosoft SQL 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());

すべてのパスで同じエラーが発生します。

文字列データ、右切り捨て

25
Jeff Puckett

残念ながら、

これはPDO_ODBC 64ビットの非互換性の問題( #61777#64824 )であり、間違いなく64ビットビルドを使用しています。パラメータをバインドします。

幸いにも、

パッチがあります 5.6リリースに最初に含まれたもの:

このバグは #61777 でも参照されており、5.5ブランチの最新の安定版リリースにも引き続き存在します。この問題にはすでに2つのチケットが存在するようですが、x64ビルドでPDO_ODBCを使用している人にとっては深刻な問題であることを思い出させるために、これらの変更をgithub経由で送信しています。

出荷されたPHPのPDO_ODBCの何が問題になっていますか?

それらの推奨パッチの1つを見ると:

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ビット符号付き整数)だけで、これはnew ODBC type SQLULEN、つまり 64 64ビットのビットODBCアプリケーションおよび32ビットの32ビットODBCアプリケーション

コミッターは、次の行でcolsizeが適切に定義されているため、SQLLENデータ型を認識していなかったと思います。

私は今どうすればいい?

  1. PHPバージョン> = 5.6にアップグレード
  2. odbc_*機能を実用的なソリューションとして使用してください。
  3. 提供されたパッチを使用してa PHP v5.5.9をコンパイルします。
  4. @GordonMの推奨に従って、独自のPDOラッパーを作成します
22
revo

これはおそらくあなたが聞きたいものではありませんが、これにはPHPのODBC PDO用ドライバー(PHP)としてあまり使用されていない)のバグのすべての特徴があります=プログラマーは、商用製品よりもMySQL/SQLite/Postgresのようなオープンソースデータベースを好む傾向があります)、または基盤となるSQLサーバードライバー(同様の理由でLinuxではサポートが不十分です)ですが、odbc_ *が機能する場合は、おそらく基盤ではありません運転者。

DSNとして"sqlite::memory:"を使用することを除いて、まったく同じタスクを実行しようとすると、すべての例が機能します。これにより、何か間違ったことをしている可能性はほとんどありません(MS Serverに、私が知らない本当に奇妙な不適合SQL構文がある場合を除く)。 ATTR_EMULATE_PREPARESが有効と無効の両方の場合、SQLiteであなたの例はうまく機能します。

あなたが現実的にできると思うのは バグレポートを提出する そして誰かがそれを拾うことを願っています。しかし、あなたは長い間待っているかもしれません。

あなたの問題に対する実際的な解決策として、あなたの選択肢は、a)PHPサポートするDBMSに切り替えるか、b)プリペアドステートメントの代わりにSQL文字列構築に頼り、回避 SQLインジェクション攻撃 自分自身。ただし、これは最後の手段と見なす必要があります。 PDO :: quote() を使用すると役立つ場合がありますが、データも完全に検証されていることを確認します。これは理想的なソリューションではないことはわかっていますが、MS SQLを使用する必要があり、PHPチームがバグ修正するのを待つことができない場合は、選択肢があまりないことがわかります。 。

または、オプションc)が機能する場合は、代わりにodbc_ *関数を使用します。もちろん、OOPスタイルを使用する場合は、手続き型odbc関数をOOメソッドでラップする独自のクラスを実装する必要があります。大変な作業になります。

[〜#〜] edit [〜#〜]:私は見つけました Stack Overflowに関する別の質問 質問者が持っているようです同様の問題。彼の解決策は、FreeTDSを支持して「公式」MSドライバーを捨てることでした。これは暗闇の中でのショットのようなものかもしれませんが、試してみる価値があるかもしれません。

4
GordonM