1--TEST--
2int mysqli_poll() and kill
3--EXTENSIONS--
4mysqli
5--SKIPIF--
6<?php
7require_once 'skipifconnectfailure.inc';
8?>
9--FILE--
10<?php
11    require_once 'connect.inc';
12
13    function get_connection() {
14        global $host, $user, $passwd, $db, $port, $socket;
15
16        if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
17            printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
18        return $link;
19    }
20
21    // Killing connection - 1
22
23    $link = get_connection();
24    if (true !== ($tmp = mysqli_query($link, "SELECT 1 AS 'processed before killed'", MYSQLI_ASYNC |  MYSQLI_USE_RESULT)))
25        printf("[002] Expecting boolean/true got %s/%s\n", gettype($tmp), var_export($tmp, true));
26
27    // Sleep 0.1s - the asynchronous query should have been processed after the wait period
28    usleep(100000);
29    $thread_id = mysqli_thread_id($link);
30    get_connection()->query('KILL '.$thread_id);
31
32    $links = array($link);
33    $errors = array($link);
34    $reject = array($link);
35
36    // Yes, 1 - the asynchronous query should have been processed
37    if (1 !== ($tmp = (mysqli_poll($links, $errors, $reject, 0, 10000))))
38        printf("[003] Expecting int/1 got %s/%s\n", gettype($tmp), var_export($tmp, true));
39
40    if (!is_array($links) || empty($links))
41        printf("[004] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true));
42    else
43        foreach ($links as $link) {
44            if (is_object($res = mysqli_reap_async_query($link))) {
45                // Yes, you can fetch a result - the query has been processed
46                var_dump(mysqli_fetch_assoc($res));
47                mysqli_free_result($res);
48            } else if ($link->errno > 0) {
49                printf("[005] Error: %d\n", $link->errno);
50            }
51        }
52
53    // No error!
54    if (!is_array($errors) || !empty($errors))
55        printf("[006] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true));
56
57    if (!is_array($reject) || !empty($reject))
58        printf("[007] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true));
59
60    // Let's pass a dead connection
61    $links = array($link);
62    $errors = array($link);
63    $reject = array($link);
64    if (0 !== ($tmp = mysqli_poll($links, $errors, $reject, 1)))
65        printf("[008] There should be no connection ready! Returned %s/%s, expecting int/0.\n",
66            gettype($tmp), var_export($tmp, true));
67
68    if (!empty($errors))
69        printf("[009] There should be no errors but one rejected connection\n");
70
71    foreach ($reject as $mysqli)
72        if (mysqli_thread_id($mysqli) != $thread_id) {
73            printf("[010] Rejected thread %d should have rejected thread %d\n",
74                mysqli_thread_id($mysqli), $thread_id);
75        }
76
77    // Killing connection - 2
78
79    $link = get_connection();
80    if (true !== ($tmp = mysqli_query($link, "SELECT 1", MYSQLI_ASYNC |  MYSQLI_USE_RESULT)))
81        printf("[011] Expecting boolean/true got %s/%s\n", gettype($tmp), var_export($tmp, true));
82
83    usleep(100000);
84    $thread_id = mysqli_thread_id($link);
85    get_connection()->query('KILL '.$thread_id);
86
87    // Yes, 1 - fetch OK packet of kill!
88    $processed = 0;
89    $begin = microtime(true);
90    do {
91        $links = array($link, $link);
92        $errors = array($link, $link);
93        $reject = array($link, $link);
94        $ready = mysqli_poll($links, $errors, $reject, 1);
95
96        if (!empty($errors)) {
97            foreach ($errors as $mysqli) {
98                printf("[012] Error on thread %d: %s/%s\n",
99                    mysqli_thread_id($mysqli),
100                    mysqli_errno($mysqli),
101                    mysqli_error($mysqli));
102            }
103            break;
104        }
105
106        if (FALSE === $ready) {
107            printf("[013] MySQLi indicates some error\n");
108            break;
109        }
110
111        if (!empty($reject)) {
112            foreach ($reject as $mysqli) {
113                printf("[014] Rejecting thread %d: %s/%s\n",
114                    mysqli_thread_id($mysqli),
115                    mysqli_errno($mysqli),
116                    mysqli_error($mysqli));
117            }
118            $processed += count($reject);
119        }
120
121        foreach ($links as $mysqli) {
122            if (is_object($res = mysqli_reap_async_query($mysqli))) {
123                printf("Fetching from thread %d...\n", mysqli_thread_id($mysqli));
124                var_dump(mysqli_fetch_assoc($res));
125            } else if (mysqli_errno($mysqli) > 0) {
126                printf("[015] %d/%s\n", mysqli_errno($mysqli), mysqli_error($mysqli));
127            }
128            $processed++;
129        }
130
131        if ((microtime(true) - $begin) > 5) {
132            printf("[016] Pulling the emergency break after 5s, something is wrong...\n");
133            break;
134        }
135
136    } while ($processed < 2);
137
138
139    // Killing connection - 3
140
141    $link = get_connection();
142    $thread_id = mysqli_thread_id($link);
143    get_connection()->query('KILL '.$thread_id);
144    // Sleep 0.1s  to ensure the KILL gets recognized
145    usleep(100000);
146    if (false !== ($tmp = mysqli_query($link, "SELECT 1 AS 'processed before killed'", MYSQLI_ASYNC |  MYSQLI_USE_RESULT)))
147        printf("[017] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true));
148
149    $links = array($link);
150    $errors = array($link);
151    $reject = array($link);
152
153    if (0 !== ($tmp = (mysqli_poll($links, $errors, $reject, 0, 10000))))
154        printf("[018] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true));
155
156    if (!is_array($links) || empty($links))
157        printf("[019] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true));
158    else
159        foreach ($links as $link) {
160            if (is_object($res = mysqli_reap_async_query($link))) {
161                // No, you cannot fetch the result
162                var_dump(mysqli_fetch_assoc($res));
163                mysqli_free_result($res);
164            } else if ($link->errno > 0) {
165                // But you are supposed to handle the error the way its shown here!
166                printf("[020] Error: %d/%s\n", $link->errno, $link->error);
167            }
168        }
169
170    // None of these will indicate an error, check errno on the list of returned connections!
171    if (!is_array($errors) || !empty($errors))
172        printf("[021] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true));
173
174    if (!is_array($reject) || !empty($reject))
175        printf("[021] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true));
176
177
178    mysqli_close($link);
179    print "done!";
180?>
181--XFAIL--
182To be fixed later. Minor issue about fetching error message from killed line
183--EXPECTF--
184array(1) {
185  ["processed before killed"]=>
186  string(1) "1"
187}
188Fetching from thread %d...
189array(1) {
190  [1]=>
191  string(1) "1"
192}
193
194Warning: mysqli_reap_async_query(): Premature end of data (mysqlnd_wireprotocol.c:%d) in %s on line %d
195
196Warning: mysqli_reap_async_query(): RSET_HEADER %s
197
198Warning: mysqli_reap_async_query(): Error reading result set's header in %s on line %d
199
200Warning: Error while sending QUERY packet. %s
201
202Warning: mysqli_reap_async_query(): %s
203
204Warning: mysqli_reap_async_query(): Error reading result set's header in %s on line %d
205[018] Error: %d/%s
206done!
207