Updated checks to distinguish smalldatetime, datetime and datetime2 fields (#1175)

This commit is contained in:
Jenny Tam 2020-08-07 18:33:12 -07:00 committed by GitHub
parent 9365fc5201
commit 284aca85ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 22 deletions

View file

@ -746,19 +746,40 @@ 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.
// Check SQL_DESC_OCTET_LENGTH of the implementation parameter descriptor (IPD) to distinguish Smalldatetime or Datetime fields from Datetime2(0) or Datetime2(3) fields, as described in
// https://docs.microsoft.com/sql/relational-databases/native-client-odbc-date-time/metadata-parameter-and-result
// This has to be done before SQLBindParameter()
if (stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP) {
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)
core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER );
if (decimal_digits == 0 || decimal_digits == 3) {
SQLHDESC hIpd = NULL;
core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
if (hIpd != NULL) {
SQLULEN octetLength = 0;
SQLINTEGER dummy = 0;
SQLRETURN r = ::SQLGetDescField(hIpd, param_num + 1, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)&octetLength, 0, &dummy);
if (SQL_SUCCEEDED(r)) {
// The octet length for datetime2 is 16 but no action required
if (octetLength == 8) {
r = ::SQLSetDescField(hIpd, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER);
} else if (octetLength == 4) {
r = ::SQLSetDescField(hIpd, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER);
}
}
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
throw core::CoreException();
}
}
}
}
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

@ -31,7 +31,7 @@ function compareDate($dtout, $dtin, $dataType) {
}
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0,*/ 1, 2, 4, 7);
$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"));
@ -101,6 +101,10 @@ try {
}
?>
--EXPECT--
Testing datetime2(0):
****Retrieving datetime2(0) as PDO::PARAM_STR is supported****
****Retrieving datetime2(0) as PDO::PARAM_LOB is supported****
Testing datetime2(1):
****Retrieving datetime2(1) as PDO::PARAM_STR is supported****
****Retrieving datetime2(1) as PDO::PARAM_LOB is supported****
@ -117,6 +121,10 @@ Testing datetime2(7):
****Retrieving datetime2(7) as PDO::PARAM_STR is supported****
****Retrieving datetime2(7) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(0):
****Retrieving datetimeoffset(0) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(0) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(1):
****Retrieving datetimeoffset(1) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(1) as PDO::PARAM_LOB is supported****
@ -133,6 +141,10 @@ Testing datetimeoffset(7):
****Retrieving datetimeoffset(7) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(7) as PDO::PARAM_LOB is supported****
Testing time(0):
****Retrieving time(0) as PDO::PARAM_STR is supported****
****Retrieving time(0) as PDO::PARAM_LOB is supported****
Testing time(1):
****Retrieving time(1) as PDO::PARAM_STR is supported****
****Retrieving time(1) as PDO::PARAM_LOB is supported****

View file

@ -63,7 +63,7 @@ try {
dropTable($conn, $tableName);
// Define the column definitions
$columns = array('c1' => 'smalldatetime', 'c2' => 'datetime', 'c3' => 'datetime2(4)');
$columns = array('c1' => 'smalldatetime', 'c2' => 'datetime', 'c3' => 'datetime2(0)', 'c4' => 'datetime2(3)');
if ($qualified) {
$tsql = createTableEncryptedQuery($conn, $tableName, $columns);
@ -75,14 +75,16 @@ try {
// Insert values that cause errors
$val1 = '9999-12-31 23:59:59';
$val2 = null;
$val3 = '9999-12-31 23:59:59.9999';
$val3 = null;
$val4 = '9999-12-31 23:59:59.999';
$tsql = "INSERT INTO $tableName (c1, c2, c3) VALUES (?,?,?)";
$tsql = "INSERT INTO $tableName (c1, c2, c3, c4) VALUES (?,?,?,?)";
$stmt = $conn->prepare($tsql);
$stmt->bindParam(1, $val1);
$stmt->bindParam(2, $val2);
$stmt->bindParam(3, $val3);
$stmt->bindParam(4, $val4);
try {
$stmt->execute();
@ -97,6 +99,7 @@ try {
// These values should work
$val1 = '2021-11-03 11:49:00';
$val2 = '2015-10-23 07:03:00.000';
$val3 = '0001-01-01 01:01:01';
try {
$stmt->execute();
@ -128,12 +131,14 @@ echo "Done\n";
?>
--EXPECT--
array(3) {
array(4) {
[0]=>
string(19) "2021-11-03 11:49:00"
[1]=>
string(23) "2015-10-23 07:03:00.000"
[2]=>
string(24) "9999-12-31 23:59:59.9999"
string(19) "0001-01-01 01:01:01"
[3]=>
string(23) "9999-12-31 23:59:59.999"
}
Done

View file

@ -29,7 +29,7 @@ function compareDate($dtout, $dtin, $dataType) {
}
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0,*/ 1, 2, 4, 7);
$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"));
@ -102,6 +102,12 @@ try {
}
?>
--EXPECT--
Testing datetime2(0):
****Conversion from PDO::PARAM_BOOL to datetime2(0) is supported****
****Conversion from PDO::PARAM_INT to datetime2(0) is supported****
****Conversion from PDO::PARAM_STR to datetime2(0) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(0) is supported****
Testing datetime2(1):
****Conversion from PDO::PARAM_BOOL to datetime2(1) is supported****
****Conversion from PDO::PARAM_INT to datetime2(1) is supported****
@ -126,6 +132,12 @@ Testing datetime2(7):
****Conversion from PDO::PARAM_STR to datetime2(7) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(7) is supported****
Testing datetimeoffset(0):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(0) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(0) is supported****
****Conversion from PDO::PARAM_STR to datetimeoffset(0) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(0) is supported****
Testing datetimeoffset(1):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(1) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(1) is supported****
@ -150,6 +162,12 @@ Testing datetimeoffset(7):
****Conversion from PDO::PARAM_STR to datetimeoffset(7) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(7) is supported****
Testing time(0):
****Conversion from PDO::PARAM_BOOL to time(0) is supported****
****Conversion from PDO::PARAM_INT to time(0) is supported****
****Conversion from PDO::PARAM_STR to time(0) is supported****
****Conversion from PDO::PARAM_LOB to time(0) is supported****
Testing time(1):
****Conversion from PDO::PARAM_BOOL to time(1) is supported****
****Conversion from PDO::PARAM_INT to time(1) is supported****

View file

@ -65,7 +65,7 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
dropTable($conn, $tableName);
// Define the column definitions
$columns = array('c1' => 'smalldatetime', 'c2' => 'datetime', 'c3' => 'datetime2(4)');
$columns = array('c1' => 'smalldatetime', 'c2' => 'datetime', 'c3' => 'datetime2(0)', 'c4' => 'datetime2(3)');
if ($qualified) {
$tsql = createTableEncryptedQuery($conn, $tableName, $columns);
@ -81,10 +81,11 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
// Insert values that cause errors
$val1 = '9999-12-31 23:59:59';
$val2 = null;
$val3 = '9999-12-31 23:59:59.9999';
$val3 = null;
$val4 = '9999-12-31 23:59:59.999';
$tsql = "INSERT INTO $tableName (c1, c2, c3) VALUES (?,?,?)";
$params = array($val1, $val2, $val3);
$tsql = "INSERT INTO $tableName (c1, c2, c3, c4) VALUES (?,?,?,?)";
$params = array($val1, $val2, $val3, $val4);
$stmt = sqlsrv_prepare($conn, $tsql, $params);
if (!$stmt) {
@ -105,8 +106,9 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
// These values should work
$val1 = '2021-11-03 11:49:00';
$val2 = '2015-10-23 07:03:00.000';
$params = array($val1, $val2, $val3);
$val3 = '0001-01-01 01:01:01';
$params = array($val1, $val2, $val3, $val4);
$stmt = sqlsrv_prepare($conn, $tsql, $params);
if (!$stmt) {
fatalError("Failed to prepare insert statement");
@ -138,12 +140,14 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
?>
--EXPECT--
array(3) {
array(4) {
["c1"]=>
string(19) "2021-11-03 11:49:00"
["c2"]=>
string(23) "2015-10-23 07:03:00.000"
["c3"]=>
string(24) "9999-12-31 23:59:59.9999"
string(19) "0001-01-01 01:01:01"
["c4"]=>
string(23) "9999-12-31 23:59:59.999"
}
Done