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,20 +746,41 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
ind_ptr = SQL_NULL_DATA; 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. // 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. // 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. // 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. // 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 (stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP) {
if (decimal_digits == 3) if (decimal_digits == 0 || decimal_digits == 3) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER); SQLHDESC hIpd = NULL;
else if (decimal_digits == 0) core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER ); 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 ){ catch( core::CoreException& e ){
stmt->free_param_data(); stmt->free_param_data();
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS ); SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );

View file

@ -31,7 +31,7 @@ function compareDate($dtout, $dtin, $dataType) {
} }
$dataTypes = array("datetime2", "datetimeoffset", "time"); $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"), $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"), "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")); "time" => array("00:00:00", "23:59:59"));
@ -101,6 +101,10 @@ try {
} }
?> ?>
--EXPECT-- --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): Testing datetime2(1):
****Retrieving datetime2(1) as PDO::PARAM_STR is supported**** ****Retrieving datetime2(1) as PDO::PARAM_STR is supported****
****Retrieving datetime2(1) as PDO::PARAM_LOB 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_STR is supported****
****Retrieving datetime2(7) as PDO::PARAM_LOB 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): Testing datetimeoffset(1):
****Retrieving datetimeoffset(1) as PDO::PARAM_STR is supported**** ****Retrieving datetimeoffset(1) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(1) as PDO::PARAM_LOB 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_STR is supported****
****Retrieving datetimeoffset(7) as PDO::PARAM_LOB 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): Testing time(1):
****Retrieving time(1) as PDO::PARAM_STR is supported**** ****Retrieving time(1) as PDO::PARAM_STR is supported****
****Retrieving time(1) as PDO::PARAM_LOB is supported**** ****Retrieving time(1) as PDO::PARAM_LOB is supported****

View file

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

View file

@ -29,7 +29,7 @@ function compareDate($dtout, $dtin, $dataType) {
} }
$dataTypes = array("datetime2", "datetimeoffset", "time"); $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"), $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"), "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")); "time" => array("00:00:00", "23:59:59"));
@ -102,6 +102,12 @@ try {
} }
?> ?>
--EXPECT-- --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): Testing datetime2(1):
****Conversion from PDO::PARAM_BOOL to datetime2(1) is supported**** ****Conversion from PDO::PARAM_BOOL to datetime2(1) is supported****
****Conversion from PDO::PARAM_INT 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_STR to datetime2(7) is supported****
****Conversion from PDO::PARAM_LOB 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): Testing datetimeoffset(1):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(1) is supported**** ****Conversion from PDO::PARAM_BOOL to datetimeoffset(1) is supported****
****Conversion from PDO::PARAM_INT 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_STR to datetimeoffset(7) is supported****
****Conversion from PDO::PARAM_LOB 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): Testing time(1):
****Conversion from PDO::PARAM_BOOL to time(1) is supported**** ****Conversion from PDO::PARAM_BOOL to time(1) is supported****
****Conversion from PDO::PARAM_INT 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); dropTable($conn, $tableName);
// Define the column definitions // 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) { if ($qualified) {
$tsql = createTableEncryptedQuery($conn, $tableName, $columns); $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 // Insert values that cause errors
$val1 = '9999-12-31 23:59:59'; $val1 = '9999-12-31 23:59:59';
$val2 = null; $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 (?,?,?,?)";
$params = array($val1, $val2, $val3); $params = array($val1, $val2, $val3, $val4);
$stmt = sqlsrv_prepare($conn, $tsql, $params); $stmt = sqlsrv_prepare($conn, $tsql, $params);
if (!$stmt) { if (!$stmt) {
@ -105,8 +106,9 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
// These values should work // These values should work
$val1 = '2021-11-03 11:49:00'; $val1 = '2021-11-03 11:49:00';
$val2 = '2015-10-23 07:03:00.000'; $val2 = '2015-10-23 07:03:00.000';
$val3 = '0001-01-01 01:01:01';
$params = array($val1, $val2, $val3); $params = array($val1, $val2, $val3, $val4);
$stmt = sqlsrv_prepare($conn, $tsql, $params); $stmt = sqlsrv_prepare($conn, $tsql, $params);
if (!$stmt) { if (!$stmt) {
fatalError("Failed to prepare insert statement"); fatalError("Failed to prepare insert statement");
@ -138,12 +140,14 @@ Verify that inserting into smalldatetime column might trigger "Datetime field ov
?> ?>
--EXPECT-- --EXPECT--
array(3) { array(4) {
["c1"]=> ["c1"]=>
string(19) "2021-11-03 11:49:00" string(19) "2021-11-03 11:49:00"
["c2"]=> ["c2"]=>
string(23) "2015-10-23 07:03:00.000" string(23) "2015-10-23 07:03:00.000"
["c3"]=> ["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 Done