Revised a few existing tests that are flawed (#1103)
This commit is contained in:
parent
cd64173f95
commit
8bb6cef33c
|
@ -16,7 +16,7 @@ trigger:
|
|||
jobs:
|
||||
- job: macOS
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
vmImage: 'macOS-10.14'
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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':
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -157,7 +158,7 @@ function get_sqlType_constant( $sqlType )
|
|||
}
|
||||
}
|
||||
|
||||
function isDateTimeType( $sqlType )
|
||||
function isDateTimeType($sqlType)
|
||||
{
|
||||
return ($sqlType == 'SQLSRV_SQLTYPE_DATE' ||
|
||||
$sqlType == 'SQLSRV_SQLTYPE_DATETIME' ||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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--
|
||||
|
||||
|
|
|
@ -5,143 +5,181 @@ Bind output 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');
|
||||
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
require_once('AEData.inc');
|
||||
|
||||
$dataTypes = array("bit", "tinyint", "smallint", "int", "bigint", "decimal(18,5)", "numeric(10,5)", "float", "real" );
|
||||
$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("bit" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"tinyint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"smallint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"int" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"bigint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP" ),
|
||||
"decimal(18,5)" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"numeric(10,5)" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"float" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT"),
|
||||
"real" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT"));
|
||||
$epsilon = 0.0001;
|
||||
|
||||
$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);
|
||||
|
||||
// TODO: It's a good idea to test conversions between different datatypes when AE is off as well.
|
||||
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");
|
||||
}
|
||||
|
||||
$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("bit" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"tinyint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"smallint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"int" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"bigint" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP" ),
|
||||
"decimal(18,5)" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"numeric(10,5)" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT", "SQLSRV_SQLTYPE_TIMESTAMP"),
|
||||
"float" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT"),
|
||||
"real" => array( "SQLSRV_SQLTYPE_BINARY", "SQLSRV_SQLTYPE_VARBINARY", "SQLSRV_SQLTYPE_CHAR", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DATETIME", "SQLSRV_SQLTYPE_SMALLDATETIME", "SQLSRV_SQLTYPE_DECIMAL(18,5)", "SQLSRV_SQLTYPE_NUMERIC(10,5)", "SQLSRV_SQLTYPE_FLOAT", "SQLSRV_SQLTYPE_REAL", "SQLSRV_SQLTYPE_BIGINT", "SQLSRV_SQLTYPE_INT", "SQLSRV_SQLTYPE_SMALLINT", "SQLSRV_SQLTYPE_TINYINT", "SQLSRV_SQLTYPE_MONEY", "SQLSRV_SQLTYPE_SMALLMONEY", "SQLSRV_SQLTYPE_BIT"));
|
||||
|
||||
function compareResults($dataType, $sqlType, $c_detOut, $c_randOut, $inputValues)
|
||||
{
|
||||
$epsilon = 0.0001;
|
||||
$success = true;
|
||||
|
||||
if ($dataType == "float" || $dataType == "real") {
|
||||
if (abs($c_detOut - $inputValues[0]) > $epsilon || abs($c_randOut - $inputValues[1]) > $epsilon) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
$success = false;
|
||||
}
|
||||
} else {
|
||||
if ($c_detOut != $inputValues[0] || $c_randOut != $inputValues[1]) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
function testOutputParam($conn, $spname, $direction, $dataType, $sqlType, $inputValues)
|
||||
{
|
||||
// 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()) {
|
||||
if (empty($errors)) {
|
||||
// With data encrypted, it's a lot stricter, so the results are expected
|
||||
// to be numeric and comparable
|
||||
$success = compareResults($dataType, $sqlType, $c_detOut, $c_randOut, $inputValues);
|
||||
} 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 && empty($errors)) {
|
||||
$success = true;
|
||||
} else {
|
||||
// Even if $dataType is compatible with $sqlType sometimes
|
||||
// we still get errors from the server -- if so, it might
|
||||
// return either SQLSTATE '42000' or '22018' (operand type
|
||||
// clash but only happens with some certain types)
|
||||
// E.g. when converting a bigint to int or an int to numeric,
|
||||
// SQLSTATE '42000' is returned, indicating an error when
|
||||
// converting from one type to another.
|
||||
// TODO 11559: investigate if SQLSTATE '42000' is indeed acceptable
|
||||
$success = ($errors[0]['SQLSTATE'] === '42000' || ($errors[0]['SQLSTATE'] === '22018' && in_array($sqlType, ['SQLSRV_SQLTYPE_XML', 'SQLSRV_SQLTYPE_BINARY', 'SQLSRV_SQLTYPE_VARBINARY', 'SQLSRV_SQLTYPE_UNIQUEIDENTIFIER', 'SQLSRV_SQLTYPE_TIMESTAMP'])));
|
||||
if (!$success) {
|
||||
if ($compatible) {
|
||||
echo "$dataType should be compatible with $sqlType.\n";
|
||||
} 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 = 'decimal(18,5)', use $decimal_params[1] and $decimal_params[2]
|
||||
// to form an array, namely [-9223372036854.80000, 9223372036854.80000]
|
||||
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
|
||||
$r;
|
||||
// convert input values to strings for decimals and numerics
|
||||
if ($dataTypes == "decimal(18,5)" || $dataTypes == "numeric(10,5)") {
|
||||
$stmt = AE\insertRow($conn, $tbname, array( $colMetaArr[0]->colName => (string) $inputValues[0], $colMetaArr[1]->colName => (string) $inputValues[1] ), $r);
|
||||
} else {
|
||||
$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);
|
||||
if ($sqlType == 'SQLSRV_SQLTYPE_FLOAT' || $sqlType == 'SQLSRV_SQLTYPE_REAL') {
|
||||
$c_detOut = 0.0;
|
||||
$c_randOut = 0.0;
|
||||
} else {
|
||||
$c_detOut = 0;
|
||||
$c_randOut = 0;
|
||||
}
|
||||
$stmt = sqlsrv_prepare($conn, $outSql,
|
||||
array(array( &$c_detOut, constant($direction), null, $sqlTypeConstant),
|
||||
array(&$c_randOut, constant($direction), null, $sqlTypeConstant)));
|
||||
|
||||
if (!$stmt) {
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
sqlsrv_execute($stmt);
|
||||
$errors = sqlsrv_errors();
|
||||
|
||||
if (!empty($errors)) {
|
||||
if (stripos("SQLSRV_SQLTYPE_" . $dataType, $sqlType) !== false) {
|
||||
var_dump(sqlsrv_errors());
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (AE\IsDataEncrypted() || stripos("SQLSRV_SQLTYPE_" . $dataType, $sqlType) !== false) {
|
||||
if ($dataType == "float" || $dataType == "real") {
|
||||
if (abs($c_detOut - $inputValues[0]) > $epsilon || abs($c_randOut - $inputValues[1]) > $epsilon) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
$success = false;
|
||||
}
|
||||
} else {
|
||||
if ($c_detOut != $inputValues[0] || $c_randOut != $inputValues[1]) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AE\isColEncrypted()) {
|
||||
dropProc($conn, $spname);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
echo "Test successfully done.\n";
|
||||
}
|
||||
|
||||
dropTable($conn, $tbname);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
$r;
|
||||
// convert input values to strings for decimals and numerics
|
||||
if ($dataTypes == "decimal(18,5)" || $dataTypes == "numeric(10,5)") {
|
||||
$stmt = AE\insertRow($conn, $tbname, array( $colMetaArr[0]->colName => (string) $inputValues[0], $colMetaArr[1]->colName => (string) $inputValues[1] ), $r);
|
||||
} else {
|
||||
$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, $inputValues);
|
||||
if (!$success) {
|
||||
// No point to continue looping
|
||||
echo("Test failed: $dataType as $sqlType\n");
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropProc($conn, $spname);
|
||||
if ($success) {
|
||||
echo "Test successfully done.\n";
|
||||
}
|
||||
|
||||
dropTable($conn, $tbname);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
|
||||
|
|
|
@ -5,117 +5,155 @@ Bind output 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');
|
||||
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
require_once('AEData.inc');
|
||||
|
||||
$dataTypes = array("char(5)", "varchar(max)", "nchar(5)", "nvarchar(max)");
|
||||
$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("char(5)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"varchar(max)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"nchar(5)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"nvarchar(max)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"));
|
||||
|
||||
$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);
|
||||
|
||||
// TODO: It's a good idea to test conversions between different datatypes when AE is off as well.
|
||||
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");
|
||||
}
|
||||
|
||||
// insert a row
|
||||
$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_INOUT, null, $sqlTypeConstant),
|
||||
array(&$c_randOut, SQLSRV_PARAM_INOUT, null, $sqlTypeConstant)));
|
||||
|
||||
if (!$stmt) {
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
sqlsrv_execute($stmt);
|
||||
$errors = sqlsrv_errors();
|
||||
|
||||
if (!empty($errors) ) {
|
||||
if (stripos("SQLSRV_SQLTYPE_" . $dataType, $sqlType) !== false) {
|
||||
var_dump(sqlsrv_errors());
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AE\IsDataEncrypted() || stripos("SQLSRV_SQLTYPE_" . $dataType, $sqlType) !== false) {
|
||||
if ($c_detOut != $inputValues[0] || $c_randOut != $inputValues[1]) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$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("char(5)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"varchar(max)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"nchar(5)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"),
|
||||
"nvarchar(max)" => array( "SQLSRV_SQLTYPE_CHAR(5)", "SQLSRV_SQLTYPE_VARCHAR", "SQLSRV_SQLTYPE_NCHAR(5)", "SQLSRV_SQLTYPE_NVARCHAR", "SQLSRV_SQLTYPE_DECIMAL", "SQLSRV_SQLTYPE_NUMERIC", "SQLSRV_SQLTYPE_NTEXT", "SQLSRV_SQLTYPE_TEXT", "SQLSRV_SQLTYPE_XML"));
|
||||
|
||||
$conn = AE\connect();
|
||||
|
||||
function compareResults($dataType, $sqlType, $c_detOut, $c_randOut, $inputValues)
|
||||
{
|
||||
$success = true;
|
||||
if ($c_detOut != $inputValues[0] || $c_randOut != $inputValues[1]) {
|
||||
echo "Incorrect output retrieved for datatype $dataType and sqlType $sqlType:\n";
|
||||
print(" c_det: " . $c_detOut . "\n");
|
||||
print(" c_rand: " . $c_randOut . "\n");
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
function testOutputParam($conn, $spname, $direction, $dataType, $sqlType, $inputValues)
|
||||
{
|
||||
// The driver does not support these types as output params, simply return
|
||||
if (isDateTimeType($sqlType) || isLOBType($sqlType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
global $compatList;
|
||||
|
||||
if (AE\isColEncrypted()) {
|
||||
dropProc($conn, $spname);
|
||||
}
|
||||
if ($success) {
|
||||
echo "Test successfully done.\n";
|
||||
}
|
||||
dropTable($conn, $tbname);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
$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, SQLSRV_PARAM_INOUT, null, $sqlTypeConstant),
|
||||
array(&$c_randOut, SQLSRV_PARAM_INOUT, null, $sqlTypeConstant))
|
||||
);
|
||||
|
||||
if (!$stmt) {
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
sqlsrv_execute($stmt);
|
||||
|
||||
$success = false;
|
||||
$errors = sqlsrv_errors();
|
||||
if (AE\IsDataEncrypted()) {
|
||||
if (empty($errors)) {
|
||||
// With data encrypted, it's a lot stricter, so the results are expected
|
||||
// to be comparable
|
||||
$success = compareResults($dataType, $sqlType, $c_detOut, $c_randOut, $inputValues);
|
||||
} 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 && empty($errors)) {
|
||||
$success = true;
|
||||
} else {
|
||||
// Even if $dataType is compatible with $sqlType sometimes
|
||||
// we still get errors from the server -- if so, it should
|
||||
// return SQLSTATE '42000', indicating an error when
|
||||
// converting from one type to another
|
||||
// With data NOT encrypted, converting string types to other
|
||||
// types will not return '22018'
|
||||
$success = ($errors[0]['SQLSTATE'] === '42000');
|
||||
if (!$success) {
|
||||
echo "Failed with SQL type: $sqlType\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 from the various $[$dataType]_params in AEData.inc
|
||||
// e.g. with $dataType = 'varchar(max)', use $varchar_params[1] and $varchar_params[2]
|
||||
// to form an array
|
||||
$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) {
|
||||
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, $inputValues);
|
||||
if (!$success) {
|
||||
// No point to continue looping
|
||||
echo("Test failed: $dataType as $sqlType\n");
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropProc($conn, $spname);
|
||||
if ($success) {
|
||||
echo "Test successfully done.\n";
|
||||
}
|
||||
dropTable($conn, $tbname);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
|
||||
|
|
Loading…
Reference in a new issue