1--TEST--
2MySQL Prepared Statements and different column counts
3--EXTENSIONS--
4pdo_mysql
5--SKIPIF--
6<?php
7require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
8MySQLPDOTest::skip();
9$db = MySQLPDOTest::factory();
10
11$row = $db->query('SELECT VERSION() as _version')->fetch(PDO::FETCH_ASSOC);
12$matches = array();
13if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches))
14    die(sprintf("skip Cannot determine MySQL Server version\n"));
15
16$version = $matches[1] * 10000 + $matches[2] * 100 + $matches[3];
17if ($version < 50000)
18    die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n",
19        $matches[1], $matches[2], $matches[3], $version));
20?>
21--FILE--
22<?php
23    require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
24    $db = MySQLPDOTest::factory();
25
26    function check_result($offset, $stmt, $columns) {
27        $row = $stmt->fetch(PDO::FETCH_ASSOC);
28        $stmt->nextRowSet();
29
30        if (!isset($row['one']) || ($row['one'] != 1)) {
31                printf("[%03d + 1] Expecting array('one' => 1), got %s\n", $offset, var_export($row, true));
32                return false;
33        }
34
35        if (($columns == 2) &&
36            (!isset($row['two']) || ($row['two'] != 2))) {
37                printf("[%03d + 2] Expecting array('one' => 1, 'two' => 2), got %s\n", $offset, var_export($row, true));
38                return false;
39        } else if (($columns == 1) && isset($row['two'])) {
40                printf("[%03d + 3] Expecting one array element got two\n", $offset);
41                return false;
42        }
43
44        return true;
45    }
46
47    try {
48
49        // What will happen if a PS returns a different number of result set column upon each execution?
50        // Lets try with a SP accepting parameters...
51        $db->exec('DROP PROCEDURE IF EXISTS p');
52        $db->exec('CREATE PROCEDURE p(IN cols INT) BEGIN IF cols < 2 THEN SELECT cols AS "one"; ELSE SELECT 1 AS "one", cols AS "two"; END IF; END;');
53
54        // Emulates PS first
55        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
56        $stmt = $db->prepare('CALL p(?)');
57
58        $columns = null;
59        $stmt->bindParam(1, $columns);
60        for ($i = 0; $i < 5; $i++) {
61            $columns = ($i % 2) + 1;
62            $stmt->execute();
63            check_result($i, $stmt, $columns);
64        }
65
66        if (MySQLPDOTest::isPDOMySQLnd()) {
67            // Native PS
68            // Libmysql cannot handle such a stored procedure. You will see leaks with libmysql
69            $db = MySQLPDOTest::factory();
70            $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
71            $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
72            $stmt = $db->prepare('CALL p(?)');
73            $stmt->bindParam(1, $columns);
74            for ($i = 5; $i < 10; $i++) {
75                $columns = ($i % 2) + 1;
76                $stmt->execute();
77                check_result($i, $stmt, $columns);
78            }
79        }
80
81        // And now without parameters... - this gives a different control flow inside PDO
82        $db->exec('DROP PROCEDURE IF EXISTS p');
83        $db->exec('CREATE PROCEDURE p() BEGIN DECLARE cols INT; SELECT @numcols INTO cols; IF cols < 2 THEN SET @numcols = 2; SELECT cols AS "one"; ELSE SET @numcols = 1; SELECT 1 AS "one", cols AS "two"; END IF; END;');
84
85                // Emulates PS first
86        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
87        $db->exec('SET @numcols = 1');
88        $stmt = $db->prepare('CALL p()');
89        $stmt->execute();
90        check_result(11, $stmt, 1);
91        $stmt->execute();
92        check_result(12, $stmt, 2);
93        $db->exec('SET @numcols = 1');
94        $stmt->execute();
95        check_result(13, $stmt, 1);
96
97        if (MySQLPDOTest::isPDOMySQLnd()) {
98            // Native PS
99            // Libmysql cannot handle such a stored procedure. You will see leaks with libmysql
100            $db = MySQLPDOTest::factory();
101            $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
102            $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
103            $db->exec('SET @numcols = 1');
104            $stmt = $db->prepare('CALL p()');
105            $stmt->execute();
106            check_result(14, $stmt, 1);
107            $stmt->execute();
108            check_result(15, $stmt, 2);
109            $db->exec('SET @numcols = 1');
110            $stmt->execute();
111            check_result(16, $stmt, 1);
112        }
113
114    } catch (PDOException $e) {
115        printf("[99] %s [%s] %s\n",
116            $e->getMessage(), $db->errorCode(), implode(' ', $db->errorInfo()));
117    }
118
119    print "done!";
120?>
121--CLEAN--
122<?php
123require_once __DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc';
124$pdo = MySQLPDOTest::factory();
125$pdo->query('DROP PROCEDURE IF EXISTS p');
126?>
127--EXPECT--
128done!
129