Skip to content
17 changes: 17 additions & 0 deletions source/pdo_sqlsrv/pdo_dbh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE,
PDO_STMT_OPTION_EMULATE_PREPARES,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE,
PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE,
PDO_STMT_OPTION_FETCHES_DATETIME_TYPE,
PDO_STMT_OPTION_FORMAT_DECIMALS,
PDO_STMT_OPTION_DECIMAL_PLACES,
Expand All @@ -104,6 +105,7 @@ const stmt_option PDO_STMT_OPTS[] = {
{ NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit ) },
{ NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr<stmt_option_emulate_prepares>( new stmt_option_emulate_prepares ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_numeric>( new stmt_option_fetch_numeric ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_bignumeric>( new stmt_option_fetch_bignumeric ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_DATETIME_TYPE, std::unique_ptr<stmt_option_fetch_datetime>( new stmt_option_fetch_datetime ) },
{ NULL, 0, PDO_STMT_OPTION_FORMAT_DECIMALS, std::unique_ptr<stmt_option_format_decimals>( new stmt_option_format_decimals ) },
{ NULL, 0, PDO_STMT_OPTION_DECIMAL_PLACES, std::unique_ptr<stmt_option_decimal_places>( new stmt_option_decimal_places ) },
Expand Down Expand Up @@ -592,6 +594,7 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )),
fetch_numeric( false ),
fetch_bignumeric( false ),
fetch_datetime( false ),
format_decimals( false ),
decimal_places( NO_CHANGE_DECIMAL_PLACES ),
Expand Down Expand Up @@ -1273,6 +1276,10 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
driver_dbh->fetch_numeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
driver_dbh->fetch_bignumeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
driver_dbh->fetch_datetime = zend_is_true(val);
break;
Expand Down Expand Up @@ -1504,6 +1511,12 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
break;
}

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_dbh->fetch_bignumeric );
break;
}

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
{
ZVAL_BOOL( return_value, driver_dbh->fetch_datetime );
Expand Down Expand Up @@ -1959,6 +1972,10 @@ void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ H
option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE;
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE;
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_DATETIME_TYPE;
break;
Expand Down
1 change: 1 addition & 0 deletions source/pdo_sqlsrv/pdo_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ namespace {
{ "SQLSRV_ATTR_CURSOR_SCROLL_TYPE" , SQLSRV_ATTR_CURSOR_SCROLL_TYPE },
{ "SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE", SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE },
{ "SQLSRV_ATTR_FETCHES_NUMERIC_TYPE", SQLSRV_ATTR_FETCHES_NUMERIC_TYPE },
{ "SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE", SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE },
{ "SQLSRV_ATTR_FETCHES_DATETIME_TYPE", SQLSRV_ATTR_FETCHES_DATETIME_TYPE },
{ "SQLSRV_ATTR_FORMAT_DECIMALS" , SQLSRV_ATTR_FORMAT_DECIMALS },
{ "SQLSRV_ATTR_DECIMAL_PLACES" , SQLSRV_ATTR_DECIMAL_PLACES },
Expand Down
40 changes: 36 additions & 4 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,16 @@ zval convert_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_PHPTYPE sqlsrv_php_t

case SQLSRV_PHPTYPE_INT:
case SQLSRV_PHPTYPE_FLOAT:
case SQLSRV_PHPTYPE_LONG:
{
if (*in_val == NULL) {
ZVAL_NULL(&out_zval);
}
else {

if (sqlsrv_php_type == SQLSRV_PHPTYPE_INT) {
if (sqlsrv_php_type == SQLSRV_PHPTYPE_LONG) {
ZVAL_LONG(&out_zval, **(reinterpret_cast<long long**>(in_val)));
}
else if (sqlsrv_php_type == SQLSRV_PHPTYPE_INT) {
ZVAL_LONG(&out_zval, **(reinterpret_cast<int**>(in_val)));
}
else {
Expand Down Expand Up @@ -340,6 +343,12 @@ void stmt_option_fetch_numeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_opt
pdo_stmt->fetch_numeric = zend_is_true(value_z);
}

void stmt_option_fetch_bignumeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->fetch_bignumeric = zend_is_true(value_z);
}

void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
Expand Down Expand Up @@ -930,6 +939,10 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
driver_stmt->fetch_numeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
driver_stmt->fetch_bignumeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
driver_stmt->fetch_datetime = zend_is_true(val);
break;
Expand Down Expand Up @@ -1027,6 +1040,12 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
}

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_stmt->fetch_bignumeric );
break;
}

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
{
ZVAL_BOOL( return_value, driver_stmt->fetch_datetime );
Expand Down Expand Up @@ -1539,10 +1558,23 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
}
break;
case SQL_BIGINT:
if ( this->fetch_bignumeric ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_LONG;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_DECIMAL:
case SQL_NUMERIC:
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
if ( this->fetch_bignumeric ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_FLOAT;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_CHAR:
case SQL_GUID:
Expand Down
9 changes: 9 additions & 0 deletions source/pdo_sqlsrv/php_pdo_sqlsrv_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum PDO_SQLSRV_ATTR {
SQLSRV_ATTR_CURSOR_SCROLL_TYPE,
SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE,
SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_DATETIME_TYPE,
SQLSRV_ATTR_FORMAT_DECIMALS,
SQLSRV_ATTR_DECIMAL_PLACES,
Expand Down Expand Up @@ -187,6 +188,7 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
long query_timeout;
zend_long client_buffer_max_size;
bool fetch_numeric;
bool fetch_bignumeric;
bool fetch_datetime;
bool format_decimals;
short decimal_places;
Expand Down Expand Up @@ -225,6 +227,10 @@ struct stmt_option_fetch_numeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};

struct stmt_option_fetch_bignumeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};

struct stmt_option_fetch_datetime : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
Expand All @@ -241,11 +247,13 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
placeholders(NULL),
bound_column_param_types( NULL ),
fetch_numeric( false ),
fetch_bignumeric( false ),
fetch_datetime( false )
{
pdo_sqlsrv_dbh* db = static_cast<pdo_sqlsrv_dbh*>( c );
direct_query = db->direct_query;
fetch_numeric = db->fetch_numeric;
fetch_bignumeric = db->fetch_bignumeric;
fetch_datetime = db->fetch_datetime;
format_decimals = db->format_decimals;
decimal_places = db->decimal_places;
Expand All @@ -265,6 +273,7 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {

pdo_param_type* bound_column_param_types;
bool fetch_numeric;
bool fetch_bignumeric;
bool fetch_datetime;
};

Expand Down
22 changes: 22 additions & 0 deletions source/shared/core_results.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
case SQL_C_BINARY: return sqlsrv_buffered_result_set::to_binary_string(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_DOUBLE: return sqlsrv_buffered_result_set::string_to_double(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_LONG: return sqlsrv_buffered_result_set::string_to_long(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_SBIGINT: return sqlsrv_buffered_result_set::string_to_long_long(field_index, buffer, buffer_length, out_buffer_length);
default:
break;
}
Expand Down Expand Up @@ -1123,6 +1124,27 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_long( _In_ SQLSMALLINT field_ind
return SQL_SUCCESS;
}

SQLRETURN sqlsrv_buffered_result_set::string_to_long_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[field_index].c_type == SQL_C_CHAR, "Invalid conversion from string to long long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONGLONG ), "Buffer needs to be big enough to hold a long long" );

unsigned char* row = get_row();
char* string_data = reinterpret_cast<char*>( &row[meta[field_index].offset] ) + sizeof( SQLULEN );

LONGLONG* number_data = reinterpret_cast<LONGLONG*>(buffer);
try {
*number_data = std::stoll(std::string(string_data));
} catch (const std::logic_error& ) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}

*out_buffer_length = sizeof(LONGLONG);
return SQL_SUCCESS;
}

SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length )
{
Expand Down
3 changes: 3 additions & 0 deletions source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ enum SQLSRV_PHPTYPE {
MIN_SQLSRV_PHPTYPE = 1, // lowest value for a php type
SQLSRV_PHPTYPE_NULL = 1,
SQLSRV_PHPTYPE_INT,
SQLSRV_PHPTYPE_LONG,
SQLSRV_PHPTYPE_FLOAT,
SQLSRV_PHPTYPE_STRING,
SQLSRV_PHPTYPE_DATETIME,
Expand Down Expand Up @@ -1926,6 +1927,8 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
_Inout_ SQLLEN* out_buffer_length );
SQLRETURN string_to_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length );
SQLRETURN string_to_long_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length );
SQLRETURN wstring_to_double( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length );
SQLRETURN wstring_to_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
Expand Down
28 changes: 28 additions & 0 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,33 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
break;
}

case SQLSRV_PHPTYPE_LONG:
{
sqlsrv_malloc_auto_ptr<SQLLEN> field_value_temp;
field_value_temp = static_cast<SQLLEN*>( sqlsrv_malloc( sizeof( SQLLEN )));
*field_value_temp = 0;

SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_SBIGINT, field_value_temp, sizeof( SQLLEN ),
field_len, true /*handle_warning*/ );

CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw core::CoreException();
}

CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index, NULL) {
throw core::CoreException();
}

if( *field_len == SQL_NULL_DATA ) {
field_value = NULL;
break;
}

field_value = field_value_temp;
field_value_temp.transferred();
break;
}

case SQLSRV_PHPTYPE_FLOAT:
{
sqlsrv_malloc_auto_ptr<double> field_value_temp;
Expand Down Expand Up @@ -1875,6 +1902,7 @@ bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type )

case SQLSRV_PHPTYPE_NULL:
case SQLSRV_PHPTYPE_INT:
case SQLSRV_PHPTYPE_LONG:
case SQLSRV_PHPTYPE_FLOAT:
case SQLSRV_PHPTYPE_DATETIME:
case SQLSRV_PHPTYPE_TABLE:
Expand Down
72 changes: 72 additions & 0 deletions test/functional/pdo_sqlsrv/pdo_fetch_bignumeric_as_int.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
--TEST--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for bigint columns
--DESCRIPTION--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for bigint columns.
The input value is a bigint bigger than the max int value in mssql.
Note that the existing attribute ATTR_STRINGIFY_FETCHES should have no effect on data retrieval.
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");

try {
$tableName = "pdo_test_table";

// Connect
$conn = connect();

// Run test
Test();

// Set PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE = false (default)
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, FALSE);
Test();

// Set PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE = true
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, TRUE);
Test();

// Close connection
unset($stmt);
unset($conn);

print "Done";
} catch (PDOException $e) {
var_dump($e->errorInfo);
}

// Generic test starts here
function Test()
{
global $conn, $tableName;

// Drop table if exists
createTable($conn, $tableName, array("c1" => "bigint"));

// Insert data using bind values
$sql = "INSERT INTO $tableName VALUES (32147483647)";
$stmt = $conn->prepare($sql);
$stmt->execute();

// Get data
$sql = "SELECT * FROM $tableName";
$stmt = $conn->query($sql);
$row = $stmt->fetchAll(PDO::FETCH_NUM);

// Print out
for ($i=0; $i<$stmt->rowCount(); $i++) {
var_dump($row[$i][0]);
}

// clean up
dropTable( $conn, $tableName );
unset( $stmt );
}
?>

--EXPECT--
string(11) "32147483647"
string(11) "32147483647"
int(32147483647)
Done
Loading