PDO tests for retrieving data as output params using various PDO param types

This commit is contained in:
Jenny Tam 2018-03-13 14:04:40 -07:00
parent a24a8a7035
commit 7a6a8d5a72
7 changed files with 1373 additions and 0 deletions

View file

@ -0,0 +1,210 @@
--TEST--
Test for retrieving encrypted data of binary types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("binary", "varbinary", "varbinary(max)");
$lengths = array(1, 2, 4, 8, 64, 512, 4000);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "07006" => "Restricted data type attribute violation");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function convert2Hex($ch, $length)
{
// Without AE, the binary values returned as integers will
// have lengths no more than 4 times the original hex string value
// (e.g. string(8) "65656565") - limited by the buffer sizes
if (!isAEConnected()) {
$count = ($length <= 2) ? $length : 4;
} else {
$count = $length;
}
return str_repeat(bin2hex($ch), $count);
}
function testOutputBinary($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_binary_types";
$spname = "test_binary_proc";
$ch0 = 'd';
$ch1 = 'e';
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat($ch0, $length);
$input1 = str_repeat($ch1, $length);
$ord0 = convert2Hex($ch0, $length);
$ord1 = convert2Hex($ch1, $length);
insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $input0, "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY"),
"c_rand" => new BindParamOp(2, $input1, "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY")), "prepareBindParam");
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout && $pdoParamType != PDO::PARAM_STR) {
// Currently do not support getting binary as strings + INOUT param
// See VSO 2829 for details
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$det = "";
$rand = "";
if ($pdoParamType == PDO::PARAM_STR || $pdoParamType == PDO::PARAM_LOB) {
$stmt->bindParam(1, $det, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY);
$stmt->bindParam(2, $rand, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY);
} elseif ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$det = $rand = 0;
$stmt->bindParam(1, $det, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
$stmt->bindParam(2, $rand, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
} else {
$stmt->bindParam(1, $det, $paramType, $length);
$stmt->bindParam(2, $rand, $paramType, $length);
}
try {
$stmt->execute();
$errMsg = "****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_STR) {
if ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
// for boolean values, they should all be bool(true)
// because all floats are non-zeroes
// This only occurs without AE
// With AE enabled, this would have caused an exception
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType is PDO::PARAM_INT
// This only occurs without AE -- likely a rare use case
// With AE enabled, this would have caused an exception
if (strval($det) != $ord0 || strval($rand) != $ord1) {
printValues($errMsg, $det, $rand, $ord0, $ord1);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL || PDO::PARAM_INT) {
if (isAEConnected()) {
if ($pdoParamType == PDO::PARAM_INT) {
// Expected to fail with this message
$error = "String data, right truncated for output parameter";
$found = strpos($message, $error);
} else {
// PDO::PARAM_BOOL -
// Expected error 07006 with AE enabled:
// "Restricted data type attribute violation"
// The data value returned for a parameter bound as
// SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not
// be converted to the data type identified by the
// ValueType argument in SQLBindParameter.
$found = strpos($message, $errors['07006']);
}
} else {
// When not AE enabled, expected to fail with something like this message
// "Implicit conversion from data type nvarchar(max) to binary is not allowed. Use the CONVERT function to run this query."
// Sometimes it's about nvarchar too
$error = "to $dataType is not allowed. Use the CONVERT function to run this query.";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// catch all
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputBinary(false);
testOutputBinary(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_binary_types";
$spname = "test_binary_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,171 @@
--TEST--
Test for retrieving encrypted data of char types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("char", "varchar", "varchar(max)");
$lengths = array(1, 8, 64, 512, 4000);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.",
"22003" => "Numeric value out of range");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function testOutputChars($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_char_types";
$spname = "test_char_proc";
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat("1", $length);
$input1 = str_repeat("2", $length);
insertRow($conn, $tbname, array("c_det" => $input0,
"c_rand" => $input1));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = $length;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($length < 64 && $pdoParamType != PDO::PARAM_STR) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// For boolean values, they should all be bool(true)
// because all "string literals" are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType = PDO::PARAM_INT
// Expect numeric values
if ($det != intval($input0) || $rand != intval($input1)) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
} elseif ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
$found = strpos($message, $errors['22003']);
} else {
// When not AE enabled, expected to fail to convert
// whatever char type to integers
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputChars(false);
testOutputChars(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_char_types";
$spname = "test_char_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,204 @@
--TEST--
Test for retrieving encrypted data of datetimes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0, */1, 2, 4, 7);
$inputValuesInit = array("datetime2" => array("0001-01-01 00:00:00", "9999-12-31 23:59:59"),
"datetimeoffset" => array("0001-01-01 00:00:00 -14:00", "9999-12-31 23:59:59 +14:00"),
"time" => array("00:00:00", "23:59:59"));
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "07006" => "Restricted data type attribute violation");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
// compareDate() returns true when the date/time values are basically the same
// e.g. 00:00:00.000 is the same as 00:00:00
function compareDate($dtout, $dtin, $dataType)
{
if ($dataType == "datetimeoffset") {
$dtarr = explode(' ', $dtin);
if (strpos($dtout, $dtarr[0]) !== false && strpos($dtout, $dtarr[1]) !== false && strpos($dtout, $dtarr[2]) !== false) {
return true;
}
} else {
if (strpos($dtout, $dtin) !== false) {
return true;
}
}
return false;
}
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function testOutputDatetimes($inout)
{
global $pdoParamTypes, $dataTypes, $precisions, $inputValuesInit, $errors;
try {
$conn = connect();
$tbname = "test_datetimes_types";
$spname = "test_datetimes_proc";
foreach ($dataTypes as $dataType) {
foreach ($precisions as $precision) {
// change the input values depending on the precision
$inputValues[0] = $inputValuesInit[$dataType][0];
$inputValues[1] = $inputValuesInit[$dataType][1];
if ($precision != 0) {
if ($dataType == "datetime2") {
$inputValues[1] .= "." . str_repeat("9", $precision);
} else if ($dataType == "datetimeoffset") {
$inputPieces = explode(" ", $inputValues[1]);
$inputValues[1] = $inputPieces[0] . " " . $inputPieces[1] . "." . str_repeat("9", $precision) . " " . $inputPieces[2];
} else if ($dataType == "time") {
$inputValues[0] .= "." . str_repeat("0", $precision);
$inputValues[1] .= "." . str_repeat("9", $precision);
}
}
$type = "$dataType($precision)";
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$stmt = insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1] ), null);
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = 0;
$rand = 0;
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
// What follows only happens with OUTPUT parameter
if ($inout) {
echo "Any datetime type as INOUT param should have caused an exception!\n";
}
if ($pdoParamType == PDO::PARAM_INT) {
// Expect an integer, the first part of the date time string
$ch = ($dataType == "time")? ':' : '-';
$tmp0 = explode($ch, $inputValues[0]);
$tmp1 = explode($ch, $inputValues[1]);
if ($det != intval($tmp0[0]) || $rand != intval($tmp1[0])) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif (!compareDate($det, $inputValues[0], $dataType) ||
!compareDate($rand, $inputValues[1], $dataType)) {
printValues($errMsg, $det, $rand, $inputValues);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:\n$message****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
} elseif (isAEConnected()) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// Expected error 07006: "Restricted data type attribute violation"
// What does this error mean?
// The data value returned for a parameter bound as
// SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not
// be converted to the data type identified by the
// ValueType argument in SQLBindParameter.
$found = strpos($message, $errors['07006']);
} else {
$error = "Invalid character value for cast specification";
$found = strpos($message, $error);
}
} else {
if ($pdoParamType == PDO::PARAM_BOOL) {
$error = "Operand type clash: int is incompatible with $dataType";
} else {
$error = "Error converting data type nvarchar to $dataType";
}
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputDatetimes(false);
testOutputDatetimes(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_datetimes_types";
$spname = "test_datetimes_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,242 @@
--TEST--
Test for retrieving encrypted data of decimals/numerics as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("decimal", "numeric");
$precisions = array(1 => array(0, 1),
4 => array(0, 1, 4),
16 => array(0, 1, 4, 16),
38 => array(0, 1, 4, 16, 38));
$inputValuesInit = array(92233720368547758089223372036854775808, -92233720368547758089223372036854775808);
$inputPrecision = 38;
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
// this function returns true if the floats are more different than expected
function compareFloats($actual, $expected)
{
$epsilon = 0.00001;
$diff = abs(($actual - $expected) / $expected);
return ($diff > $epsilon);
}
// function compareIntegers() returns false when the fetched values
// are different from the expected inputs
function compareIntegers($det, $rand, $inputValues, $pdoParamType)
{
///////////////////////////////////////////////////////////////////////
// See GitHub issue 707 - Fix this method when the problem is addressed
//
// Assume $pdoParamType is PDO::PARAM_BOOL or PDO::PARAM_INT
if (is_string($det)) {
return (!compareFloats(floatval($det), $inputValues[0])
&& !compareFloats(floatval($rand), $inputValues[1]));
} elseif ($pdoParamType == PDO::PARAM_INT) {
$input0 = floor($inputValues[0]); // the positive float
$input1 = ceil($inputValues[1]); // the negative float
return ($det == $input0 && $rand == $input1);
} else {
// $pdoParamType == PDO::PARAM_BOOL
// Expect bool(true) or bool(false) depending on the rounded input values
// But with AE enabled (aforementioned GitHub issue), the fetched values
// are floats instead, which should be fixed
$input0 = floor($inputValues[0]); // the positive float
$input1 = ceil($inputValues[1]); // the negative float
if (isAEConnected()) {
$det = boolval(floor($det));
$rand = boolval(ceil($rand));
}
return ($det == boolval($input0) && $rand == boolval($input1));
}
}
// function compareDecimals() returns false when the fetched values
// are different from the inputs, based on precision, scale
function compareDecimals($det, $rand, $inputValues, $pdoParamType, $precision, $scale)
{
// Assume $pdoParamType is PDO::PARAM_STR
for ($i = 0; $i < 2; $i++) {
$inputStr = strval($inputValues[$i]);
$fetchedStr = ($i == 0) ? strval(floatval($det)) : strval(floatval($rand));
if ($precision == $scale) {
// compare up to $precision + digits left if radix point ('.') +
// 1 digit ('.') + possibly the negative sign
$len = $precision + 2 + $i;
} elseif ($scale > 0) {
// compare up to $precision + 1 digit ('.')
// + possibly the negative sign
$len = $precision + 1 + $i;
} else {
// in this case, $scale = 0
// compare up to $precision + possibly the negative sign
$len = $precision + $i;
}
trace("Comparing $len...");
$result = substr_compare($inputStr, $fetchedStr, 0, $len);
if ($result != 0) {
return false;
}
}
return true;
}
function testOutputDecimals($inout)
{
global $pdoParamTypes, $dataTypes, $inputValuesInit, $precisions, $inputPrecision, $errors;
try {
$conn = connect();
$tbname = "test_decimals_types";
$spname = "test_decimals_proc";
foreach ($dataTypes as $dataType) {
foreach ($precisions as $precision => $scales) {
foreach ($scales as $scale) {
// construct the input values depending on the precision and scale
$precDiff = $inputPrecision - ($precision - $scale);
$inputValues = $inputValuesInit;
foreach ($inputValues as &$inputValue) {
$inputValue = $inputValue / pow(10, $precDiff);
}
$type = "$dataType($precision, $scale)";
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$stmt = insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1] ), null);
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = $rand = 0.0;
$stmt = $conn->prepare($outSql);
$len = 2048;
// Do not initialize $det or $rand as empty strings
// See VSO 2915 for details
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
if (!compareIntegers($det, $rand, $inputValues, $pdoParamType)) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
// When $pdoParamType is PDO::PARAM_STR, the accuracies
// should have been preserved based on the original
// precision and scale, so compare the retrieved values
// against the input values with more details
if (!compareDecimals($det, $rand, $inputValues, $pdoParamType, $precision, $scale)) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif (!isAEConnected() && $precision >= 16 && $pdoParamType == PDO::PARAM_BOOL) {
// When not AE enabled, large numbers are expected to
// fail when converting to booleans
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputDecimals(false);
testOutputDecimals(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_decimals_types";
$spname = "test_decimals_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,181 @@
--TEST--
Test for retrieving encrypted data of floats as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
// this function returns true if the floats are more different than expected
function compareFloats($actual, $expected)
{
$epsilon = 0.00001;
$diff = abs(($actual - $expected) / $expected);
return ($diff > $epsilon);
}
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function testOutputFloats($fetchNumeric, $inout)
{
global $pdoParamTypes, $inputValues, $errors;
try {
$conn = connect();
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE, $fetchNumeric);
$tbname = "test_floats_types";
$spname = "test_floats_proc";
$bits = array(1, 12, 24, 36, 53);
foreach ($bits as $bit) {
$type = "float($bit)";
trace("\nTesting $type:\n");
$inputValues = array();
// create random input values
for ($i = 0; $i < 2; $i++) {
$mantissa = rand(1, 100000000);
$decimals = rand(1, 100000000);
$floatNum = $mantissa + $decimals / 10000000;
if ($i > 0) {
// make the second input negative
$floatNum *= -1;
}
array_push($inputValues, $floatNum);
}
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
insertRow($conn,
$tbname,
array("c_det" => new BindParamOp(1, $inputValues[0], 'PDO::PARAM_INT'),
"c_rand" => new BindParamOp(2, $inputValues[1], 'PDO::PARAM_INT')),
"prepareBindParam");
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
if ($pdoParamType == PDO::PARAM_INT && strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') {
// Bug 2876 in VSO: Linux - when retrieving a float as OUTPUT
// or INOUT parameter with PDO::PARAM_INT, the returned values
// are always single digits, regardless of the original floats
continue;
}
$det = 0.0;
$rand = 0.0;
$stmt = $conn->prepare($outSql);
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = 0;
$rand = 0;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_BOOL) {
// for boolean values, they should all be bool(true)
// because all floats are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
// Compare the retrieved values against the input values
// if either of them is very different, print them all
if (compareFloats(floatval($det), $inputValues[0]) ||
compareFloats(floatval($rand), $inputValues[1])) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputFloats(false, false);
testOutputFloats(true, false);
testOutputFloats(false, true);
testOutputFloats(true, true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_floats_types";
$spname = "test_floats_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,193 @@
--TEST--
Test for retrieving encrypted data of integral types as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("bit", "tinyint", "smallint", "int", "bigint");
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "22003" => "Numeric value out of range", "42000" => "Error converting data type bigint to int");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function generateInputs($dataType)
{
// create random input values based on data types
// make the second input negative but only for some data types
if ($dataType == "bit") {
$inputValues = array(0, 1);
} elseif ($dataType == "tinyint") {
$inputValues = array();
for ($i = 0; $i < 2; $i++) {
$randomNum = rand(0, 255);
array_push($inputValues, $randomNum);
}
} else {
switch ($dataType) {
case "smallint":
$max = 32767;
break;
case "int":
$max = 2147483647;
break;
default:
$max = getrandmax();
}
$inputValues = array();
for ($i = 0; $i < 2; $i++) {
$randomNum = rand(0, $max);
if ($i > 0) {
// make the second input negative but only for some data types
$randomNum *= -1;
}
array_push($inputValues, $randomNum);
if (traceMode()) {
echo "input: "; var_dump($inputValues[$i]);
}
}
}
return $inputValues;
}
function testOutputInts($inout)
{
global $pdoParamTypes, $dataTypes, $errors;
try {
$conn = connect();
$tbname = "test_integers_types";
$spname = "test_integers_proc";
foreach ($dataTypes as $dataType) {
trace("\nTesting $dataType:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = generateInputs($dataType);
insertRow($conn, $tbname, array("c_det" => $inputValues[0],
"c_rand" => $inputValues[1]));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $dataType OUTPUT, @c_rand $dataType OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = 0;
$rand = 0;
$stmt = $conn->prepare($outSql);
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_STR) {
if ($det !== strval($inputValues[0]) || $rand !== strval($inputValues[1])) {
// comparisons between strings, use '!=='
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif ($pdoParamType == PDO::PARAM_INT || $pdoParamType == PDO::PARAM_BOOL) {
// comparisons between integers and booleans, do not use '!=='
if ($det != $inputValues[0] || $rand != $inputValues[1]) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif ($dataType == "bigint" && ($pdoParamType == PDO::PARAM_INT || $pdoParamType == PDO::PARAM_BOOL)) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
// This is expected when converting big integer to integer or bool
$found = strpos($message, $errors['22003']);
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
// Expected error 42000: "Error converting data type bigint to int"
// This is expected when not AE connected and converting big integer to bool
$found = strpos($message, $errors['42000']);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputInts(false);
testOutputInts(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_integers_types";
$spname = "test_integers_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,172 @@
--TEST--
Test for retrieving encrypted data of nchar types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
Note: Because the maximum allowable table row size is 8060 bytes, 7 bytes of which are reserved for internal overhead. In other words, this allows up to two nvarchar() columns with length slightly
more than 2000 wide characters. Therefore, the max length in this test is 2010.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("nchar", "nvarchar", "nvarchar(max)");
$lengths = array(1, 8, 64, 512, 2010);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "22003" => "Numeric value out of range");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function testOutputNChars($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_nchar_types";
$spname = "test_nchar_proc";
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat("1", $length);
$input1 = str_repeat("2", $length);
insertRow($conn, $tbname, array("c_det" => $input0,
"c_rand" => $input1));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = $length;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
// When $length >= 64, a string is returned regardless of $pdoParamType
if ($length < 64 && $pdoParamType != PDO::PARAM_STR) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// For boolean values, they should all be bool(true)
// because all "string literals" are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType = PDO::PARAM_INT
// Expect numeric values
if ($det != intval($input0) || $rand != intval($input1)) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
} elseif ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
$found = strpos($message, $errors['22003']);
} else {
// When not AE enabled, expected to fail to convert
// whatever char type to integers
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputNChars(false);
testOutputNChars(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_nchar_types";
$spname = "test_nchar_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done