PHP mysql_stmt :: fetch () дает устаревшую память ошибок PHP

CentOS 6.4 PHP 5.3.3 MySQL 5.1.69 x86_64

mysql_stmt::fetch() 

При выполнении выборки с помощью подготовленного оператора PHP дает ошибку: PHP Неустранимая ошибка: допустимый размер памяти 134217728 байт исчерпан (пытался выделить 4294967296 байт).

Это происходит, когда переменная, включенная в инструкцию 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"; 

Вы обнаружите, что это происходит только тогда, когда @status имеет NULL или строку.

Проблема двоякая:

  1. В отличие от локальных переменных , пользовательские переменные MySQL поддерживают очень ограниченный набор типов данных:

    Пользовательским переменным может быть присвоено значение из ограниченного набора типов данных: целочисленная, десятичная, с плавающей точкой, двоичная или недвоичная строка или значение NULL .

    В документации не упоминается, что используемые фактические типы данных являются соответственно BIGINT , DECIMAL(65,30) , DOUBLE , LONGBLOB , LONGTEXT и LONGBLOB . Что касается последнего, руководство по крайней мере объясняет:

    Если вы ссылаетесь на переменную, которая не была инициализирована, она имеет значение NULL и тип строки.

    Хранение первых трех этих типов данных (то есть для целых, десятичных и плавающих значений) требует 8, 30 и 8 байтов соответственно. Другие типы данных (т. NULL значений строк и NULL ) требуют (до) 4 гигабайт памяти.

  2. Так как вы используете версию PHP до v5.4.0, то по умолчанию MySQL-драйвер – libmysql , с которым на сервере привязаны только метаданные типа столбца, поэтому MySQLi пытается выделить достаточное количество памяти для хранения всех возможных значений (даже если полный буфер в конечном счете не требуется); таким образом, NULL – и переменные пользовательские переменные с максимальным размером 4GiB приводят к тому, что PHP превысит лимит памяти по умолчанию (128MiB с PHP v5.2.0).

Ваши варианты включают:

  • Переопределение типа данных столбца в определении таблицы:

     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() перед 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'); 
  • Перекомпиляция PHP, настроенная на использование собственного драйвера mysqlnd (включенного в PHP с версии v5.3.0, но не настроенного по умолчанию до версии PHP v5.4.0) вместо libmysql:

     ./configure --with-mysqli=mysqlnd 
  • Переход на PHP v5.4.0 или новее, чтобы mysqlnd использовался по умолчанию.