1--TEST--
2MySQL: PDOStatement->getColumnMeta()
3--EXTENSIONS--
4pdo_mysql
5--SKIPIF--
6<?php
7require_once __DIR__ . '/inc/mysql_pdo_test.inc';
8MySQLPDOTest::skip();
9?>
10--FILE--
11<?php
12require_once __DIR__ . '/inc/mysql_pdo_test.inc';
13$db = MySQLPDOTest::factory();
14$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
15
16$db->exec('CREATE TABLE test_stmt_getcolumnmeta(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=InnoDB');
17$db->exec("INSERT INTO test_stmt_getcolumnmeta(id, label) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')");
18
19try {
20
21    $stmt = $db->prepare('SELECT id FROM test_stmt_getcolumnmeta ORDER BY id ASC');
22
23    // execute() has not been called yet
24    // NOTE: no warning
25    if (false !== ($tmp = $stmt->getColumnMeta(0)))
26        printf("[002] Expecting false got %s\n", var_export($tmp, true));
27
28    $stmt->execute();
29
30    // invalid offset
31    try {
32        $stmt->getColumnMeta(-1);
33    } catch (\ValueError $e) {
34        echo $e->getMessage(), \PHP_EOL;
35    }
36
37    $emulated =  $stmt->getColumnMeta(0);
38
39    printf("Testing native PS...\n");
40    $db->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0);
41        if (0 != $db->getAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY))
42            printf("[007] Unable to turn off emulated prepared statements\n");
43
44    $stmt = $db->prepare('SELECT id FROM test_stmt_getcolumnmeta ORDER BY id ASC');
45    $stmt->execute();
46    $native = $stmt->getColumnMeta(0);
47    if (count($native) == 0) {
48        printf("[008] Meta data seems wrong, %s / %s\n",
49            var_export($native, true), var_export($emulated, true));
50    }
51
52    // invalid offset
53    if (false !== ($tmp = $stmt->getColumnMeta(1)))
54        printf("[009] Expecting false because of invalid offset got %s\n", var_export($tmp, true));
55
56
57    function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) {
58
59        $db->exec('DROP TABLE IF EXISTS test_stmt_getcolumnmeta');
60
61        $sql = sprintf('CREATE TABLE test_stmt_getcolumnmeta(id INT, label %s) ENGINE=%s', $sql_type, MySQLPDOTest::getTableEngine());
62        if (!($stmt = @$db->prepare($sql)) || (!@$stmt->execute())) {
63            // Some engines and/or MySQL server versions might not support the data type
64            return true;
65        }
66
67        if (!$db->exec(sprintf("INSERT INTO test_stmt_getcolumnmeta(id, label) VALUES (1, '%s')", $value))) {
68            printf("[%03d] + 1] Insert failed, %d - %s\n", $offset,
69                $db->errorCode(), var_export($db->errorInfo(), true));
70            return false;
71        }
72
73        $stmt = $db->prepare('SELECT id, label FROM test_stmt_getcolumnmeta');
74        $stmt->execute();
75        $meta = $stmt->getColumnMeta(1);
76        $row = $stmt->fetch(PDO::FETCH_ASSOC);
77
78        if (empty($meta)) {
79            printf("[%03d + 2] getColumnMeta() failed, %d - %s\n", $offset,
80                $stmt->errorCode(), var_export($stmt->errorInfo(), true));
81            return false;
82        }
83
84        $elements = array('flags', 'table', 'name', 'len', 'precision', 'pdo_type');
85        foreach ($elements as $k => $element)
86            if (!isset($meta[$element])) {
87                printf("[%03d + 3] Element %s missing, %s\n", $offset,
88                    $element, var_export($meta, true));
89                return false;
90            }
91
92        if (($meta['table'] != 'test_stmt_getcolumnmeta') || ($meta['name'] != 'label')) {
93            printf("[%03d + 4] Table or field name is wrong, %s\n", $offset,
94                var_export($meta, true));
95            return false;
96        }
97
98        if (!is_null($native_type)) {
99            if (!isset($meta['native_type'])) {
100                printf("[%03d + 5] Element native_type missing, %s\n", $offset,
101                    var_export($meta, true));
102                return false;
103            }
104
105            if (!is_array($native_type))
106                $native_type = array($native_type);
107
108            $found = false;
109            foreach ($native_type as $k => $type) {
110                if ($meta['native_type'] == $type) {
111                    $found = true;
112                    break;
113                }
114            }
115
116            if (!$found) {
117                printf("[%03d + 6] Expecting native type %s, %s\n", $offset,
118                    var_export($native_type, true), var_export($meta, true));
119                return false;
120            }
121        }
122
123        if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) {
124            printf("[%03d + 6] Expecting PDO type %s got %s (%s)\n", $offset,
125                $pdo_type, var_export($meta, true), var_export($meta['native_type']));
126            return false;
127        }
128
129        return true;
130    }
131
132    $stmt = $db->prepare('SELECT @@sql_mode AS _mode');
133    $stmt->execute();
134    $row = $stmt->fetch(PDO::FETCH_ASSOC);
135    $real_as_float = (false === stristr($row['_mode'], "REAL_AS_FLOAT")) ? false : true;
136
137    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
138    test_meta($db, 20, 'BIT(8)', 1, 'BIT', PDO::PARAM_INT);
139    test_meta($db, 30, 'TINYINT', -127, 'TINY', PDO::PARAM_INT);
140    test_meta($db, 40, 'TINYINT UNSIGNED', 255, 'TINY', PDO::PARAM_INT);
141    test_meta($db, 50, 'BOOLEAN', 1, NULL, PDO::PARAM_INT);
142
143    test_meta($db, 60, 'SMALLINT', -32768, 'SHORT', PDO::PARAM_INT);
144    test_meta($db, 70, 'SMALLINT UNSIGNED', 65535, 'SHORT', PDO::PARAM_INT);
145
146    test_meta($db, 80, 'MEDIUMINT', -8388608, 'INT24', PDO::PARAM_INT);
147    test_meta($db, 90, 'MEDIUMINT UNSIGNED', 16777215, 'INT24', PDO::PARAM_INT);
148
149    test_meta($db, 100, 'INT', -2147483648, 'LONG', PDO::PARAM_INT);
150    test_meta($db, 110, 'INT UNSIGNED', 4294967295, 'LONG', PDO::PARAM_INT);
151
152    test_meta($db, 120, 'BIGINT', '-9223372036854775808', 'LONGLONG', (PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT);
153    test_meta($db, 130, 'BIGINT UNSIGNED', '18446744073709551615', 'LONGLONG', (PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT);
154
155    test_meta($db, 130, 'REAL', -1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
156    test_meta($db, 140, 'REAL UNSIGNED', 1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
157    test_meta($db, 150, 'REAL ZEROFILL', 1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
158    test_meta($db, 160, 'REAL UNSIGNED ZEROFILL', 1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR);
159
160    test_meta($db, 170, 'DOUBLE', -1.01, 'DOUBLE', PDO::PARAM_STR);
161    test_meta($db, 180, 'DOUBLE UNSIGNED', 1.01, 'DOUBLE', PDO::PARAM_STR);
162    test_meta($db, 190, 'DOUBLE ZEROFILL', 1.01, 'DOUBLE', PDO::PARAM_STR);
163    test_meta($db, 200, 'DOUBLE UNSIGNED ZEROFILL', 1.01, 'DOUBLE', PDO::PARAM_STR);
164
165    test_meta($db, 210, 'FLOAT', -1.01, 'FLOAT', PDO::PARAM_STR);
166    test_meta($db, 220, 'FLOAT UNSIGNED', 1.01, 'FLOAT', PDO::PARAM_STR);
167    test_meta($db, 230, 'FLOAT ZEROFILL', 1.01, 'FLOAT', PDO::PARAM_STR);
168    test_meta($db, 240, 'FLOAT UNSIGNED ZEROFILL', 1.01, 'FLOAT', PDO::PARAM_STR);
169
170    test_meta($db, 250, 'DECIMAL', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
171    test_meta($db, 260, 'DECIMAL UNSIGNED', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
172    test_meta($db, 270, 'DECIMAL ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
173    test_meta($db, 280, 'DECIMAL UNSIGNED ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
174
175    test_meta($db, 290, 'NUMERIC', -1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
176    test_meta($db, 300, 'NUMERIC UNSIGNED', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
177    test_meta($db, 310, 'NUMERIC ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
178    test_meta($db, 320, 'NUMERIC UNSIGNED ZEROFILL', 1.01, array('DECIMAL', 'NEWDECIMAL'), PDO::PARAM_STR);
179
180    test_meta($db, 330, 'DATE', '2008-04-23', array('DATE', 'NEWDATE'), PDO::PARAM_STR);
181    test_meta($db, 340, 'TIME', '14:37:00', 'TIME', PDO::PARAM_STR);
182    test_meta($db, 350, 'TIMESTAMP', '2008-03-23 14:38:00', 'TIMESTAMP', PDO::PARAM_STR);
183    test_meta($db, 360, 'DATETIME', '2008-03-23 14:38:00', 'DATETIME', PDO::PARAM_STR);
184    test_meta($db, 370, 'YEAR', '2008', 'YEAR', PDO::PARAM_INT);
185
186    test_meta($db, 380, 'CHAR(1)', 'a', 'STRING', PDO::PARAM_STR);
187    test_meta($db, 390, 'CHAR(10)', '0123456789', 'STRING', PDO::PARAM_STR);
188    test_meta($db, 400, 'CHAR(255)', str_repeat('z', 255), 'STRING', PDO::PARAM_STR);
189    test_meta($db, 410, 'VARCHAR(1)', 'a', 'VAR_STRING', PDO::PARAM_STR);
190    test_meta($db, 420, 'VARCHAR(10)', '0123456789', 'VAR_STRING', PDO::PARAM_STR);
191    test_meta($db, 430, 'VARCHAR(255)', str_repeat('z', 255), 'VAR_STRING', PDO::PARAM_STR);
192
193    test_meta($db, 440, 'BINARY(1)', str_repeat('a', 1), 'STRING', PDO::PARAM_STR);
194    test_meta($db, 450, 'BINARY(255)', str_repeat('b', 255), 'STRING', PDO::PARAM_STR);
195    test_meta($db, 460, 'VARBINARY(1)', str_repeat('a', 1), 'VAR_STRING', PDO::PARAM_STR);
196    test_meta($db, 470, 'VARBINARY(255)', str_repeat('b', 255), 'VAR_STRING', PDO::PARAM_STR);
197
198    test_meta($db, 480, 'TINYBLOB', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR);
199    test_meta($db, 490, 'BLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
200    test_meta($db, 500, 'MEDIUMBLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
201    test_meta($db, 510, 'LONGBLOB', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
202
203    test_meta($db, 520, 'TINYTEXT', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR);
204    test_meta($db, 530, 'TINYTEXT BINARY', str_repeat('b', 255), 'BLOB', PDO::PARAM_STR);
205
206    test_meta($db, 560, 'TEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
207    test_meta($db, 570, 'TEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
208
209    test_meta($db, 580, 'MEDIUMTEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
210    test_meta($db, 590, 'MEDIUMTEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
211
212    test_meta($db, 600, 'LONGTEXT', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
213    test_meta($db, 610, 'LONGTEXT BINARY', str_repeat('b', 256), 'BLOB', PDO::PARAM_STR);
214
215    test_meta($db, 620, "ENUM('yes', 'no') DEFAULT 'yes'", 'no', NULL, PDO::PARAM_STR);
216    test_meta($db, 630, "SET('yes', 'no') DEFAULT 'yes'", 'no', NULL, PDO::PARAM_STR);
217
218/*
219  | spatial_type
220*/
221
222    // unique key
223    $db->exec('DROP TABLE IF EXISTS test_stmt_getcolumnmeta');
224    $sql = sprintf('CREATE TABLE test_stmt_getcolumnmeta(id INT, label INT UNIQUE) ENGINE = %s', MySQLPDOTest::getTableEngine());
225    if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) {
226        $db->exec('INSERT INTO test_stmt_getcolumnmeta(id, label) VALUES (1, 2)');
227        $stmt = $db->query('SELECT id, label FROM test_stmt_getcolumnmeta');
228        $meta = $stmt->getColumnMeta(1);
229        if (!isset($meta['flags'])) {
230            printf("[1000] No flags contained in metadata %s\n", var_export($meta, true));
231        } else {
232            $flags = $meta['flags'];
233            $found = false;
234            foreach ($flags as $k => $flag) {
235                if ($flag == 'unique_key')
236                    $found = true;
237            }
238            if (!$found)
239                printf("[1001] Flags seem wrong %s\n", var_export($meta, true));
240        }
241    }
242
243    // primary key
244    $db->exec('DROP TABLE IF EXISTS test_stmt_getcolumnmeta');
245    $sql = sprintf('CREATE TABLE test_stmt_getcolumnmeta(id INT PRIMARY KEY NOT NULL AUTO_INCREMENT) ENGINE = %s', MySQLPDOTest::getTableEngine());
246    if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) {
247        $db->exec('INSERT INTO test_stmt_getcolumnmeta(id) VALUES (1)');
248        $stmt = $db->query('SELECT id FROM test_stmt_getcolumnmeta');
249        $meta = $stmt->getColumnMeta(0);
250        if (!isset($meta['flags'])) {
251            printf("[1002] No flags contained in metadata %s\n", var_export($meta, true));
252        } else {
253            $flags = $meta['flags'];
254            $found = false;
255            foreach ($flags as $k => $flag) {
256                if ($flag == 'primary_key')
257                    $found = true;
258            }
259            if (!$found)
260                printf("[1003] Flags seem wrong %s\n", var_export($meta, true));
261        }
262    }
263
264    // multiple key
265    $db->exec('DROP TABLE IF EXISTS test_stmt_getcolumnmeta');
266    $sql = sprintf('CREATE TABLE test_stmt_getcolumnmeta(id INT, label1 INT, label2 INT, INDEX idx1(label1, label2)) ENGINE = %s', MySQLPDOTest::getTableEngine());
267    if (($stmt = @$db->prepare($sql)) && @$stmt->execute()) {
268        $db->exec('INSERT INTO test_stmt_getcolumnmeta(id, label1, label2) VALUES (1, 2, 3)');
269        $stmt = $db->query('SELECT id, label1, label2 FROM test_stmt_getcolumnmeta');
270        $meta = $stmt->getColumnMeta(1);
271        if (!isset($meta['flags'])) {
272            printf("[1004] No flags contained in metadata %s\n", var_export($meta, true));
273        } else {
274            $flags = $meta['flags'];
275            $found = false;
276            foreach ($flags as $k => $flag) {
277                if ($flag == 'multiple_key')
278                    $found = true;
279            }
280            if (!$found)
281                printf("[1005] Flags seem wrong %s\n", var_export($meta, true));
282        }
283    }
284
285    $stmt = $db->query('SELECT NULL AS col1');
286    $meta = $stmt->getColumnMeta(0);
287    if ('NULL' !== $meta['native_type'])
288        printf("[1006] Expecting NULL got %s\n", $meta['native_type']);
289
290} catch (PDOException $e) {
291    // we should never get here, we use warnings, but never trust a system...
292    printf("[001] %s, [%s} %s\n",
293        $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo()));
294}
295
296print "done!";
297?>
298--CLEAN--
299<?php
300require_once __DIR__ . '/inc/mysql_pdo_test.inc';
301$db = MySQLPDOTest::factory();
302$db->exec('DROP TABLE IF EXISTS test_stmt_getcolumnmeta');
303?>
304--EXPECT--
305PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0
306Testing native PS...
307done!
308