From e02db623df77ef028ce8b133713efa0022265e71 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 1 May 2019 13:09:15 -0700 Subject: [PATCH] Added more pdo tests to verify different error conditions (#984) --- ...y_encoding_error_bound_by_name_errors.phpt | 158 ++++++++++++++++++ .../pdo_sqlsrv/pdo_fetch_column_twice.phpt | 151 +++++++++++++++++ .../pdo_sqlsrv/pdo_output_decimal.phpt | 4 +- .../pdo_sqlsrv/pdo_output_decimal_errors.phpt | 128 ++++++++++++++ .../pdo_sqlsrv/pdostatement_get_set_attr.phpt | 108 ++++++------ 5 files changed, 499 insertions(+), 50 deletions(-) create mode 100644 test/functional/pdo_sqlsrv/pdo_035_binary_encoding_error_bound_by_name_errors.phpt create mode 100644 test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt create mode 100644 test/functional/pdo_sqlsrv/pdo_output_decimal_errors.phpt diff --git a/test/functional/pdo_sqlsrv/pdo_035_binary_encoding_error_bound_by_name_errors.phpt b/test/functional/pdo_sqlsrv/pdo_035_binary_encoding_error_bound_by_name_errors.phpt new file mode 100644 index 00000000..14ccac36 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_035_binary_encoding_error_bound_by_name_errors.phpt @@ -0,0 +1,158 @@ +--TEST-- +GitHub Issue #35 binary encoding error when binding by name +--DESCRIPTION-- +Based on pdo_035_binary_encoding_error_bound_by_name.phpt but this includes error checking for various encoding errors +--SKIPIF-- + +--FILE-- +prepare($sql); + $stmt->bindParam(1, $value, PDO::PARAM_INT, 0, PDO::SQLSRV_ENCODING_DEFAULT); + $stmt->setAttribute(constant('PDO::SQLSRV_ATTR_ENCODING'), PDO::SQLSRV_ENCODING_BINARY); + $stmt->bindParam(2, $input, PDO::PARAM_LOB); + $stmt->execute(); + } catch (PDOException $e) { + $error = '*An encoding was specified for parameter 1. Only PDO::PARAM_LOB and PDO::PARAM_STR can take an encoding option.'; + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in bindTypeNoEncoding\n"; + var_dump($e->getMessage()); + } + } +} + +function bindDefaultEncoding($conn, $sql, $input) +{ + try { + $value = 1; + + $stmt = $conn->prepare($sql); + $stmt->bindParam(1, $value, PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_DEFAULT); + $stmt->setAttribute(constant('PDO::SQLSRV_ATTR_ENCODING'), PDO::SQLSRV_ENCODING_BINARY); + $stmt->bindParam(2, $input, PDO::PARAM_LOB); + $stmt->execute(); + } catch (PDOException $e) { + $error = '*Invalid encoding specified for parameter 1.'; + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in bindDefaultEncoding\n"; + var_dump($e->getMessage()); + } + } +} + +function insertData($conn, $sql, $input) +{ + try { + $value = 1; + + $stmt = $conn->prepare($sql); + $stmt->bindParam(1, $value); + $stmt->setAttribute(constant('PDO::SQLSRV_ATTR_ENCODING'), PDO::SQLSRV_ENCODING_BINARY); + $stmt->bindParam(2, $input, PDO::PARAM_LOB); + $stmt->execute(); + } catch (PDOException $e) { + echo "Error unexpected in insertData\n"; + var_dump($e->getMessage()); + } +} + +function invalidEncoding1($conn, $sql) +{ + try { + $stmt = $conn->prepare($sql); + $stmt->bindColumn(1, $id, PDO::PARAM_INT, 0, PDO::SQLSRV_ENCODING_UTF8); + $stmt->execute(); + $stmt->fetch(PDO::FETCH_BOUND); + } catch (PDOException $e) { + $error = '*An encoding was specified for column 1. Only PDO::PARAM_LOB and PDO::PARAM_STR column types can take an encoding option.'; + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in invalidEncoding1\n"; + var_dump($e->getMessage()); + } + } +} + +function invalidEncoding2($conn, $sql) +{ + try { + $stmt = $conn->prepare($sql); + $stmt->bindColumn('Value', $val1, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_DEFAULT); + $stmt->execute(); + $stmt->fetch(PDO::FETCH_BOUND); + } catch (PDOException $e) { + $error = '*Invalid encoding specified for column 1.'; + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in invalidEncoding2\n"; + var_dump($e->getMessage()); + } + } +} + +function invalidEncoding3($conn, $sql) +{ + try { + $stmt = $conn->prepare($sql); + $stmt->bindColumn(1, $id, PDO::PARAM_STR, 0, "dummy"); + $stmt->execute(); + $stmt->fetch(PDO::FETCH_BOUND); + } catch (PDOException $e) { + $error = '*An invalid type or value was given as bound column driver data for column 1. Only encoding constants such as PDO::SQLSRV_ENCODING_UTF8 may be used as bound column driver data.'; + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in invalidEncoding3\n"; + var_dump($e->getMessage()); + } + } +} + +try { + require_once( "MsCommon_mid-refactor.inc" ); + + // Connect + $conn = connect(); + + // Create a table + $tableName = "testTableIssue35"; + createTable($conn, $tableName, array("ID" => "int", "Value" => "varbinary(max)")); + + // Insert data using bind parameters + $sql = "INSERT INTO $tableName VALUES (?, ?)"; + $message = "This is to test github issue 35."; + $value = base64_encode($message); + + // Errors expected + bindTypeNoEncoding($conn, $sql, $value); + bindDefaultEncoding($conn, $sql, $value); + + // No error expected + insertData($conn, $sql, $value); + + // Fetch data, but test several invalid encoding issues (errors expected) + $sql = "SELECT * FROM $tableName"; + invalidEncoding1($conn, $sql); + invalidEncoding2($conn, $sql); + invalidEncoding3($conn, $sql); + + // Now fetch it back + $stmt = $conn->prepare("SELECT Value FROM $tableName"); + $stmt->bindColumn('Value', $val1, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY); + $stmt->execute(); + $stmt->fetch(PDO::FETCH_BOUND); + var_dump($val1 === $value); + + // Close connection + dropTable($conn, $tableName); + unset($stmt); + unset($conn); + print "Done\n"; +} catch (PDOException $e) { + var_dump($e->errorInfo); +} +?> +--EXPECT-- +bool(true) +Done \ No newline at end of file diff --git a/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt b/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt new file mode 100644 index 00000000..7e2ec194 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt @@ -0,0 +1,151 @@ +--TEST-- +Test fetchColumn twice in a row. Intentionally trigger various error messages. +--DESCRIPTION-- +This is similar to sqlsrv_fetch_field_twice_data_types.phpt. +--SKIPIF-- + +--FILE-- +prepare($tsql); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row !== false) { + echo "fetchBeforeExecute: fetch should have failed before execute!\n"; + } + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_NUM); + + for ($i = 0; $i < count($inputs); $i++) { + if ($row[$i] !== $inputs[$i]) { + echo "fetchBeforeExecute: expected $inputs[$i] but got $row[$i]\n"; + } + } + + unset($stmt); + } catch (PDOException $e) { + var_dump($e->getMessage()); + } +} + +function fetchColumnTwice($conn, $tableName, $col, $input) +{ + try { + $tsql = "SELECT * FROM $tableName"; + $stmt = $conn->query($tsql); + $result = $stmt->fetchColumn($col); + if ($result !== $input) { + echo "fetchColumnTwice (1): expected $input but got $result\n"; + } + $result = $stmt->fetchColumn($col); + if ($result !== false) { + echo "fetchColumnTwice (2): expected the second fetchColumn to fail\n"; + } + + // Re-run the query with fetch style + $stmt = $conn->query($tsql, PDO::FETCH_COLUMN, $col); + $result = $stmt->fetch(); + if ($result !== $input) { + echo "fetchColumnTwice (3): expected $input but got $result\n"; + } + $result = $stmt->fetch(); + if ($result !== false) { + echo "fetchColumnTwice (4): expected the second fetch to fail\n"; + } + $result = $stmt->fetchColumn($col); + echo "fetchColumnTwice (5): expected fetchColumn to throw an exception\n"; + unset($stmt); + } catch (PDOException $e) { + $error = '*There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved.'; + + if (!fnmatch($error, $e->getMessage())) { + echo "Error message unexpected in fetchColumnTwice\n"; + var_dump($e->getMessage()); + } + } +} + +function fetchColumnOutOfBound1($conn, $tableName, $col) +{ + try { + $tsql = "SELECT * FROM $tableName"; + $stmt = $conn->query($tsql); + $result = $stmt->fetchColumn($col); + echo "fetchColumnOutOfBound1: expected fetchColumn to throw an exception\n"; + unset($stmt); + } catch (PDOException $e) { + $error1 = '*General error: Invalid column index'; + $error2 = '*An invalid column number was specified.'; + + // Different errors may be returned depending on running with run-tests.php or not + if (fnmatch($error1, $e->getMessage()) || fnmatch($error2, $e->getMessage())) { + ; + } else { + echo "Error message unexpected in fetchColumnOutOfBound1\n"; + var_dump($e->getMessage()); + } + } +} + +function fetchColumnOutOfBound2($conn, $tableName, $col) +{ + try { + $tsql = "SELECT * FROM $tableName"; + $stmt = $conn->query($tsql, PDO::FETCH_COLUMN, $col); + $result = $stmt->fetch(); + unset($stmt); + } catch (PDOException $e) { + var_dump($e->getMessage()); + } +} + +try { + $conn = connect(); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $tableName = 'pdoFetchColumnTwice'; + $colMeta = array(new ColumnMeta('int', 'c1_int'), + new ColumnMeta('varchar(20)', 'c2_varchar'), + new ColumnMeta('decimal(5, 3)', 'c3_decimal'), + new ColumnMeta('datetime', 'c4_datetime')); + createTable($conn, $tableName, $colMeta); + + $inputs = array('968580013', 'dummy value', '3.438', ('1756-04-16 23:27:09.130')); + $numCols = count($inputs); + + $tsql = "INSERT INTO $tableName(c1_int, c2_varchar, c3_decimal, c4_datetime) VALUES (?,?,?,?)"; + $stmt = $conn->prepare($tsql); + + for ($i = 0; $i < $numCols; $i++) { + $stmt->bindParam($i + 1, $inputs[$i]); + } + $stmt->execute(); + unset($stmt); + + fetchBeforeExecute($conn, $tableName, $inputs); + for ($i = 0; $i < $numCols; $i++) { + fetchColumnTwice($conn, $tableName, $i, $inputs[$i]); + } + + fetchColumnOutOfBound1($conn, $tableName, -1); + + // Change to warning mode + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + fetchColumnOutOfBound2($conn, $tableName, $numCols + 1); + + dropTable($conn, $tableName); + unset($conn); + echo "Done\n"; +} catch (PDOException $e) { + var_dump($e); +} +?> +--EXPECTREGEX-- +Warning: PDOStatement::fetch\(\): SQLSTATE\[HY000\]: General error: Invalid column index in .+(\/|\\)pdo_fetch_column_twice.php on line [0-9]+ + +Warning: PDOStatement::fetch\(\): SQLSTATE\[HY000\]: General error in .+(\/|\\)pdo_fetch_column_twice.php on line [0-9]+ +Done diff --git a/test/functional/pdo_sqlsrv/pdo_output_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_output_decimal.phpt index b4512c31..3cdee20c 100644 --- a/test/functional/pdo_sqlsrv/pdo_output_decimal.phpt +++ b/test/functional/pdo_sqlsrv/pdo_output_decimal.phpt @@ -42,7 +42,7 @@ try { $expected1 = "7.4"; $expected2 = "7"; if ($outValue1 == $expected1 && $outValue2 == $expected2) { - echo "Test Successfully\n"; + echo "Test Successful\n"; } dropProc($conn, $proc_scale); @@ -56,4 +56,4 @@ try { ?> --EXPECT-- -Test Successfully +Test Successful diff --git a/test/functional/pdo_sqlsrv/pdo_output_decimal_errors.phpt b/test/functional/pdo_sqlsrv/pdo_output_decimal_errors.phpt new file mode 100644 index 00000000..5ec6c6fb --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_output_decimal_errors.phpt @@ -0,0 +1,128 @@ +--TEST-- +Call stored procedures with inputs of different datatypes to get outputs of various types +--DESCRIPTION-- +Similar to pdo_output_decimal.phpt but this time intentionally test some error cases +--SKIPIF-- + +--FILE-- +prepare("{CALL $proc (?, ?, ?)}"); + $stmt->bindValue(1, $inValue1); + $stmt->bindValue(2, $inValue2); + $stmt->bindParam(3, $outValue1, PDO::PARAM_STR, -1); + $stmt->execute(); + } catch (PDOException $e) { + $error = '*Invalid size for output string parameter 3. Input/output string parameters must have an explicit length.'; + + if (!fnmatch($error, $e->getMessage())) { + var_dump($e->getMessage()); + } + } +} + +function testInvalidDirection($conn, $proc) +{ + global $inValue1, $inValue2, $outValue1; + + // Request input output parameter but do not provide a size + try { + $stmt = $conn->prepare("{CALL $proc (?, ?, ?)}"); + $stmt->bindValue(1, $inValue1); + $stmt->bindValue(2, $inValue2); + $stmt->bindParam(3, $outValue1, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT); + $stmt->execute(); + } catch (PDOException $e) { + $error = '*Invalid direction specified for parameter 3. Input/output parameters must have a length.'; + + if (!fnmatch($error, $e->getMessage())) { + var_dump($e->getMessage()); + } + } +} + +function testInvalidType($conn, $proc) +{ + global $inValue1, $inValue2; + + $outValue = 0.3; + + // Pass an invalid type that is incompatible for the output parameter + try { + $stmt = $conn->prepare("{CALL $proc (?, ?, ?)}"); + $stmt->bindValue(1, $inValue1); + $stmt->bindValue(2, $inValue2); + $stmt->bindParam(3, $outValue, PDO::PARAM_BOOL | PDO::PARAM_INPUT_OUTPUT, 1024); + $stmt->execute(); + } catch (PDOException $e) { + $error = '*Types for parameter value and PDO::PARAM_* constant must be compatible for input/output parameter 3.'; + + if (!fnmatch($error, $e->getMessage())) { + var_dump($e->getMessage()); + } + } +} + +try { + $conn = connect(); + + $proc_scale = getProcName('scale_proc'); + $proc_no_scale = getProcName('noScale_proc'); + + $stmt = $conn->exec("CREATE PROC $proc_scale (@p1 DECIMAL(18, 1), @p2 DECIMAL(18, 1), @p3 CHAR(128) OUTPUT) + AS BEGIN SELECT @p3 = CONVERT(CHAR(128), @p1 + @p2) END"); + + $inValue1 = '2.1'; + $inValue2 = '5.3'; + $outValue1 = '0'; + $outValue2 = '0'; + + // First error case: pass an invalid size for the output parameter + testInvalidSize($conn, $proc_scale); + testInvalidDirection($conn, $proc_scale); + testInvalidType($conn, $proc_scale); + + $stmt = $conn->prepare("{CALL $proc_scale (?, ?, ?)}"); + $stmt->bindValue(1, $inValue1); + $stmt->bindValue(2, $inValue2); + $stmt->bindParam(3, $outValue1, PDO::PARAM_STR, 300); + $stmt->execute(); + + $outValue1 = trim($outValue1); + + $stmt = $conn->exec("CREATE PROC $proc_no_scale (@p1 DECIMAL, @p2 DECIMAL, @p3 CHAR(128) OUTPUT) + AS BEGIN SELECT @p3 = CONVERT(CHAR(128), @p1 + @p2) END"); + + $stmt = $conn->prepare("{CALL $proc_no_scale (?, ?, ?)}"); + $stmt->bindValue(1, $inValue1); + $stmt->bindValue(2, $inValue2); + $stmt->bindParam(3, $outValue2, PDO::PARAM_STR, 300); + $stmt->execute(); + + $outValue2 = trim($outValue2); + + $expected1 = "7.4"; + $expected2 = "7"; + if ($outValue1 == $expected1 && $outValue2 == $expected2) { + echo "Test Successfully done\n"; + } + + dropProc($conn, $proc_scale); + dropProc($conn, $proc_no_scale); + + unset($stmt); + unset($conn); +} catch (Exception $e) { + echo $e->getMessage(); +} +?> + +--EXPECT-- +Test Successfully done diff --git a/test/functional/pdo_sqlsrv/pdostatement_get_set_attr.phpt b/test/functional/pdo_sqlsrv/pdostatement_get_set_attr.phpt index af341baa..087ce296 100644 --- a/test/functional/pdo_sqlsrv/pdostatement_get_set_attr.phpt +++ b/test/functional/pdo_sqlsrv/pdostatement_get_set_attr.phpt @@ -1,5 +1,5 @@ --TEST-- -Test setting and getting various statement attributes. +Test setting and getting various statement attributes with error verifications. --SKIPIF-- --FILE-- @@ -8,40 +8,30 @@ Test setting and getting various statement attributes. function set_stmt_option($conn, $arr) { try { - - $stmt = $conn->prepare( "Select * from temptb", $arr ); + $stmt = $conn->prepare("Select * from temptb", $arr); return $stmt; - } - - catch( PDOException $e) - { + } catch (PDOException $e) { echo $e->getMessage() . "\n\n"; - return NULL; - } + return null; + } } function set_stmt_attr($conn, $attr, $val) { - $stmt = NULL; - try - { + $stmt = null; + try { echo "Set Attribute: " . $attr . "\n"; - $stmt = $conn->prepare( "Select * from temptb"); - } - catch( PDOException $e) - { + $stmt = $conn->prepare("Select * from temptb"); + } catch (PDOException $e) { echo $e->getMessage() . "\n\n"; - return NULL; + return null; } try { $res = $stmt->setAttribute(constant($attr), $val); var_dump($res); echo "\n\n"; - } - - catch( PDOException $e) - { + } catch (PDOException $e) { echo $e->getMessage() . "\n\n"; } return $stmt; @@ -49,27 +39,23 @@ function set_stmt_attr($conn, $attr, $val) function get_stmt_attr($stmt, $attr) { - try - { + try { echo "Get Attribute: " . $attr. "\n"; $res = $stmt->getAttribute(constant($attr)); var_dump($res); echo "\n"; - } - - catch( PDOException $e) - { + } catch (PDOException $e) { echo $e->getMessage() . "\n\n"; - } + } } // valid function Test1($conn) -{ - echo "Test1 - Set stmt option: SQLSRV_ATTR_ENCODING, ATTR_CURSOR, SQLSRV_ATTR_QUERY_TIMEOUT \n"; - set_stmt_option($conn, array(PDO::SQLSRV_ATTR_ENCODING => 3, PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY, PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 44)); - echo "Test Successful\n\n"; -} + { + echo "Test1 - Set stmt option: SQLSRV_ATTR_ENCODING, ATTR_CURSOR, SQLSRV_ATTR_QUERY_TIMEOUT \n"; + set_stmt_option($conn, array(PDO::SQLSRV_ATTR_ENCODING => 3, PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY, PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 44)); + echo "Test Successful\n\n"; + } // invalid function Test2($conn) @@ -84,10 +70,11 @@ function Test3($conn) echo "Test3 \n"; $attr = "PDO::ATTR_CURSOR"; $stmt = set_stmt_attr($conn, $attr, 1); - if($stmt) + if ($stmt) { get_stmt_attr($stmt, $attr); - else + } else { echo "Test3: stmt was null"; + } } // not supported attribute @@ -117,16 +104,33 @@ function Test6($conn) $attr = "PDO::SQLSRV_ATTR_QUERY_TIMEOUT"; $stmt = set_stmt_attr($conn, $attr, 45); get_stmt_attr($stmt, $attr); - } - -try -{ +// PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE=>PDO::SQLSRV_CURSOR_BUFFERED or invalid option +// Expect errors +function Test7($conn) +{ + echo "Test7 - Set stmt option: SQLSRV_ATTR_CURSOR_SCROLL_TYPE \n"; + set_stmt_option($conn, array(PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED)); + + // pass an invalid option + set_stmt_option($conn, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => 10)); +} + +// PDO::SQLSRV_ATTR_DIRECT_QUERY as statement attribute +// Expect error +function Test8($conn) +{ + echo "Test8 - Set stmt attr: SQLSRV_ATTR_DIRECT_QUERY \n"; + $attr = "PDO::SQLSRV_ATTR_DIRECT_QUERY"; + $stmt = set_stmt_attr($conn, $attr, true); +} + +try { include("MsSetup.inc"); - $conn = new PDO( "sqlsrv:Server=$server; Database = $databaseName ", $uid, $pwd); - $conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + $conn = new PDO("sqlsrv:Server=$server; Database = $databaseName ", $uid, $pwd); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $conn->exec("IF OBJECT_ID('temptb', 'U') IS NOT NULL DROP TABLE temptb"); $conn->exec("CREATE TABLE temptb(id INT NOT NULL PRIMARY KEY, val VARCHAR(10)) "); @@ -136,12 +140,12 @@ try test4($conn); test5($conn); test6($conn); - -} - -catch( PDOException $e ) { - - var_dump( $e ); + test7($conn); + test8($conn); + + $conn->exec("DROP TABLE temptb"); +} catch (PDOException $e) { + var_dump($e); exit; } @@ -181,4 +185,12 @@ bool\(true\) Get Attribute: PDO::SQLSRV_ATTR_QUERY_TIMEOUT -int\(45\) \ No newline at end of file +int\(45\) + +Test7 - Set stmt option: SQLSRV_ATTR_CURSOR_SCROLL_TYPE +SQLSTATE\[IMSSP\]: The PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE attribute may only be set when PDO::ATTR_CURSOR is set to PDO::CURSOR_SCROLL in the $driver_options array of PDO::prepare. + +SQLSTATE\[IMSSP\]: The value passed for the 'Scrollable' statement option is invalid. + +Test8 - Set stmt attr: SQLSRV_ATTR_DIRECT_QUERY +SQLSTATE\[IMSSP\]: The PDO::SQLSRV_ATTR_DIRECT_QUERY attribute may only be set in the $driver_options array of PDO::prepare. \ No newline at end of file