Bug #81567 [Ver]: Connection error status not immediately set for killed async query
| From: | nikic@php.net | Date: | Thu, 04 Nov 2021 15:07:58 +0000 |
| Subject: | Bug #81567 [Ver]: Connection error status not immediately set for killed async query | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-237560@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=81567&edit=1
ID: 81567
Updated by: nikic@php.net
Reported by: steven at movista dot com
Summary: Connection error status not immediately set for
killed async query
Status: Verified
Type: Bug
Package: MySQLi related
Operating System: Ubuntu 20.04.3 LTS
PHP Version: 7.4.25
-Assigned To:
+Assigned To: dharman
Block user comment: N
Private report: N
New Comment:
After looking a bit closer, I don't think this is a bug. With STORE_RESULT, the response is
processed at that point, so any errors will be encountered there. With USE_RESULT, it will happen
when actually reading from the result sets, so you need to be prepared for errors to occur at that
point.
This doesn't have anything to do with async, the behavior is the same if you do:
$c->query('SET max_execution_time=1');
$result = $c->query('SELECT SLEEP(10) UNION ALL SELECT 1', MYSQLI_USE_RESULT);
var_dump($result->fetch_row());
It will throw on the fetch_row(), not the query().
> For other queries with bad syntax or that reference non-existent columns/tables, the connection
> handle error status is updated when reap_async_query() is called, but not when the query gets
> killed.
This is also expected. Syntax errors are detected when the query is initially submitted, while the
timeout occurs only after the query has successfully started execution.
I'd like a second opinion from @dharman to confirm that the current behavior is correct.
Previous Comments:
------------------------------------------------------------------------
[2021-11-04 14:51:34] nikic@php.net
Can reproduce this. I had to adjust the test case to use
$c->query('SET max_execution_time=1');
$c->query('SELECT SLEEP(10) UNION ALL SELECT 1', MYSQLI_ASYNC|MYSQLI_USE_RESULT);
because my MySQL version doesn't support max_statement_time, and seems to immediately return
from SLEEP without the UNION ALL.
------------------------------------------------------------------------
[2021-10-29 21:47:20] steven at movista dot com
Description:
------------
I'm running the Ubuntu php 7.4.25 distribution from http://ppa.launchpad.net/ondrej/php/ubuntu:
PHP 7.4.25 (cli) (built: Oct 22 2021 12:34:33) ( NTS )
I'm working with MariaDB (10.4.21-MariaDB-1:10.4.21+maria~focal-log) and Apache2
(2.4.41-4ubuntu3.8) with mod_php (7.4.25-1+ubuntu20.04.1+deb.sury.org+1). This also occurs via CLI
so I don't think Apache has anything to do with it.
I don't have any special configs for mysqlnd.
I'm running an async query that could be killed via MariaDB's max_statement_time and
MYSQLI_USE_RESULT.
When a query gets killed, then I call mysqli::poll(), then reap_async_query(), the connection handle
error status is not updated; errno is 0 and error is "".
For other queries with bad syntax or that reference non-existent columns/tables, the connection
handle error status is updated when reap_async_query() is called, but not when the query gets
killed.
The connection handle's error status is not updated until after I attempt a fetch on the
mysqli_stmt object I get back from reap_async_query(). I would expect that the connection handle
error status for a killed query would be set after reap_async_query(), but since I don't get it
until after attempting a fetch, this complicates my error handling.
For what it's worth, if I use MYSQLI_STORE_RESULT instead of MYSQLI_USE_RESULT, the connection
handle error status is set after reap_async_query(), but I prefer to use MYSQLI_USE_RESULT so I can
stream a result set directly to redis, a file, or whatever else so I don't have to keep the
entire result set in memory.
Test script:
---------------
<?php
$c = new mysqli(HOST,USER,PASSWORD,DATABASE);
$c->query('SET STATEMENT max_statement_time=1 FOR SELECT
SLEEP(10);',MYSQLI_ASYNC|MYSQLI_USE_RESULT);
$reads = $errors = $rejects = [$c];
mysqli::poll($reads,$errors,$rejects,2);
$c = $reads[0];
if($c->error) die("Error after poll! {$c->error}\n");
$rs = $c->reap_async_query();
if($c->error) die("Error after reaping query! {$c->error}\n");
$rs->fetch_row();
if($c->error) die("Error after fetching result set! {$c->error}\n");
Expected result:
----------------
What I expect to happen with my test script is that when it executes, there should be a short delay
and it should print "Error after reaping query! Query execution was interrupted
(max_statement_time exceeded)".
I expect that the connection handle error status should be set after reap_async_query(), so I can
check if the async query was successful or encountered some kind of error.
Actual result:
--------------
The script prints "Error after fetching result set! Query execution was interrupted
(max_statement_time exceeded)"
This indicates that the mysqli connection handle error status is not updated until after I've
called reap_async_query() and attempted any kind of fetch*().
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=81567&edit=1