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