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