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

Decimal places for money types only #886

Merged
merged 22 commits into from
Nov 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8f9ecde
Merge pull request #787 from Microsoft/dev
yitam May 30, 2018
9654020
Dev (#820)
yitam Jul 21, 2018
ff3c6a4
Merge branch 'master' into dev
yitam Sep 24, 2018
b47cec2
Dev to Master - 5.4.0-preview (#853)
yitam Sep 24, 2018
0fa3a43
Merge branch 'master' of https://github.com/Microsoft/msphpsql
yitam Sep 24, 2018
80442d4
Change readme links to https
BackEndTea Oct 1, 2018
14acf00
Merge pull request #857 from BackEndTea/patch-1
david-puglielli Oct 1, 2018
097c06f
Merge branch 'master' of https://github.com/Microsoft/msphpsql
yitam Oct 3, 2018
eb679ea
Update survey image link
Nov 14, 2018
2e13851
Merge branch 'master' of https://github.com/Microsoft/msphpsql
yitam Nov 14, 2018
f831c9e
Merge branch 'master' into dev
yitam Nov 14, 2018
b69c824
Merge branch 'dev' of https://github.com/Microsoft/msphpsql into dev
yitam Nov 16, 2018
3919628
Merge branch 'dev' of https://github.com/Microsoft/msphpsql into dev
yitam Nov 22, 2018
4637279
Decimal places for money types only
yitam Nov 23, 2018
1bdbf86
Merge branch 'dev' of https://github.com/Microsoft/msphpsql into dev
yitam Nov 23, 2018
f6a7c09
Merge branch 'dev' into decimalPlaces
yitam Nov 23, 2018
fc5d86d
Adding new tests for various decimal places
yitam Nov 23, 2018
1378d4b
Decimal places for money types only
yitam Nov 23, 2018
8779520
Adding new tests for various decimal places
yitam Nov 23, 2018
f790469
Minor fixes to the tests
yitam Nov 23, 2018
e7a11cb
Minor fixes to the tests
yitam Nov 23, 2018
7b8a309
Applied review comments
yitam Nov 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions source/pdo_sqlsrv/pdo_dbh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_EMULATE_PREPARES,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE,
PDO_STMT_OPTION_FETCHES_DATETIME_TYPE,
PDO_STMT_OPTION_FORMAT_DECIMALS
PDO_STMT_OPTION_FORMAT_DECIMALS,
PDO_STMT_OPTION_DECIMAL_PLACES
};

// List of all the statement options supported by this driver.
Expand All @@ -97,6 +98,7 @@ const stmt_option PDO_STMT_OPTS[] = {
{ 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_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 ) },

{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
};
Expand Down Expand Up @@ -500,7 +502,9 @@ 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_datetime( false )
fetch_datetime( false ),
format_decimals( false ),
decimal_places( NO_CHANGE_DECIMAL_PLACES )
{
if( client_buffer_max_size < 0 ) {
client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT;
Expand Down Expand Up @@ -1069,7 +1073,28 @@ int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
driver_dbh->fetch_datetime = (zend_is_true(val)) ? true : false;
break;


case SQLSRV_ATTR_FORMAT_DECIMALS:
driver_dbh->format_decimals = (zend_is_true(val)) ? true : false;
break;

case SQLSRV_ATTR_DECIMAL_PLACES:
{
// first check if the input is an integer
if (Z_TYPE_P(val) != IS_LONG) {
THROW_PDO_ERROR(driver_dbh, SQLSRV_ERROR_INVALID_DECIMAL_PLACES);
}

zend_long decimal_places = Z_LVAL_P(val);
if (decimal_places < 0 || decimal_places > SQL_SERVER_MAX_MONEY_SCALE) {
// ignore decimal_places as this is out of range
decimal_places = NO_CHANGE_DECIMAL_PLACES;
}

driver_dbh->decimal_places = static_cast<short>(decimal_places);
}
break;

// Not supported
case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES:
Expand Down Expand Up @@ -1097,7 +1122,6 @@ int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case PDO_ATTR_EMULATE_PREPARES:
case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
case SQLSRV_ATTR_FORMAT_DECIMALS:
{
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
}
Expand Down Expand Up @@ -1156,7 +1180,6 @@ int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case PDO_ATTR_EMULATE_PREPARES:
case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
case SQLSRV_ATTR_FORMAT_DECIMALS:
{
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
}
Expand Down Expand Up @@ -1229,6 +1252,18 @@ int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
break;
}

case SQLSRV_ATTR_FORMAT_DECIMALS:
{
ZVAL_BOOL( return_value, driver_dbh->format_decimals );
break;
}

case SQLSRV_ATTR_DECIMAL_PLACES:
{
ZVAL_LONG( return_value, driver_dbh->decimal_places );
break;
}

default:
{
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR );
Expand Down Expand Up @@ -1594,6 +1629,10 @@ void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_
option_key = PDO_STMT_OPTION_FORMAT_DECIMALS;
break;

case SQLSRV_ATTR_DECIMAL_PLACES:
option_key = PDO_STMT_OPTION_DECIMAL_PLACES;
break;

default:
CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core::CoreException();
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 @@ -287,6 +287,7 @@ namespace {
{ "SQLSRV_ATTR_FETCHES_NUMERIC_TYPE", SQLSRV_ATTR_FETCHES_NUMERIC_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 },

// used for the size for output parameters: PDO::PARAM_INT and PDO::PARAM_BOOL use the default size of int,
// PDO::PARAM_STR uses the size of the string in the variable
Expand Down
18 changes: 17 additions & 1 deletion source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,11 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;

case SQLSRV_ATTR_FORMAT_DECIMALS:
core_sqlsrv_set_format_decimals(driver_stmt, val TSRMLS_CC);
driver_stmt->format_decimals = ( zend_is_true( val )) ? true : false;
break;

case SQLSRV_ATTR_DECIMAL_PLACES:
core_sqlsrv_set_decimal_places(driver_stmt, val TSRMLS_CC);
break;

default:
Expand Down Expand Up @@ -973,6 +977,18 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
}

case SQLSRV_ATTR_FORMAT_DECIMALS:
{
ZVAL_BOOL( return_value, driver_stmt->format_decimals );
break;
}

case SQLSRV_ATTR_DECIMAL_PLACES:
{
ZVAL_LONG( return_value, driver_stmt->decimal_places );
break;
}

default:
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_STMT_ATTR );
break;
Expand Down
6 changes: 1 addition & 5 deletions source/pdo_sqlsrv/pdo_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,9 @@ pdo_error PDO_ERRORS[] = {
{ IMSSP, (SQLCHAR*) "The Azure AD Access Token is empty. Expected a byte string.", -91, false}
},
{
SQLSRV_ERROR_INVALID_FORMAT_DECIMALS,
SQLSRV_ERROR_INVALID_DECIMAL_PLACES,
{ IMSSP, (SQLCHAR*) "Expected an integer to specify number of decimals to format the output values of decimal data types.", -92, false}
},
{
SQLSRV_ERROR_FORMAT_DECIMALS_OUT_OF_RANGE,
{ IMSSP, (SQLCHAR*) "For formatting decimal data values, %1!d! is out of range. Expected an integer from 0 to 38, inclusive.", -93, true}
},

{ UINT_MAX, {} }
};
Expand Down
7 changes: 6 additions & 1 deletion source/pdo_sqlsrv/php_pdo_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ enum PDO_SQLSRV_ATTR {
SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE,
SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_DATETIME_TYPE,
SQLSRV_ATTR_FORMAT_DECIMALS
SQLSRV_ATTR_FORMAT_DECIMALS,
SQLSRV_ATTR_DECIMAL_PLACES
};

// valid set of values for TransactionIsolation connection option
Expand Down Expand Up @@ -206,6 +207,8 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
zend_long client_buffer_max_size;
bool fetch_numeric;
bool fetch_datetime;
bool format_decimals;
short decimal_places;

pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC );
};
Expand Down Expand Up @@ -267,6 +270,8 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
direct_query = db->direct_query;
fetch_numeric = db->fetch_numeric;
fetch_datetime = db->fetch_datetime;
format_decimals = db->format_decimals;
decimal_places = db->decimal_places;
}

virtual ~pdo_sqlsrv_stmt( void );
Expand Down
34 changes: 20 additions & 14 deletions source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ const int SQL_SERVER_MAX_FIELD_SIZE = 8000;
const int SQL_SERVER_MAX_PRECISION = 38;
const int SQL_SERVER_MAX_TYPE_SIZE = 0;
const int SQL_SERVER_MAX_PARAMS = 2100;
const int SQL_SERVER_MAX_MONEY_SCALE = 4;

// increase the maximum message length to accommodate for the long error returned for operand type clash
// or for conversion of a long string
const int SQL_MAX_ERROR_MESSAGE_LENGTH = SQL_MAX_MESSAGE_LENGTH * 2;
Expand Down Expand Up @@ -230,6 +232,9 @@ enum SQLSRV_FETCH_TYPE {
// buffer size of a sql state (including the null character)
const int SQL_SQLSTATE_BUFSIZE = SQL_SQLSTATE_SIZE + 1;

// default value of decimal places (no formatting required)
const short NO_CHANGE_DECIMAL_PLACES = -1;

// buffer size allocated to retrieve data from a PHP stream. This number
// was chosen since PHP doesn't return more than 8k at a time even if
// the amount requested was more.
Expand Down Expand Up @@ -1108,6 +1113,7 @@ enum SQLSRV_STMT_OPTIONS {
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE,
SQLSRV_STMT_OPTION_DATE_AS_STRING,
SQLSRV_STMT_OPTION_FORMAT_DECIMALS,
SQLSRV_STMT_OPTION_DECIMAL_PLACES,

// Driver specific connection options
SQLSRV_STMT_OPTION_DRIVER_SPECIFIC = 1000,
Expand Down Expand Up @@ -1302,6 +1308,11 @@ struct stmt_option_format_decimals : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
};

struct stmt_option_decimal_places : 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 {

Expand Down Expand Up @@ -1372,7 +1383,7 @@ struct sqlsrv_output_param {
SQLLEN original_buffer_len; // used to make sure the returned length didn't overflow the buffer
SQLSRV_PHPTYPE php_out_type; // used to convert output param if necessary
bool is_bool;
param_meta_data meta_data; // parameter meta data
param_meta_data meta_data; // parameter meta data

// string output param constructor
sqlsrv_output_param( _In_ zval* p_z, _In_ SQLSRV_ENCODING enc, _In_ int num, _In_ SQLUINTEGER buffer_len ) :
Expand All @@ -1399,15 +1410,9 @@ struct sqlsrv_output_param {
meta_data.nullable = nullable;
}

SQLSMALLINT getDecimalDigits()
param_meta_data& getMetaData()
{
// Return decimal_digits only for decimal / numeric types. Otherwise, return -1
if (meta_data.sql_type == SQL_DECIMAL || meta_data.sql_type == SQL_NUMERIC) {
return meta_data.decimal_digits;
}
else {
return -1;
}
return meta_data;
}
};

Expand Down Expand Up @@ -1435,7 +1440,8 @@ struct sqlsrv_stmt : public sqlsrv_context {
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
short num_decimals; // indicates number of decimals shown in fetched results (-1 by default, which means no formatting required)
bool format_decimals; // false by default but the user can set this to true to add the missing leading zeroes and/or control number of decimal digits to show
short decimal_places; // indicates number of decimals shown in fetched results (-1 by default, which means no change to number of decimal digits)

// 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
Expand Down Expand Up @@ -1476,9 +1482,10 @@ struct field_meta_data {
SQLULEN field_precision;
SQLSMALLINT field_scale;
SQLSMALLINT field_is_nullable;
bool field_is_money_type;

field_meta_data() : field_name_len(0), field_type(0), field_size(0), field_precision(0),
field_scale (0), field_is_nullable(0)
field_scale (0), field_is_nullable(0), field_is_money_type(false)
{
}

Expand Down Expand Up @@ -1527,7 +1534,7 @@ void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z
bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLEN limit TSRMLS_DC );
void core_sqlsrv_set_format_decimals(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC);
void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC);

//*********************************************************************************************************************************
// Result Set
Expand Down Expand Up @@ -1769,8 +1776,7 @@ enum SQLSRV_ERROR_CODES {
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
SQLSRV_ERROR_INVALID_OPTION_WITH_ACCESS_TOKEN,
SQLSRV_ERROR_EMPTY_ACCESS_TOKEN,
SQLSRV_ERROR_INVALID_FORMAT_DECIMALS,
SQLSRV_ERROR_FORMAT_DECIMALS_OUT_OF_RANGE,
SQLSRV_ERROR_INVALID_DECIMAL_PLACES,

// Driver specific error codes starts from here.
SQLSRV_ERROR_DRIVER_SPECIFIC = 1000,
Expand Down
Loading