зеркало из https://github.com/microsoft/msphpsql.git
Feature request - add ReturnDatesAsStrings option to statement level for sqlsrv (#844)
* Added ReturnDatesAsStrings option to the statement level * Added new tests for ReturnDatesAsStrings at statement level * Added more datetime types as per review
This commit is contained in:
Родитель
7521f095ee
Коммит
902a03263e
|
@ -1098,6 +1098,7 @@ enum SQLSRV_STMT_OPTIONS {
|
|||
SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC,
|
||||
SQLSRV_STMT_OPTION_SCROLLABLE,
|
||||
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE,
|
||||
SQLSRV_STMT_OPTION_DATE_AS_STRING,
|
||||
|
||||
// Driver specific connection options
|
||||
SQLSRV_STMT_OPTION_DRIVER_SPECIFIC = 1000,
|
||||
|
@ -1282,6 +1283,11 @@ struct stmt_option_buffered_query_limit : public stmt_option_functor {
|
|||
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
|
||||
};
|
||||
|
||||
struct stmt_option_date_as_string : public stmt_option_functor {
|
||||
|
||||
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
|
||||
};
|
||||
|
||||
// used to hold the table for statment options
|
||||
struct stmt_option {
|
||||
|
||||
|
@ -1393,6 +1399,7 @@ struct sqlsrv_stmt : public sqlsrv_context {
|
|||
// last results
|
||||
unsigned long query_timeout; // maximum allowed statement execution time
|
||||
zend_long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
|
||||
bool date_as_string; // false by default but the user can set this to true to retrieve datetime values as strings
|
||||
|
||||
// holds output pointers for SQLBindParameter
|
||||
// We use a deque because it 1) provides the at/[] access in constant time, and 2) grows dynamically without moving
|
||||
|
|
|
@ -141,6 +141,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 ),
|
||||
|
@ -1404,6 +1405,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.
|
||||
|
|
|
@ -173,6 +173,7 @@ namespace SSStmtOptionNames {
|
|||
const char SEND_STREAMS_AT_EXEC[] = "SendStreamParamsAtExec";
|
||||
const char SCROLLABLE[] = "Scrollable";
|
||||
const char CLIENT_BUFFER_MAX_SIZE[] = INI_BUFFERED_QUERY_LIMIT;
|
||||
const char DATE_AS_STRING[] = "ReturnDatesAsStrings";
|
||||
}
|
||||
|
||||
namespace SSConnOptionNames {
|
||||
|
@ -243,6 +244,12 @@ const stmt_option SS_STMT_OPTS[] = {
|
|||
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE,
|
||||
std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit )
|
||||
},
|
||||
{
|
||||
SSStmtOptionNames::DATE_AS_STRING,
|
||||
sizeof( SSStmtOptionNames::DATE_AS_STRING ),
|
||||
SQLSRV_STMT_OPTION_DATE_AS_STRING,
|
||||
std::unique_ptr<stmt_option_date_as_string>( new stmt_option_date_as_string )
|
||||
},
|
||||
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
|
||||
};
|
||||
|
||||
|
@ -988,7 +995,7 @@ PHP_FUNCTION( sqlsrv_prepare )
|
|||
|
||||
// Initialize the options array to be passed to the core layer
|
||||
ALLOC_HASHTABLE( ss_stmt_options_ht );
|
||||
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */,
|
||||
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */,
|
||||
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
|
||||
|
||||
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
|
||||
|
@ -1111,7 +1118,7 @@ PHP_FUNCTION( sqlsrv_query )
|
|||
|
||||
// Initialize the options array to be passed to the core layer
|
||||
ALLOC_HASHTABLE( ss_stmt_options_ht );
|
||||
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */, ZVAL_PTR_DTOR,
|
||||
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */, ZVAL_PTR_DTOR,
|
||||
0 /*persistent*/ TSRMLS_CC );
|
||||
|
||||
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
|
||||
|
|
|
@ -129,6 +129,10 @@ ss_sqlsrv_stmt::ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_
|
|||
fetch_fields_count ( 0 )
|
||||
{
|
||||
core_sqlsrv_set_buffered_query_limit( this, SQLSRV_G( buffered_query_limit ) TSRMLS_CC );
|
||||
|
||||
// initialize date_as_string based on the corresponding connection option
|
||||
ss_sqlsrv_conn* ss_conn = static_cast<ss_sqlsrv_conn*>(conn);
|
||||
date_as_string = ss_conn->date_as_string;
|
||||
}
|
||||
|
||||
ss_sqlsrv_stmt::~ss_sqlsrv_stmt( void )
|
||||
|
@ -230,7 +234,7 @@ sqlsrv_phptype ss_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type, _
|
|||
case SQL_SS_TIMESTAMPOFFSET:
|
||||
case SQL_SS_TIME2:
|
||||
case SQL_TYPE_TIMESTAMP:
|
||||
if( reinterpret_cast<ss_sqlsrv_conn*>( this->conn )->date_as_string ) {
|
||||
if (this->date_as_string) {
|
||||
ss_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
||||
ss_phptype.typeinfo.encoding = this->conn->encoding();
|
||||
}
|
||||
|
@ -1678,8 +1682,7 @@ sqlsrv_phptype determine_sqlsrv_php_type( _In_ ss_sqlsrv_stmt const* stmt, _In_
|
|||
case SQL_SS_TIME2:
|
||||
case SQL_TYPE_TIMESTAMP:
|
||||
{
|
||||
ss_sqlsrv_conn* c = static_cast<ss_sqlsrv_conn*>( stmt->conn );
|
||||
if( c->date_as_string ) {
|
||||
if (stmt->date_as_string) {
|
||||
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
||||
sqlsrv_phptype.typeinfo.encoding = stmt->encoding();
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,197 @@
|
|||
--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()), SYSDATETIME(),
|
||||
CONVERT(smalldatetime, SYSDATETIME()),
|
||||
CONVERT(datetime, SYSDATETIME()),
|
||||
SYSDATETIMEOFFSET(),
|
||||
CONVERT(time, 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', 'c5', 'c6');
|
||||
$dataTypes = array('date', 'datetime2', 'smalldatetime', 'datetime', '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 data values
|
||||
$inputData = array($colMeta[0]->colName => $values[0],
|
||||
$colMeta[1]->colName => $values[1],
|
||||
$colMeta[2]->colName => $values[2],
|
||||
$colMeta[3]->colName => $values[3],
|
||||
$colMeta[4]->colName => $values[4],
|
||||
$colMeta[5]->colName => $values[5]);
|
||||
$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
|
|
@ -0,0 +1,104 @@
|
|||
--TEST--
|
||||
Test retrieving datetime values as output params with statement option ReturnDatesAsStrings
|
||||
--DESCRIPTION--
|
||||
Test retrieving datetime values as output params with statement option ReturnDatesAsStrings
|
||||
with sqlsrv_prepare. When ReturnDatesAsStrings option is false, expect an error to return.
|
||||
--SKIPIF--
|
||||
<?php require('skipif_versions_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
|
||||
function runTest($conn, $storedProcName, $inputValue, $sqlType, $dateAsString)
|
||||
{
|
||||
$outDateStr = '';
|
||||
$outSql = AE\getCallProcSqlPlaceholders($storedProcName, 1);
|
||||
$stmt = sqlsrv_prepare($conn, $outSql,
|
||||
array(array(&$outDateStr, SQLSRV_PARAM_OUT, null, $sqlType)), array('ReturnDatesAsStrings' => $dateAsString));
|
||||
if (!$stmt) {
|
||||
fatalError("Failed when preparing to call $storedProcName");
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if ($dateAsString) {
|
||||
// Expect to succeed when returning a DateTime value as a string
|
||||
// The output param value should be the same as the input value
|
||||
if (!$result) {
|
||||
fatalError("Failed when invoking $storedProcName");
|
||||
}
|
||||
if ($outDateStr != $inputValue) {
|
||||
echo "Expected $inputValue but got $outDateStr\n";
|
||||
}
|
||||
} else {
|
||||
// Expect to fail with an error message because setting a DateTime object as the
|
||||
// output parameter is not allowed
|
||||
if ($result) {
|
||||
fatalError("Returning DateTime as output param is expected to fail!");
|
||||
}
|
||||
// Check if the error message is the expected one
|
||||
$error = sqlsrv_errors()[0]['message'];
|
||||
$message = 'An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters';
|
||||
if (strpos($error, $message) === false) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()), SYSDATETIME(), SYSDATETIMEOFFSET(), CONVERT(time, CURRENT_TIMESTAMP)';
|
||||
$stmt = sqlsrv_query($conn, $query);
|
||||
$values = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_NUMERIC);
|
||||
|
||||
// Create the test table of date and time columns
|
||||
$tableName = 'OuputParamDateAsString';
|
||||
$columns = array('c1', 'c2', 'c3', 'c4');
|
||||
$dataTypes = array('date', 'datetime2', 'datetimeoffset', 'time');
|
||||
$sqlTypes = array(SQLSRV_SQLTYPE_DATE,
|
||||
SQLSRV_SQLTYPE_DATETIME2,
|
||||
SQLSRV_SQLTYPE_DATETIMEOFFSET,
|
||||
SQLSRV_SQLTYPE_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]));
|
||||
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);
|
||||
|
||||
for ($i = 0; $i < count($columns); $i++) {
|
||||
// create the stored procedure first
|
||||
$storedProcName = "spDateTimeOutParam" . $i;
|
||||
$procArgs = "@col $dataTypes[$i] OUTPUT";
|
||||
$procCode = "SELECT @col = $columns[$i] FROM $tableName";
|
||||
createProc($conn, $storedProcName, $procArgs, $procCode);
|
||||
|
||||
// call stored procedure to retrieve output param
|
||||
runTest($conn, $storedProcName, $values[$i], $sqlTypes[$i], true);
|
||||
runTest($conn, $storedProcName, $values[$i], $sqlTypes[$i], false);
|
||||
}
|
||||
|
||||
dropTable($conn, $tableName);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
Загрузка…
Ссылка в новой задаче