1--TEST--
2mysqli_stmt_bind_result()
3--EXTENSIONS--
4mysqli
5--SKIPIF--
6<?php
7require_once('skipifconnectfailure.inc');
8?>
9--FILE--
10<?php
11    require_once("connect.inc");
12    require('table.inc');
13
14    $stmt = mysqli_stmt_init($link);
15    if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test ORDER BY id LIMIT 1"))
16        printf("[002a] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
17
18    mysqli_stmt_close($stmt);
19    $stmt = mysqli_stmt_init($link);
20
21    $id = null;
22    $label = null;
23    $foo = null;
24
25    try {
26        mysqli_stmt_bind_result($stmt, $id);
27    } catch (Error $exception) {
28        echo $exception->getMessage() . "\n";
29    }
30
31    if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test ORDER BY id LIMIT 1"))
32        printf("[004] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
33
34    try {
35        mysqli_stmt_bind_result($stmt, $id);
36    } catch (\ArgumentCountError $e) {
37        echo $e->getMessage() . PHP_EOL;
38    }
39
40    if (true !== ($tmp = mysqli_stmt_bind_result($stmt, $id, $label)))
41        printf("[006] Expecting boolean/true, got %s/%s\n", gettype($tmp), $tmp);
42
43    try {
44        mysqli_stmt_bind_result($stmt, $id, $label, $foo);
45    } catch (\ArgumentCountError $e) {
46        echo $e->getMessage() . PHP_EOL;
47    }
48
49    if (!mysqli_stmt_execute($stmt))
50        printf("[008] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
51
52    while (mysqli_stmt_fetch($stmt)) {
53        var_dump($id);
54        var_dump($label);
55    }
56    mysqli_stmt_close($stmt);
57
58
59    function func_mysqli_stmt_bind_result($link, $engine, $bind_type, $sql_type, $bind_value, $offset, $type_hint = null) {
60
61        if (!mysqli_query($link, "DROP TABLE IF EXISTS test")) {
62            printf("[%04d] [%d] %s\n", $offset, mysqli_errno($link), mysqli_error($link));
63            return false;
64        }
65
66        if (!mysqli_query($link, sprintf("CREATE TABLE test(id INT, label %s, PRIMARY KEY(id)) ENGINE = %s", $sql_type, $engine))) {
67            // don't bail - column type might not be supported by the server, ignore this
68            return false;
69        }
70
71        if (!$stmt = mysqli_stmt_init($link)) {
72            printf("[%04d] [%d] %s\n", $offset + 1, mysqli_errno($link), mysqli_error($link));
73            return false;
74        }
75
76        if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (?, ?)")) {
77            printf("[%04d] [%d] %s\n", $offset + 2, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
78            return false;
79        }
80
81        $id = null;
82        if (!mysqli_stmt_bind_param($stmt, "i" . $bind_type, $id, $bind_value)) {
83            printf("[%04d] [%d] %s\n", $offset + 3, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
84            mysqli_stmt_close($stmt);
85            return false;
86        }
87
88        for ($id = 1; $id < 4; $id++) {
89            if (!mysqli_stmt_execute($stmt)) {
90                printf("[%04d] [%d] %s\n", $offset + 3 + $id, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
91                mysqli_stmt_close($stmt);
92                return false;
93            }
94        }
95        mysqli_stmt_close($stmt);
96
97        $stmt = mysqli_stmt_init($link);
98
99        if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test")) {
100            printf("[%04d] [%d] %s\n", $offset + 7, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
101            mysqli_stmt_close($stmt);
102            return false;
103        }
104
105        if (!mysqli_stmt_execute($stmt)) {
106            printf("[%04d] [%d] %s\n", $offset + 8, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
107            mysqli_stmt_close($stmt);
108            return false;
109        }
110
111        $result = mysqli_stmt_result_metadata($stmt);
112
113        $bind_res = null;
114        if (!mysqli_stmt_bind_result($stmt, $id, $bind_res)) {
115            printf("[%04d] [%d] %s\n", $offset + 9, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
116            mysqli_stmt_close($stmt);
117            return false;
118        }
119        $num = 0;
120        $fields = mysqli_fetch_fields($result);
121
122        while (mysqli_stmt_fetch($stmt)) {
123            if (!gettype($bind_res)=="unicode") {
124                if ($bind_res !== $bind_value && (!$type_hint || ($type_hint !== gettype($bind_res)))) {
125                    printf("[%04d] [%d] Expecting %s/'%s' [type hint = %s], got %s/'%s'\n",
126                        $offset + 10, $num,
127                        gettype($bind_value), $bind_value, $type_hint,
128                        gettype($bind_res), $bind_res);
129                        mysqli_stmt_close($stmt);
130                        return false;
131                }
132            }
133            $num++;
134        }
135
136        if ($num != 3) {
137            printf("[%04d] [%d] %s, expecting 3 results, got only %d results\n",
138                $offset + 11, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt), $num);
139            mysqli_stmt_close($stmt);
140            return false;
141        }
142
143        mysqli_stmt_close($stmt);
144        return true;
145    }
146
147
148    function func_mysqli_stmt_bind_make_string($len) {
149
150        $ret = '';
151        for ($i = 0; $i < $len; $i++)
152            $ret .= chr(mt_rand(65, 90));
153
154        return $ret;
155    }
156
157    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT", -11, 20);
158    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT", NULL, 40);
159    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT UNSIGNED", 1, 60);
160    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT UNSIGNED", NULL, 80);
161
162    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOL", 1, 100);
163    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOL", NULL, 120);
164    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOLEAN", 0, 140);
165    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOLEAN", NULL, 160);
166
167    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", -32768, 180);
168    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", 32767, 200);
169    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", NULL, 220);
170    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT UNSIGNED", 65535, 240);
171    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT UNSIGNED", NULL, 260);
172
173    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", -8388608, 280, "integer");
174    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", 8388607, 300, "integer");
175    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", NULL, 320);
176    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT UNSIGNED", 16777215, 340, "integer");
177    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT UNSIGNED", NULL, 360);
178
179    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", (defined("PHP_INT_MAX")) ? max(-1 * PHP_INT_MAX + 1, -2147483648) : 1, 380);
180    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", -2147483647, 400, "integer");
181    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", (defined("PHP_INT_MAX")) ? min(2147483647, PHP_INT_MAX) : 1, 420);
182    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", NULL, 440);
183    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", (defined("PHP_INT_MAX")) ? min(4294967295, 2147483647) : 1, 460);
184    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", 4294967295, 480, (defined("PHP_INT_MAX") && (4294967295 > PHP_INT_MAX)) ? "string" : null);
185    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", NULL, 500);
186
187    /* test is broken too: we bind "integer" but value is a float
188    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -9223372036854775808, 520);
189    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", 18446744073709551615, 560);
190    */
191    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", NULL, 540);
192    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", NULL, 580);
193    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -1, 1780);
194    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", 1, 1800);
195    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -1 * PHP_INT_MAX + 1, 1820);
196    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", PHP_INT_MAX, 1840);
197    func_mysqli_stmt_bind_result($link, $engine, "s", "BIGINT UNSIGNED", "18446744073709551615", 1860);
198    func_mysqli_stmt_bind_result($link, $engine, "s", "BIGINT", "-9223372036854775808", 1880);
199
200    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT", -9223372036854775808 - 1.1, 600);
201    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT", NULL, 620);
202    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT UNSIGNED", 18446744073709551615 + 1.1, 640);
203    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT UNSIGNED ", NULL, 660);
204
205    // Yes, we need the temporary variable. The PHP casting will fouls us otherwise.
206    $tmp = strval('-99999999.99');
207    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2)", $tmp, 680, "string");
208    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2)", NULL, 700);
209    $tmp = strval('99999999.99');
210    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2) UNSIGNED", $tmp , 720, "string");
211    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2) UNSIGNED", NULL, 740);
212    $tmp = strval('-99999999.99');
213    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", $tmp, 760, "string");
214    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", NULL, 780);
215    $tmp = strval('99999999.99');
216    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", $tmp, 800, "string");
217    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", NULL, 820);
218
219    // don't care about date() strict TZ warnings...
220    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE", @date('Y-m-d'), 840);
221    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE NOT NULL", @date('Y-m-d'), 860);
222    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE", NULL, 880);
223
224    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME", @date('Y-m-d H:i:s'), 900);
225    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME NOT NULL", @date('Y-m-d H:i:s'), 920);
226    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME", NULL, 940);
227
228    func_mysqli_stmt_bind_result($link, $engine, "s", "TIMESTAMP", @date('Y-m-d H:i:s'), 960);
229
230    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", @date('H:i:s'), 980);
231    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME NOT NULL", @date('H:i:s'), 1000);
232    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", NULL, 1020);
233
234    $tmp = intval(@date('Y'));
235    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR", $tmp, 1040, "integer");
236    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR NOT NULL", $tmp, 1060, "integer");
237    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR", NULL, 1080);
238
239    $string255 = func_mysqli_stmt_bind_make_string(255);
240    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1)", "a", 1110, 'string');
241    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(255)", $string255, 1120, 'string');
242    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1) NOT NULL", "a", 1140, 'string');
243    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1)", NULL, 1160);
244
245    $string65k = func_mysqli_stmt_bind_make_string(65535);
246    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1)", "a", 1180, 'string');
247    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(255)", $string255, 1200, 'string');
248    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(65635)", $string65k, 1220, 'string');
249    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1) NOT NULL", "a", 1240, 'string');
250    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1)", NULL, 1260);
251
252    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", "a", 1280);
253    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", chr(0), 1300);
254    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1) NOT NULL", "b", 1320);
255    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", NULL, 1340);
256
257    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", "a", 1360);
258    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", chr(0), 1380);
259    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1) NOT NULL", "b", 1400);
260    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", NULL, 1420);
261
262    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", "a", 1440);
263    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", chr(0), 1460);
264    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB NOT NULL", "b", 1480);
265    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", NULL, 1500);
266
267    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT", "a", 1520, 'string');
268    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT NOT NULL", "a", 1540, 'string');
269    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT", NULL, 1560, 'string');
270
271    // Note: you cannot insert any blob values this way. But you can check the API at least partly this way
272    // Extra BLOB tests are in mysqli_stmt_send_long()
273    func_mysqli_stmt_bind_result($link, $engine, "b", "BLOB", "", 1580);
274    func_mysqli_stmt_bind_result($link, $engine, "b", "TEXT", "", 1600, 'string');
275    func_mysqli_stmt_bind_result($link, $engine, "b", "MEDIUMBLOB", "", 1620);
276    func_mysqli_stmt_bind_result($link, $engine, "b", "MEDIUMTEXT", "", 1640, 'string');
277
278    /* Is this one related? http://bugs.php.net/bug.php?id=35759 */
279    if (($IS_MYSQLND) || (!$IS_MYSQLND && (ini_get('memory_limit') > 4294967296))) {
280        /* NOTE: the MySQL Client Library - not mysqlnd - will allocate
281        a hugge max_length(type) = 4GB bind buffer */
282        func_mysqli_stmt_bind_result($link, $engine, "b", "LONGBLOB", "", 1660);
283        func_mysqli_stmt_bind_result($link, $engine, "b", "LONGTEXT", "", 1680, 'string');
284    }
285
286    func_mysqli_stmt_bind_result($link, $engine, "s", "ENUM('a', 'b')", "a", 1700, 'string');
287    func_mysqli_stmt_bind_result($link, $engine, "s", "ENUM('a', 'b')", NULL, 1720, 'string');
288    func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", "a", 1740, 'string');
289    func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", NULL, 1760, 'string');
290
291    if (mysqli_get_server_version($link) >= 50600)
292        func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", "13:31:34.123456", 1770, "13:31:34");
293
294    $stmt = mysqli_stmt_init($link);
295    if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (1000, 'z')"))
296        printf("[3001] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
297
298    $id = null;
299    try {
300        mysqli_stmt_bind_result($stmt, $id);
301    } catch (\ArgumentCountError $e) {
302        $e->getMessage() . \PHP_EOL;
303    }
304
305    mysqli_stmt_close($stmt);
306
307    mysqli_close($link);
308    print "done!";
309?>
310--CLEAN--
311<?php
312    require_once("clean_table.inc");
313?>
314--EXPECTF--
315mysqli_stmt object is not fully initialized
316Number of bind variables doesn't match number of fields in prepared statement
317Number of bind variables doesn't match number of fields in prepared statement
318int(1)
319%s(1) "a"
320done!
321