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