Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bigint attr #1515

Closed
wants to merge 10 commits into from
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
Loading