Further reverted changes to distinguish datetime fields (#1178)

This commit is contained in:
Jenny Tam 2020-08-12 17:24:39 -07:00 committed by GitHub
parent f02aefd6bd
commit 6349d06fee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 225 additions and 25 deletions

View file

@ -746,32 +746,23 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
ind_ptr = SQL_NULL_DATA;
}
core::SQLBindParameter( stmt, param_num + 1, direction,
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr );
// When calling SQLDescribeParam() on a parameter targeting a Datetime column, the return values for ParameterType, ColumnSize and DecimalDigits are SQL_TYPE_TIMESTAMP, 23, and 3 respectively.
// For a parameter targeting a SmallDatetime column, the return values are SQL_TYPE_TIMESTAMP, 16, and 0. Inputting these values into SQLBindParameter() results in Operand type clash error.
// This is because SQL_TYPE_TIMESTAMP corresponds to Datetime2 by default, and conversion of Datetime2 to Datetime and conversion of Datetime2 to SmallDatatime is not allowed with encrypted columns.
// To fix the conversion problem, set the SQL_CA_SS_SERVER_TYPE field of the parameter to SQL_SS_TYPE_DATETIME and SQL_SS_TYPE_SMALLDATETIME respectively for a Datetime and Smalldatetime column.
// Note this must be called after SQLBindParameter() or SQLSetDescField() may fail.
// TODO: how to correctly distinguish datetime from datetime2(3)? Both have the same decimal_digits and column_size
if (stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP) {
if (decimal_digits == 0 || decimal_digits == 3) {
SQLHDESC hIpd = NULL;
SQLRETURN r = SQL_SUCCESS;
core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
if (hIpd != NULL) {
if (decimal_digits == 0 && column_size == 16) {
r = ::SQLSetDescField(hIpd, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER);
} else if (decimal_digits == 3) {
r = ::SQLSetDescField(hIpd, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER);
}
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
throw core::CoreException();
}
}
if (decimal_digits == 3) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER);
} else if (decimal_digits == 0 && column_size == 16) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER);
}
}
core::SQLBindParameter( stmt, param_num + 1, direction,
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr );
}
catch( core::CoreException& e ){
stmt->free_param_data();

View file

@ -1,7 +1,7 @@
--TEST--
Test for inserting and retrieving encrypted data of datetime and smalldatetime types encrypted
--DESCRIPTION--
Verify that inserting into smalldatetime column might trigger "Datetime field overflow" error
Verify that inserting into smalldatetime column (if encrypted) might trigger "Datetime field overflow" error
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
@ -45,7 +45,7 @@ function createTablePlainQuery($conn, $tableName, $columns)
try {
// This test requires to connect with the Always Encrypted feature
// First check if the system is qualified to run this test
$dsn = getDSN($server, null);
$dsn = "sqlsrv:Server=$server; Database=$databaseName;";
$conn = new PDO($dsn, $uid, $pwd);
$qualified = isAEQualified($conn) && (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
@ -89,9 +89,9 @@ try {
try {
$stmt->execute();
} catch (PDOException $e) {
$error = ($qualified || isColEncrypted())? '*Datetime field overflow' : '*The conversion of a nvarchar data type to a smalldatetime data type resulted in an out-of-range value.';
$error = ($qualified)? '*Datetime field overflow' : '*The conversion of a nvarchar data type to a smalldatetime data type resulted in an out-of-range value.';
if (!fnmatch($error, $e->getMessage())) {
echo "The error message is unexpected:\n";
echo "Expected $error but got:\n";
var_dump($e->getMessage());
}
}

View file

@ -1,7 +1,7 @@
--TEST--
Test for inserting and retrieving encrypted data of datetime and smalldatetime types encrypted
--DESCRIPTION--
Verify that inserting into smalldatetime column might trigger "Datetime field overflow" error
Verify that inserting into smalldatetime column (if encrypted) might trigger "Datetime field overflow" error
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
@ -95,8 +95,9 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
if ($result) {
echo "Inserting invalid values should have failed!\n";
} else {
$error = ($qualified || AE\isDataEncrypted())? '*Datetime field overflow' : '*The conversion of a varchar data type to a smalldatetime data type resulted in an out-of-range value.';
$error = ($qualified)? '*Datetime field overflow' : '*The conversion of a varchar data type to a smalldatetime data type resulted in an out-of-range value.';
if (!fnmatch($error, sqlsrv_errors()[0]['message'])) {
echo "Expected $error but got:\n";
var_dump(sqlsrv_errors());
}
}

View file

@ -0,0 +1,208 @@
--TEST--
variety of connection parameters.
--DESCRIPTION--
The equivalent of sqlsrv_testConnection.phpt but for running outside Windows to verify that GitHub issue 430 is fixed
--SKIPIF--
<?php
require('skipif_azure.inc');
if ((strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN')) {
die("Skip Test on Linux or Mac only.");
}
?>
--FILE--
<?php
require 'MsSetup.inc';
function connect($options=array())
{
require 'MsSetup.inc';
if (!isset($options['UID']) && !isset($options['uid'])) {
$options['uid'] = $uid;
}
if (!isset($options['pwd']) && !isset($options['PWD'])) {
$options['pwd'] = $pwd;
}
if (!isset($options['Database'])) {
$options['database'] = $databaseName;
}
return sqlsrv_connect($server, $options);
}
sqlsrv_configure('WarningsReturnAsErrors', 0);
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);
echo "Test sqlsrv_connect with driver injection\n";
$conn = sqlsrv_connect( $server, array( "UID" => $userName, "PWD" => "$userPassword;Driver={SQL Server Native Client 11.0}}" ));
if (!$conn) {
var_dump(sqlsrv_errors());
} else {
sqlsrv_close($conn);
die("Shouldn't have opened the connection.");
}
echo "Test sqlsrv_connect with driver injection (2)\n";
$conn = sqlsrv_connect( $server, array( "UID" => $userName, "PWD" => "{$userPassword};Driver={SQL Server Native Client 11.0}" ));
if (!$conn) {
var_dump(sqlsrv_errors());
} else {
sqlsrv_close($conn);
die("Shouldn't have opened the connection.");
}
echo "Test sqlsrv_connect with driver injection (3)\n";
$conn = sqlsrv_connect( $server, array( "UID" => "sa", "PWD" => "{$userPassword}};Driver={SQL Server Native Client 11.0}" ));
if (!$conn) {
var_dump(sqlsrv_errors());
} else {
sqlsrv_close($conn);
die("Shouldn't have opened the connection.");
}
// Test a bunch of options. The Failover_Partner keyword does not work
// on Unix, so we replace it with MultiSubnetFailover instead.
$conn_options_all = array( "APP" => "PHP Unit Test",
"ConnectionPooling" => true,
"Database" => $databaseName,
"Encrypt" => 0,
"LoginTimeout" => 120,
"MultipleActiveResultSets" => false,
"QuotedId" => false,
"TraceOn" => true,
"TraceFile" => "trace.odbc",
"TransactionIsolation" => SQLSRV_TXN_READ_COMMITTED,
"TrustServerCertificate" => 1,
"WSID" => "JAYKINT1" );
$conn_options_int = array( "APP" => "PHP Unit Test",
"ConnectionPooling" => false,
"Database" => $databaseName,
"Encrypt" => 0,
"LoginTimeout" => 120,
"MultipleActiveResultSets" => false,
"QuotedId" => true,
"TraceOn" => true,
"TraceFile" => "trace.odbc",
"TransactionIsolation" => SQLSRV_TXN_READ_COMMITTED,
"TrustServerCertificate" => 1,
"WSID" => "JAYKINT1" );
echo "Test sqlsrv_connect with all options\n";
$conn_options_all['MultiSubnetFailover'] = true;
$conn = connect($conn_options_all);
print_r(sqlsrv_errors()[0]);
print_r(sqlsrv_errors()[1]);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
echo "Test sqlsrv_connect with all options and integrated auth\n";
$conn_options_int['MultiSubnetFailover'] = true;
$conn = connect($conn_options_int);
print_r(sqlsrv_errors()[0]);
print_r(sqlsrv_errors()[1]);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo "Test succeeded.\n";
?>
--EXPECTREGEX--
Test sqlsrv_connect with driver injection
array\(1\) \{
\[0\]=>
array\(6\) \{
\[0\]=>
string\(5\) "28000"
\["SQLSTATE"\]=>
string\(5\) "28000"
\[1\]=>
int\(18456\)
\["code"\]=>
int\(18456\)
\[2\]=>
string\(81\) ".*Login failed for user 'sa'."
\["message"\]=>
string\(81\) ".*Login failed for user 'sa'."
\}
\}
Test sqlsrv_connect with driver injection \(2\)
array\(1\) \{
\[0\]=>
array\(6\) \{
\[0\]=>
string\(5\) "IMSSP"
\["SQLSTATE"\]=>
string\(5\) "IMSSP"
\[1\]=>
int\(-4\)
\["code"\]=>
int\(-4\)
\[2\]=>
string\(140\) "An unescaped right brace \(\}\) was found in either the user name or password. All right braces must be escaped with another right brace \(\}\}\)."
\["message"\]=>
string\(140\) "An unescaped right brace \(\}\) was found in either the user name or password. All right braces must be escaped with another right brace \(\}\}\)."
\}
\}
Test sqlsrv_connect with driver injection \(3\)
array\(1\) \{
\[0\]=>
array\(6\) \{
\[0\]=>
string\(5\) "IMSSP"
\["SQLSTATE"\]=>
string\(5\) "IMSSP"
\[1\]=>
int\(-4\)
\["code"\]=>
int\(-4\)
\[2\]=>
string\(140\) "An unescaped right brace \(\}\) was found in either the user name or password. All right braces must be escaped with another right brace \(\}\}\)."
\["message"\]=>
string\(140\) "An unescaped right brace \(\}\) was found in either the user name or password. All right braces must be escaped with another right brace \(\}\}\)."
\}
\}
Test sqlsrv_connect with all options
Array
\(
\[0\] => 01000
\[SQLSTATE\] => 01000
\[1\] => 5701
\[code\] => 5701
\[2\] => .*Changed database context to '.*'.
\[message\] => .*Changed database context to '.*'.
\)
Array
\(
\[0\] => 01000
\[SQLSTATE\] => 01000
\[1\] => 5703
\[code\] => 5703
\[2\] => .*Changed language setting to us_english.
\[message\] => .*Changed language setting to us_english.
\)
Test sqlsrv_connect with all options and integrated auth
Array
\(
\[0\] => 01000
\[SQLSTATE\] => 01000
\[1\] => 5701
\[code\] => 5701
\[2\] => .*Changed database context to '.*'.
\[message\] => .*Changed database context to '.*'.
\)
Array
\(
\[0\] => 01000
\[SQLSTATE\] => 01000
\[1\] => 5703
\[code\] => 5703
\[2\] => .*Changed language setting to us_english.
\[message\] => .*Changed language setting to us_english.
\)
Test succeeded.