From 6349d06fee455362fbab75b4c9a4c5694a9dd248 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 12 Aug 2020 17:24:39 -0700 Subject: [PATCH] Further reverted changes to distinguish datetime fields (#1178) --- source/shared/core_stmt.cpp | 29 +-- .../pdo_ae_insert_datetime_encrypted.phpt | 8 +- .../sqlsrv_ae_insert_datetime_encrypted.phpt | 5 +- .../sqlsrv/sqlsrv_testConnection_unix.phpt | 208 ++++++++++++++++++ 4 files changed, 225 insertions(+), 25 deletions(-) create mode 100644 test/functional/sqlsrv/sqlsrv_testConnection_unix.phpt diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 99e38977..bbe86505 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -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(); diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_datetime_encrypted.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_datetime_encrypted.phpt index 136e8559..05d7c64e 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_datetime_encrypted.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_datetime_encrypted.phpt @@ -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-- --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()); } } diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_datetime_encrypted.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_datetime_encrypted.phpt index 62d8116d..f57dab9c 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_datetime_encrypted.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_datetime_encrypted.phpt @@ -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-- --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()); } } diff --git a/test/functional/sqlsrv/sqlsrv_testConnection_unix.phpt b/test/functional/sqlsrv/sqlsrv_testConnection_unix.phpt new file mode 100644 index 00000000..1ce20395 --- /dev/null +++ b/test/functional/sqlsrv/sqlsrv_testConnection_unix.phpt @@ -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-- + +--FILE-- + $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.