Skip to content

Revised a few existing tests that are flawed #1103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ trigger:
jobs:
- job: macOS
pool:
vmImage: 'macOS-10.13'
vmImage: 'macOS-10.14'
steps:
- checkout: self
clean: true
Expand Down
31 changes: 24 additions & 7 deletions test/functional/sqlsrv/AEData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,9 @@ $sqlTypes = array(
function is_incompatible_types_error( $dataType, $sqlType )
{
$errors = sqlsrv_errors();
foreach ( $errors as $error )
{
foreach ($errors as $error) {
// 22018 is the SQLSTATE for the operand crash error for incompatible types
if ( $error['SQLSTATE'] == 22018 )
{
if ($error['SQLSTATE'] == '22018') {
echo "Encrypted $sqlType is incompatible with encrypted $dataType\n";
}
}
Expand All @@ -109,7 +107,6 @@ function get_sqlType_constant( $sqlType )
{
switch ( $sqlType ) {
case 'SQLSRV_SQLTYPE_BIGINT':
case 'SQLSRV_SQLTYPE_BINARY':
case 'SQLSRV_SQLTYPE_BIT':
case 'SQLSRV_SQLTYPE_DATE':
case 'SQLSRV_SQLTYPE_DATETIME':
Expand All @@ -135,6 +132,10 @@ function get_sqlType_constant( $sqlType )
case 'SQLSRV_SQLTYPE_XML':
return constant( $sqlType );
break;
case 'SQLSRV_SQLTYPE_BINARY':
// our tests always use precision 5 for SQLSRV_SQLTYPE_BINARY
return SQLSRV_SQLTYPE_BINARY(5);
break;
case 'SQLSRV_SQLTYPE_CHAR':
// our tests always use precision 5 for SQLSRV_SQLTYPE_CHAR
return SQLSRV_SQLTYPE_CHAR(5);
Expand All @@ -146,7 +147,7 @@ function get_sqlType_constant( $sqlType )
case 'SQLSRV_SQLTYPE_NCHAR':
// our tests always use precision 5 for SQLSRV_SQLTYPE_NCHAR
return SQLSRV_SQLTYPE_NCHAR(5);
break;
break;
case 'SQLSRV_SQLTYPE_NUMERIC':
// our tests always use precision 10 scale 5 for SQLSRV_SQLTYPE_NUMERIC
return SQLSRV_SQLTYPE_NUMERIC(10, 5);
Expand All @@ -157,7 +158,7 @@ function get_sqlType_constant( $sqlType )
}
}

function isDateTimeType( $sqlType )
function isDateTimeType($sqlType)
{
return ($sqlType == 'SQLSRV_SQLTYPE_DATE' ||
$sqlType == 'SQLSRV_SQLTYPE_DATETIME' ||
Expand All @@ -167,4 +168,20 @@ function isDateTimeType( $sqlType )
$sqlType == 'SQLSRV_SQLTYPE_TIME');
}

function isLOBType($sqlType)
{
return ($sqlType == 'SQLSRV_SQLTYPE_TEXT' || $sqlType == 'SQLSRV_SQLTYPE_NTEXT' || $sqlType == 'SQLSRV_SQLTYPE_IMAGE');
}

function isCompatible($compatList, $dataType, $sqlType)
{
foreach ($compatList[$dataType] as $compatType) {
if (stripos($compatType, $sqlType) !== false) {
return true;
}
}

return false;
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ foreach ($dataTypes as $dataType) {
}
}
// 22018 is the SQLSTATE for any incompatible conversion errors
if ($isCompatible && sqlsrv_errors()[0]['SQLSTATE'] == 22018) {
if ($isCompatible && sqlsrv_errors()[0]['SQLSTATE'] == '22018') {
echo "$sqlType should be compatible with $dataType\n";
$success = false;
}
Expand Down
243 changes: 143 additions & 100 deletions test/functional/sqlsrv/sqlsrv_ae_output_param_sqltype_datetime.phpt
Original file line number Diff line number Diff line change
@@ -1,111 +1,154 @@
--TEST--
Test for inserting and retrieving encrypted data of datetime types
--DESCRIPTION--
Bind output params using sqlsrv_prepare with all sql_type
Bind output/inout params using sqlsrv_prepare with all sql_type
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
require_once('MsCommon.inc');
require_once('AEData.inc');

date_default_timezone_set("Canada/Pacific");
$dataTypes = array("date", "datetime", "datetime2", "smalldatetime", "time", "datetimeoffset");
$directions = array("SQLSRV_PARAM_OUT", "SQLSRV_PARAM_INOUT");

// this is a list of implicit datatype conversion that SQL Server allows (https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine)
$compatList = array("date" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetime" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetime2" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"smalldatetime" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"time" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetimeoffset" => array("SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIMEOFFSET") );

$conn = AE\connect();

foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
$success = true;

// create table
$tbname = GetTempTableName("", false);
$colMetaArr = array(new AE\ColumnMeta($dataType, "c_det"), new AE\ColumnMeta($dataType, "c_rand", null, false));
AE\createTable($conn, $tbname, $colMetaArr);

if (AE\isColEncrypted()) {
// Create a Store Procedure
$spname = 'selectAllColumns';
createProc($conn, $spname, "@c_det $dataType OUTPUT, @c_rand $dataType OUTPUT", "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname");
}

<?php
require_once('MsCommon.inc');
require_once('AEData.inc');

date_default_timezone_set("Canada/Pacific");
$dataTypes = array("date", "datetime", "datetime2", "smalldatetime", "time", "datetimeoffset");

$directions = array(SQLSRV_PARAM_OUT, SQLSRV_PARAM_INOUT);

// this is a list of implicit datatype conversion that SQL Server allows (https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine)
$compatList = array("date" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetime" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetime2" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"smalldatetime" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DATE", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"time" => array( "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_TIME", "SQLSRV_SQLTYPE_DATETIMEOFFSET", "SQLSRV_SQLTYPE_DATETIME2"),
"datetimeoffset" => array("SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIMEOFFSET") );

function testOutputParam($conn, $spname, $direction, $dataType, $sqlType)
{
// The driver does not support these types as output params, simply return
if (isDateTimeType($sqlType) || isLOBType($sqlType)) {
return true;
}

global $compatList;

$sqlTypeConstant = get_sqlType_constant($sqlType);

// Call store procedure
$outSql = AE\getCallProcSqlPlaceholders($spname, 2);

// Set these to NULL such that the PHP type of each output parameter is inferred
// from the SQLSRV_SQLTYPE_* constant
$c_detOut = null;
$c_randOut = null;
$stmt = sqlsrv_prepare(
$conn,
$outSql,
array(array( &$c_detOut, $direction, null, $sqlTypeConstant),
array(&$c_randOut, $direction, null, $sqlTypeConstant ))
);
if (!$stmt) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_execute($stmt);

$success = false;
$errors = sqlsrv_errors();
if (AE\IsDataEncrypted()) {
// With data encrypted, errors are totally expected
if (empty($errors)) {
echo "Encrypted data: $dataType should NOT be compatible with $sqlType\n";
} else {
// This should return 22018, the SQLSTATE for any incompatible conversion,
// except the XML type
$success = ($errors[0]['SQLSTATE'] === '22018');
if (!$success) {
if ($sqlType === 'SQLSRV_SQLTYPE_XML') {
$success = ($errors[0]['SQLSTATE'] === '42000');
} else {
echo "Encrypted data: unexpected errors with SQL type: $sqlType\n";
}
}
}
} else {
$compatible = isCompatible($compatList, $dataType, $sqlType);
if ($compatible) {
if (!empty($errors)) {
echo "$dataType should be compatible with $sqlType.\n";
} else {
$success = true;
}
} else {
$implicitConv = 'Implicit conversion from data type ';

// 22018 is the SQLSTATE for any incompatible conversion errors
if ($errors[0]['SQLSTATE'] === '22018') {
$success = true;
} elseif (strpos($errors[0]['message'], $implicitConv) !== false) {
$success = true;
} else {
echo "Failed with SQL type: $sqlType\n";
}
}
}
return $success;
}

////////////////////////////////////////////////////////////////////////////////////////

$conn = AE\connect();

foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
$success = true;

// create table
$tbname = GetTempTableName("", false);
$colMetaArr = array(new AE\ColumnMeta($dataType, "c_det"), new AE\ColumnMeta($dataType, "c_rand", null, false));
AE\createTable($conn, $tbname, $colMetaArr);

// Create a Store Procedure
$spname = 'selectAllColumns';
createProc($conn, $spname, "@c_det $dataType OUTPUT, @c_rand $dataType OUTPUT", "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname");

// insert a row
// Take the second and third entres (some edge cases) from the various
// $[$dataType]_params in AEData.inc
// e.g. with $dataType = 'date', use $date_params[1] and $date_params[2]
// to form an array, namely ["0001-01-01", "9999-12-31"]
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
$r;
$stmt = AE\insertRow($conn, $tbname, array( $colMetaArr[0]->colName => $inputValues[0], $colMetaArr[1]->colName => $inputValues[1] ), $r);
if ($r === false) {
is_incompatible_types_error($dataType, "default type");
}

foreach($directions as $direction) {
echo "Testing as $direction:\n";

// test each SQLSRV_SQLTYPE_ constants
foreach ($sqlTypes as $sqlType) {
if (!AE\isColEncrypted()) {
$isCompatible = false;
foreach ($compatList[$dataType] as $compatType) {
if (stripos($compatType, $sqlType) !== false) {
$isCompatible = true;
}
}
// 22018 is the SQLSTATE for any incompatible conversion errors
$errors = sqlsrv_errors();
if (!empty($errors) && $isCompatible && $errors[0]['SQLSTATE'] == 22018) {
echo "$sqlType should be compatible with $dataType\n";
$success = false;
}
} else {
// skip unsupported datetime types
if (!isDateTimeType($sqlType)) {
$sqlTypeConstant = get_sqlType_constant($sqlType);

// Call store procedure
$outSql = AE\getCallProcSqlPlaceholders($spname, 2);
$c_detOut = '';
$c_randOut = '';
$stmt = sqlsrv_prepare( $conn, $outSql,
array(array( &$c_detOut, SQLSRV_PARAM_OUT, null, $sqlTypeConstant),
array(&$c_randOut, SQLSRV_PARAM_OUT, null, $sqlTypeConstant )));
if (!$stmt) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_execute($stmt);
$errors = sqlsrv_errors();
if (empty($errors) && AE\IsDataEncrypted()) {
// SQLSRV_PHPTYPE_DATETIME not supported
echo "$dataType should not be compatible with any datetime type.\n";
$success = false;
}
}
}
}
}

// cleanup
sqlsrv_free_stmt($stmt);
sqlsrv_query($conn, "TRUNCATE TABLE $tbname");

if ($success) {
echo "Test successfully done.\n";
}

if (AE\isColEncrypted()) {
dropProc($conn, $spname);
}
dropTable($conn, $tbname);
}

sqlsrv_close($conn);
$r;
$stmt = AE\insertRow($conn, $tbname, array( $colMetaArr[0]->colName => $inputValues[0], $colMetaArr[1]->colName => $inputValues[1] ), $r);
if ($r === false) {
fatalError("Failed to insert data of type $dataType\n");
}

foreach ($directions as $direction) {
$dir = ($direction == SQLSRV_PARAM_OUT) ? 'SQLSRV_PARAM_OUT' : 'SQLSRV_PARAM_INOUT';
echo "Testing as $dir:\n";

// test each SQLSRV_SQLTYPE_* constants
foreach ($sqlTypes as $sqlType) {
$success = testOutputParam($conn, $spname, $direction, $dataType, $sqlType);
if (!$success) {
// No point to continue looping
echo("Test failed: $dataType as $sqlType\n");
die(print_r(sqlsrv_errors(), true));
}
}
}

// cleanup
sqlsrv_free_stmt($stmt);
sqlsrv_query($conn, "TRUNCATE TABLE $tbname");

dropProc($conn, $spname);
if ($success) {
echo "Test successfully done.\n";
}
dropTable($conn, $tbname);
}

sqlsrv_close($conn);
?>
--EXPECT--

Expand Down
Loading