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