Skip to content

Commit

Permalink
Added new tests for ReturnDatesAsStrings at statement level
Browse files Browse the repository at this point in the history
  • Loading branch information
yitam committed Sep 14, 2018
1 parent d049f59 commit b149fdc
Show file tree
Hide file tree
Showing 4 changed files with 423 additions and 0 deletions.
10 changes: 10 additions & 0 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error
last_field_index( -1 ),
past_next_result_end( false ),
query_timeout( QUERY_TIMEOUT_INVALID ),
date_as_string(false),
buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ),
param_ind_ptrs( 10 ), // initially hold 10 elements, which should cover 90% of the cases and only take < 100 byte
send_streams_at_exec( true ),
Expand Down Expand Up @@ -1403,6 +1404,15 @@ void stmt_option_buffered_query_limit:: operator()( _Inout_ sqlsrv_stmt* stmt, s
core_sqlsrv_set_buffered_query_limit( stmt, value_z TSRMLS_CC );
}

void stmt_option_date_as_string:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
{
if (zend_is_true(value_z)) {
stmt->date_as_string = true;
}
else {
stmt->date_as_string = false;
}
}

// internal function to release the active stream. Called by each main API function
// that will alter the statement and cancel any retrieval of data from a stream.
Expand Down
120 changes: 120 additions & 0 deletions test/functional/sqlsrv/sqlsrv_statement_datetimes_as_nulls.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
--TEST--
Test retrieving null datetime values with statement option ReturnDatesAsStrings as true
--DESCRIPTION--
Test retrieving null datetime values with statement option ReturnDatesAsStrings as true,
which is false by default. Whether retrieved as strings or date time objects should return
NULLs.
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
require_once('MsCommon.inc');

function testFetch($conn, $query, $columns, $withBuffer = false)
{
// The statement option ReturnDatesAsStrings set to true
// Test different fetching
if ($withBuffer){
$options = array('Scrollable' => 'buffered', 'ReturnDatesAsStrings' => true);
} else {
$options = array('ReturnDatesAsStrings' => true);
}

$size = count($columns);
$stmt = sqlsrv_prepare($conn, $query, array(), $options);
// Fetch by getting one field at a time
sqlsrv_execute($stmt);
if( sqlsrv_fetch( $stmt ) === false) {
fatalError("Failed in retrieving data\n");
}
for ($i = 0; $i < $size; $i++) {
$field = sqlsrv_get_field($stmt, $i); // expect string
if (!is_null($field)) {
echo "Expected null for column $columns[$i] but got: ";
var_dump($field);
}
}

// Fetch row as an object
sqlsrv_execute($stmt);
$object = sqlsrv_fetch_object($stmt);

$objArray = (array)$object; // turn the object into an associated array
for ($i = 0; $i < $size; $i++) {
$col = $columns[$i];
$val = $objArray[$col];

if (!is_null($val)) {
echo "Expected null for column $columns[$i] but got: ";
var_dump($val);
}
}
}

function createTestTable($conn, $tableName, $columns)
{
// Create the test table of date and time columns
$dataTypes = array('date', 'smalldatetime', 'datetime', 'datetime2', 'datetimeoffset', 'time');

$colMeta = array(new AE\ColumnMeta($dataTypes[0], $columns[0]),
new AE\ColumnMeta($dataTypes[1], $columns[1]),
new AE\ColumnMeta($dataTypes[2], $columns[2]),
new AE\ColumnMeta($dataTypes[3], $columns[3]),
new AE\ColumnMeta($dataTypes[4], $columns[4]),
new AE\ColumnMeta($dataTypes[5], $columns[5]));
AE\createTable($conn, $tableName, $colMeta);

// Insert null values
$inputData = array($colMeta[0]->colName => null,
$colMeta[1]->colName => null,
$colMeta[2]->colName => null,
$colMeta[3]->colName => null,
$colMeta[4]->colName => null,
$colMeta[5]->colName => null);
$stmt = AE\insertRow($conn, $tableName, $inputData);
if (!$stmt) {
fatalError("Failed to insert data.\n");
}
sqlsrv_free_stmt($stmt);
}

function runTest($tableName, $columns, $dateAsString)
{
// Connect
$conn = connect(array('ReturnDatesAsStrings' => $dateAsString));
if (!$conn) {
fatalError("Could not connect.\n");
}

$query = "SELECT * FROM $tableName";
testFetch($conn, $query, $columns);
testFetch($conn, $query, $columns, true);

sqlsrv_close($conn);
}

set_time_limit(0);
sqlsrv_configure('WarningsReturnAsErrors', 1);

$tableName = "TestNullDateTime";
$columns = array('c1', 'c2', 'c3', 'c4', 'c5', 'c6');

// Connect
$conn = connect();
if (!$conn) {
fatalError("Could not connect.\n");
}

createTestTable($conn, $tableName, $columns);

runTest($tableName, $columns, true);
runTest($tableName, $columns, false);

dropTable($conn, $tableName);

sqlsrv_close($conn);

echo "Done\n";
?>
--EXPECT--
Done
189 changes: 189 additions & 0 deletions test/functional/sqlsrv/sqlsrv_statement_datetimes_as_strings.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
--TEST--
Test retrieving datetime values with statement option ReturnDatesAsStrings set to true
--DESCRIPTION--
Test retrieving datetime values with statement option ReturnDatesAsStrings set to true,
which is false by default. The statement option should override the corresponding
connection option ReturnDatesAsStrings.
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
require_once('MsCommon.inc');

function compareDateTime($expectedStr, $actualObj)
{
$dtime = date_create($expectedStr);
$dtExpected = $dtime->format('Y-m-d H:i:s.u');

// actual datetime value from date time object to string
$dtActual = date_format($actualObj, 'Y-m-d H:i:s.u');

return ($dtActual === $dtExpected);
}

function testNoOption($conn, $tableName, $inputs, $exec)
{
// Without the statement option, should return datetime values as strings
// because the connection option ReturnDatesAsStrings is set to true
$query = "SELECT * FROM $tableName";
if ($exec) {
$stmt = sqlsrv_query($conn, $query);
} else {
$stmt = sqlsrv_prepare($conn, $query);
sqlsrv_execute($stmt);
}

$results = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_NUMERIC);

// Compare values only
$diffs = array_diff($inputs, $results);
if (!empty($diffs)) {
echo 'The results are different from the input values: ';
print_r($diffs);
}
}

function testStmtOption($conn, $tableName, $inputs, $stmtDateAsStr)
{
// The statement option should always override the connection option
$query = "SELECT * FROM $tableName";
$options = array('ReturnDatesAsStrings' => $stmtDateAsStr);
$stmt = sqlsrv_query($conn, $query, array(), $options);

if ($stmtDateAsStr) {
$results = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);

// Compare values only
$diffs = array_diff($inputs, $results);
if (!empty($diffs)) {
echo 'The results are different from the input values: ';
print_r($diffs);
}
} else {
$results = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_NUMERIC);

// Expect DateTime Objects in $results
for ($i = 0; $i < count($inputs); $i++) {
if (is_object($results[$i])) {
$matched = compareDateTime($inputs[$i], $results[$i]);
if (!$matched) {
echo "Expected a DateTime object of $inputs[$i] but got: \n";
var_dump($results[$i]);
}
} else {
echo "Expect a DateTime object but got $results[$i]\n";
}
}
}
}

function testFetching($conn, $tableName, $inputs, $columns, $withBuffer)
{
// The statement option ReturnDatesAsStrings set to true
// Test different fetching
$query = "SELECT * FROM $tableName";
if ($withBuffer){
$options = array('Scrollable' => 'buffered', 'ReturnDatesAsStrings' => true);
} else {
$options = array('ReturnDatesAsStrings' => true);
}

$size = count($inputs);
$stmt = sqlsrv_prepare($conn, $query, array(), $options);

// Fetch by getting one field at a time
sqlsrv_execute($stmt);

if( sqlsrv_fetch( $stmt ) === false) {
fatalError("Failed in retrieving data\n");
}
for ($i = 0; $i < $size; $i++) {
$field = sqlsrv_get_field($stmt, $i); // expect string
if ($field != $inputs[$i]) {
echo "Expected $inputs[$i] for column $columns[$i] but got: ";
var_dump($field);
}
}

// Fetch row as an object
sqlsrv_execute($stmt);
$object = sqlsrv_fetch_object($stmt);

$objArray = (array)$object; // turn the object into an associated array
for ($i = 0; $i < $size; $i++) {
$col = $columns[$i];
$val = $objArray[$col];

if ($val != $inputs[$i]) {
echo "Expected $inputs[$i] for column $columns[$i] but got: ";
var_dump($val);
}
}
}

set_time_limit(0);
sqlsrv_configure('WarningsReturnAsErrors', 1);
date_default_timezone_set('America/Los_Angeles');

// Connect with ReturnDatesAsStrings option set to true
$conn = connect(array('ReturnDatesAsStrings' => true));
if (!$conn) {
fatalError("Could not connect.\n");
}

// Generate input values for the test table
$query = 'SELECT CONVERT(date, SYSDATETIME()), CONVERT(smalldatetime, SYSDATETIME()), CONVERT(datetime, SYSDATETIME()), SYSDATETIME()';
$stmt = sqlsrv_query($conn, $query);
$values = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_NUMERIC);

// Create the test table of date and time columns
$tableName = 'StmtDateAsString';
$columns = array('c1', 'c2', 'c3', 'c4');
$dataTypes = array('date', 'smalldatetime', 'datetime', 'datetime2');

$colMeta = array(new AE\ColumnMeta($dataTypes[0], $columns[0]),
new AE\ColumnMeta($dataTypes[1], $columns[1]),
new AE\ColumnMeta($dataTypes[2], $columns[2]),
new AE\ColumnMeta($dataTypes[3], $columns[3]));
AE\createTable($conn, $tableName, $colMeta);

// Insert data values
$inputData = array($colMeta[0]->colName => $values[0],
$colMeta[1]->colName => $values[1],
$colMeta[2]->colName => $values[2],
$colMeta[3]->colName => $values[3]);
$stmt = AE\insertRow($conn, $tableName, $inputData);
if (!$stmt) {
fatalError("Failed to insert data.\n");
}
sqlsrv_free_stmt($stmt);

// Do not set ReturnDatesAsStrings at statement level
testNoOption($conn, $tableName, $values, true);
testNoOption($conn, $tableName, $values, false);

// Set ReturnDatesAsStrings to false at statement level
testStmtOption($conn, $tableName, $values, false);

sqlsrv_close($conn);

// Now connect but with ReturnDatesAsStrings option set to false
$conn = connect(array('ReturnDatesAsStrings' => false));
if (!$conn) {
fatalError("Could not connect.\n");
}

// Set ReturnDatesAsStrings to true at statement level
testStmtOption($conn, $tableName, $values, true);

// Test fetching by setting ReturnDatesAsStrings to true at statement level
testFetching($conn, $tableName, $values, $columns, true);
testFetching($conn, $tableName, $values, $columns, false);

dropTable($conn, $tableName);
sqlsrv_close($conn);

echo "Done\n";
?>
--EXPECT--
Done
Loading

0 comments on commit b149fdc

Please sign in to comment.