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