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
или строку.
Проблема двоякая:
В отличие от локальных переменных , пользовательские переменные MySQL поддерживают очень ограниченный набор типов данных:
Пользовательским переменным может быть присвоено значение из ограниченного набора типов данных: целочисленная, десятичная, с плавающей точкой, двоичная или недвоичная строка или значение
NULL
.
В документации не упоминается, что используемые фактические типы данных являются соответственно BIGINT
, DECIMAL(65,30)
, DOUBLE
, LONGBLOB
, LONGTEXT
и LONGBLOB
. Что касается последнего, руководство по крайней мере объясняет:
Если вы ссылаетесь на переменную, которая не была инициализирована, она имеет значение NULL и тип строки.
Хранение первых трех этих типов данных (то есть для целых, десятичных и плавающих значений) требует 8, 30 и 8 байтов соответственно. Другие типы данных (т. NULL
значений строк и NULL
) требуют (до) 4 гигабайт памяти.
Так как вы используете версию 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 использовался по умолчанию.