5

CentOS 6.4 PHP 5.3.3 MySQL 5.1.69 x86_64

mysql_stmt::fetch()

プリペアド ステートメントを使用してフェッチを実行すると、PHP で次のエラーが発生します。

これは、一時テーブルの作成に使用される SELECT ステートメントに含まれる変数が設定解除されている場合に発生します。これは、ストアド プロシージャが呼び出される前に変数が環境に設定されているかどうかに関係なく発生します。変数は、ストアド プロシージャ内で設定する必要があります。SELECT ステートメントを使用して一時テーブルのデータを PHP に返し、PHP が mysql_stmt::fetch() を使用してデータにアクセスすると、PHP は上記の致命的なエラーを生成します。

MySQL コード:

DELIMITER $$
CREATE PROCEDURE test_sp()
BEGIN

    # uncomment below line, and PHP call to mysqli_stmt::fetch() works
    # SET @status = 1;

    # remove tmp table
    DROP TABLE IF EXISTS tmp_table;
    # CREATE TEMPORARY TABLE
    CREATE TEMPORARY TABLE tmp_table
        SELECT @status AS status;

    SELECT * FROM tmp_table;

END $$
DELIMITER ;

PHP コード:

// obtain MySQL login info
require_once(MYSQLOBJ);

// initialize status
$status = "";


$db = new mysqli(
    DB_HOST,
    DB_USER,
    DB_PASSWORD,
    DB_NAME
    );


$query = "CALL test_sp";

$stmt = $db->prepare($query);

$stmt->execute();

$stmt->bind_result( $status );

$stmt->store_result();

$stmt->fetch(); // PHP FATAL ERROR OCCURS HERE

$stmt->free_result();

$db->close();

print "<p>status = $status</p>\n";
4

1 に答える 1

6

これはisまたは string の場合にのみ発生することがわかります。@statusNULL

問題は 2 つあります。

  1. ローカル変数とは異なり、MySQLユーザー変数は非常に限られたデータ型のセットをサポートします。

    ユーザー変数には、整数、10 進数、浮動小数点、バイナリまたは非バイナリ文字列、またはNULL値など、限られたデータ型のセットから値を割り当てることができます。

    ドキュメントには、使用される実際のデータ型がそれぞれBIGINTDECIMAL(65,30)DOUBLE、およびLONGBLOBであることが記載されていません。最後のものに関しては、マニュアルでは少なくとも次のように説明されています。LONGTEXTLONGBLOB

    初期化されていない変数を参照すると、その値は NULL になり、型は文字列になります。

    これらのデータ型の最初の 3 つ (つまり、整数、10 進数、および浮動小数点値) の格納には、それぞれ 8、30、および 8 バイトが必要です。その他のデータ型 (つまり、文字列とNULL値) には、(最大で) 4 ギガバイトのストレージが必要です。

  2. v5.4.0 より前のバージョンの PHP を使用しているため、デフォルトの MySQL ドライバーはlibmysqlであり、データ バインディング時にサーバーから利用できるのは列型のメタデータのみです。完全なバッファが最終的に必要でない場合); したがってNULL、最大サイズが 4GiB の文字列値のユーザー変数により、PHP はデフォルトのメモリ制限 (PHP v5.2.0 以降では 128MiB) を超えます。

オプションは次のとおりです。

  • テーブル定義で列のデータ型をオーバーライドする:

    DROP TEMPORARY TABLE IF EXISTS tmp_table;
    CREATE TEMPORARY TABLE tmp_table (
      status VARCHAR(2)
    ) SELECT @status AS status;
    
  • ユーザー変数をより具体的なデータ型に明示的にキャストします。

    DROP TEMPORARY TABLE IF EXISTS tmp_table;
    CREATE TEMPORARY TABLE tmp_table
      SELECT CAST(@status AS CHAR(2)) AS status;
    
  • 明示的なデータ型で宣言されたローカル変数の使用:

    DECLARE status VARCHAR(2) DEFAULT @status;
    DROP TEMPORARY TABLE IF EXISTS tmp_table;
    CREATE TEMPORARY TABLE tmp_table
      SELECT status;
    
  • mysqli_stmt::store_result() before を呼び出すことで問題を回避しmysqli_stmt::bind_result()ます。これにより、結果セットが libmysql に格納され (PHP のメモリ制限外)、PHP はレコードを取得するときにレコードを保持するために必要な実際のメモリのみを割り当てます。

    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result( $status );
    $stmt->fetch();
    
  • PHP のメモリ制限を引き上げて、4GiB バッファの割り当てに対応できるようにする (ただし、そうすることによるハードウェア リソースへの影響を認識しておく必要があります)。たとえば、メモリの制約を完全に削除します (ただし、潜在的な悪影響に注意してくださいこれを行うことから、例えば本物のメモリリークから):

    ini_set('memory_limit', '-1');
    
  • libmysql の代わりにネイティブの mysqlnd ドライバー(PHP v5.3.0 以降に含まれていますが、PHP v5.4.0 まではデフォルトとして構成されていません) を使用するように構成された PHP を再コンパイルします。

    ./configure --with-mysqli=mysqlnd
    
  • mysqlnd がデフォルトで使用されるように、PHP v5.4.0 以降にアップグレードします。

于 2013-07-07T06:49:44.100 に答える