我正在尝试解决看起来很模糊的问题,我认为这可能是mysqlnd驱动程序或mysql中的错误,或者可能是PHP端。我希望有人能够确认这是一个错误,还是我做错了什么(可能更有可能!)。我在Ubuntu 15.10上使用php 5.6.11-1ubuntu3和MariaDB / mysql版本10.0.20-MariaDB-0ubuntu0.15.04.1。
我发布的代码是概念证明,并不是我遇到过的原始代码 - 它仅用于说明目的,尽管它确实运行。
请注意,这是/一般调用存储过程并获取结果的问题。我知道存储过程将返回多个需要处理的结果集,并且这是处理的。问题特别是当存储过程使用游标时。
我创建了以下POC php代码,通过mysqli中的预准备语句调用存储过程:
ini_set("display_errors", 1);
ini_set("display_startup_errors", 1);
ini_set("error_reporting", E_ALL);
ini_set("log_errors", true);
error_reporting("E_ALL");
/* Trying to find a solution to this bug:
* mysqli used to connect
* call stored proc via a prepared stmt. Proc contains a cursor.
* Connection is lost during processing, possibly at the point cursor is opened.
*/
function dbg()
{
foreach( func_get_args() as $a )
{
if( !is_string($a) ) $a = var_export($a, true);
echo "\n<br/>\nDBG: " . $a;
ob_flush();
flush();
}
}
function sdown()
{
dbg("SHUT DOWN");
}
function err($num, $err)
{
dbg("EH: $err");
}
register_shutdown_function("sdown");
set_error_handler(err);
try
{
test();
}
catch( Exception $e )
{
dbg("Exception caught: " . $r->getMessage());
}
function test()
{
dbg("Connecting...");
$db = new mysqli("localhost", "testuser", "testtesttest", "testdb");
dbg("Connected");
if( $db->connect_error )
throw new Exception("Failed connecting to DB server");
dbg("set charset");
$db->set_charset("utf8-bin");
$stmt = $db->stmt_init();
dbg("init");
$result = $stmt->prepare("call curTest(4,5)");
dbg("prepared");
$stmt->execute();
dbg("executed");
$stmt->store_result();
$stmt->bind_result($a, $b);
dbg("bound");
do {
dbg("About to fetch...");
$result = $stmt->fetch();
dbg("fetched");
dbg("RES:", $result);
if( $result == false )
{
dbg(" ** ERROR: " . $db->error);
exit;
}
dbg("Got vars:", $a, $b);
$result->free();
} while ($stmt->more_results() && $stmt->next_result());
}
调用的存储过程如下所示:
create procedure curTest (AuthTok char(32), LogId char(11))
begin
declare vBC varchar(10);
declare vBID, done int unsigned;
declare cur1 cursor for select Id, BookingCode from Bookings;
declare continue handler for not found set done=true;
drop table if exists tBC;
create temporary table tBC (bid int unsigned, bc varchar(10));
insert into tBC (bid, bc) values (42, "foo");
open cur1;
set done = false;
read_loop: loop
fetch cur1 into vBID, vBC;
if done then
leave read_loop;
end if;
insert into tBC (bid, bc) values (vBID, vBC);
end loop;
close cur1;
select * from tBC;
drop table tBC;
end
运行程序时,此输出显示在屏幕上。注意EH行告诉我在与服务器通信时协议出现严重问题。此时我实际上失去了连接,这在POC代码中没有显示。还要注意,它是第一个出错的“fetch”;这不是“多个结果集”的问题。
DBG: Connecting...
DBG: Connected
DBG: set charset
DBG: init
DBG: prepared
DBG: executed
DBG: bound
DBG: About to fetch...
DBG: EH: Packets out of order. Expected 1 received 5. Packet size=15
DBG: fetched
DBG: RES:
DBG: false
DBG: ** ERROR:
DBG: SHUT DOWN
如果我从mysql客户端运行它,我会收到预期的结果:
MariaDB [hb]> call curTest(1,2);
+------+----------+
| bid | bc |
+------+----------+
| 42 | foo |
| 1 | PCRFXDVX |
+------+----------+
2 rows in set (0.07 sec)
Query OK, 0 rows affected (0.09 sec)
如果编辑存储过程只打开和关闭游标而没有游标循环,我仍然会收到相同的错误。
但如果编辑它以便光标根本不打开或关闭,那么它可以正常工作:
create procedure curTest (AuthTok char(32), LogId char(11))
begin
declare vBC varchar(10);
declare vBID, done int unsigned;
declare cur1 cursor for select Id, BookingCode from Bookings;
declare continue handler for not found set done=true;
drop table if exists tBC;
create temporary table tBC (bid int unsigned, bc varchar(10));
insert into tBC (bid, bc) values (42, "foo");
-- open cur1;
--
-- set done = false;
-- read_loop: loop
-- fetch cur1 into vBID, vBC;
-- if done then
-- leave read_loop;
-- end if;
-- insert into tBC (bid, bc) values (vBID, vBC);
-- end loop;
--
-- close cur1;
select * from tBC;
drop table tBC;
end
这给了我一个很好的干净运行:
DBG: Connecting...
DBG: Connected
DBG: set charset
DBG: init
DBG: prepared
DBG: executed
DBG: bound
DBG: About to fetch...
DBG: fetched
DBG: RES:
DBG: true
DBG: Got vars:
DBG: 42
DBG: foo
DBG: SHUT DOWN
我为此搜索了很多内容并继续寻找死胡同。我能找到的最接近的匹配是人们与PDO相似并通过使用“模拟准备”PDO设置来修复它。感觉是有司机问题。 mysqli似乎没有相同的设置。
所以我想知道 -
这里有没有人经历过这个?
我是否缺少一个设置或步骤可以使这项工作?
非常感谢!
谢谢,
-OLi