У меня есть этот пример кода:
<?php
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=test_sql',
'root',
'',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$pdo->query('DROP FUNCTION IF EXISTS tst');
$pdo->query('DROP PROCEDURE IF EXISTS tst2');
$pdo->query('CREATE FUNCTION tst() RETURNS VARCHAR(5) BEGIN RETURN \'x123456\'; END');
$pdo->query('CREATE PROCEDURE tst2() BEGIN SELECT tst(); END');
$st = $pdo->prepare('CALL tst2()');
try {
$st->execute();
} catch (Throwable $ex) {
var_dump($ex->getMessage());
$st->closeCursor(); // same with unset($st)
var_dump('This is never executed');
}
Так что это PROCEDURE
(tst2), который возвращает один набор результатов, в котором я называю FUNCTION
. Но это FUNCTION
(каким-то образом) повреждено, поскольку оно возвращает данные, слишком длинные для его предложения RETURNS
.
Когда я запускаю этот файл, я получаю ожидаемый результат:
string(93) "SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'tst()' at row 1"
Но моя проблема возникает потом: PDOStatement кажется «сломанным», и когда PHP пытается собрать мусор, или когда я пытаюсь closeCursor()
(чтобы я мог выполнить другие запросы снова), или когда я пытаюсь unset
это (для витрины), то PHP застрял. Он никогда не достигает другого var_dump, он просто ... простаивал навсегда?!
У меня есть эта проблема на PHP 5.6.35, но также на 7.1.16 и 7.2.4 (все используют mysqlnd драйвер 5.0.5 или 5.0.12). Я использую MySQL 5.7.21 (я не пробовал другие версии MySQL).
Есть подсказка?
Между тем, я также пытался без использования подготовленных операторов (поэтому с использованием $pdo->query
) и даже с использованием mysqli
функций: та же проблема, PHP застрял.
Но, изменив содержание процедуры, чтобы она не пыталась вернуть набор результатов, где происходит SIGNAL
, PHP больше не зависает. Так что это будет «работать» (работа = я получаю исключение SQL в своем коде, и мой скрипт PHP не заморожен)
<?php
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=test_sql',
'root',
'',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$pdo->query('DROP FUNCTION IF EXISTS tst');
$pdo->query('DROP PROCEDURE IF EXISTS tst2');
$pdo->query('CREATE FUNCTION tst() RETURNS VARCHAR(5) BEGIN RETURN \'x123456\'; END');
$pdo->query('CREATE PROCEDURE tst2() BEGIN SET @x := (SELECT tst()); END');
$st = $pdo->prepare('CALL tst2()');
try {
$st->execute();
} catch (Throwable $ex) {
var_dump($ex->getMessage());
$st->closeCursor(); // same with unset($st)
}
var_dump('end');
На самом деле, похоже, что поскольку ошибка SQL происходит внутри набора результатов, PDO получает заголовки набора результатов из MySQL, затем получает исключение и останавливается на этом, и никогда не "закрывает" набор результатов (оператор будет оставлен " открыл ", ожидая данных, которые никогда не будут приходить).
Увидев все это, я сообщил об ошибке команде PHP, потому что я получил то же самое в mysqli и PDO, с любой опцией PDO и в очень конкретном случае
https://bugs.php.net/bug.php?id=76815