From 9d6aba62ce2a53c2add3d14eefc9674aa85d58c9 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Wed, 15 Nov 2017 16:36:10 -0800 Subject: [PATCH 01/10] implement adjustInputPrecision --- source/shared/core_stmt.cpp | 159 +++++++-- .../pdo_sqlsrv/pdo_ae_insert_decimal.phpt | 308 ++++++++++++++++++ .../sqlsrv/sqlsrv_ae_insert_decimal.phpt | 307 +++++++++++++++++ 3 files changed, 744 insertions(+), 30 deletions(-) create mode 100644 test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt create mode 100644 test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index e47bca4d..7a59cdd0 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -19,6 +19,7 @@ #include "core_sqlsrv.h" +#include namespace { // certain drivers using this layer will call for repeated or out of order field retrievals. To allow this, we cache the @@ -115,6 +116,7 @@ bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type ); void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z, _In_ SQLULEN paramno, SQLSRV_ENCODING encoding, _In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _In_ SQLSMALLINT decimal_digits, _Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len TSRMLS_DC ); +void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits ); bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ); void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param TSRMLS_DC ); // send all the stream data @@ -526,39 +528,13 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ break; case IS_STRING: { + if( stmt->conn->ce_option.enabled && ( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC )){ + adjustInputPrecision( param_z, decimal_digits ); + } + buffer = Z_STRVAL_P( param_z ); buffer_len = Z_STRLEN_P( param_z ); - if( stmt->conn->ce_option.enabled && ( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC )){ - // get the double value - double dval = zend_strtod( ZSTR_VAL( Z_STR_P( param_z )), NULL ); - // find the precision: number of digits before decimal place + decimal_digits - size_t numDigitsBeforeDec = 0; - double temp = dval; - if( abs( dval ) < 1){ - numDigitsBeforeDec = 0; - } - else{ - while( abs( temp ) > 1 ){ - temp /= 10; - numDigitsBeforeDec++; - } - } - size_t precision = numDigitsBeforeDec + decimal_digits; - // when passing a precision 0 to strppintf, it still returns a string with precision of 1 - // work around it by manually rounding the zval - if( precision == 0 && abs( dval ) < 1 ){ - if( dval < 0.5 ) - dval = 0; - else - dval = 1; - } - // reformat it with the right number of decimal digits - zend_string *str = strpprintf( 0, "%.*G", ( int )precision, dval ); - zend_string_release( Z_STR_P( param_z )); - ZVAL_NEW_STR( param_z, str ); - } - // if the encoding is UTF-8, translate from UTF-8 to UTF-16 (the type variables should have already been adjusted) if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ){ @@ -2682,6 +2658,129 @@ bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ) { return false; } +void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits ) { + // 38 is the maximum precision supported for sql decimal types + // 6 is a composition of: 1 for '.'; 1 for sign of the number; + // 1 for 'e' or 'E' (scientific notation); 1 for sign of scientific exponent; 2 for length of scientific exponent + size_t maxDecimalLen = 38 + 6; + SQLSRV_ASSERT( Z_STRLEN_P( param_z ) < maxDecimalLen, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); + std::vector digits; + char* ptr = ZSTR_VAL( Z_STR_P( param_z )); + bool isNeg = false; + char scientificChar = ' '; + int scientificExp = 0; + // parse digits in param_z into the vector digits + if( *ptr == '+' || *ptr == '-' ){ + if( *ptr = '-' ){ + isNeg = true; + } + ptr++; + } + size_t numInt = 0; + size_t numDec = 0; + while( isdigit( *ptr )){ + digits.push_back( *ptr - '0' ); + ptr++; + numInt++; + } + if( *ptr == '.' ){ + ptr++; + while( isdigit(*ptr) ){ + digits.push_back( *ptr - '0' ); + ptr++; + numDec++; + } + } + if( *ptr == 'e' || *ptr == 'E' ){ + scientificChar = *ptr; + } + if( scientificChar != ' ' ){ + ptr++; + bool isNegExp = false; + if( *ptr == '+' || *ptr == '-' ){ + if( *ptr == '-' ){ + isNegExp = true; + } + ptr++; + } + while( isdigit( *ptr )){ + scientificExp = scientificExp * 10 + *ptr - '0'; + ptr++; + } + SQLSRV_ASSERT( scientificExp <=38, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); + if( isNegExp ){ + scientificExp = scientificExp * -1; + } + } + SQLSRV_ASSERT( *ptr == '\0', "Invalid input decimal: Invalid character found in the decimal string." ); + // if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision + if(( int )numDec > scientificExp ){ + int decToRemove = numDec - scientificExp - decimal_digits; + if( decToRemove > 0 ){ + bool carryOver = false; + int backInd = 0; + // pop digits from the vector until there is only 1 more decimal place than required decimal_digits + while( decToRemove != 1 && !digits.empty() ){ + digits.pop_back(); + decToRemove--; + } + if( !digits.empty() ){ + // check if the last digit to be popped is greater than 5, if so, the digit before is needs to round up + carryOver = digits.back() >= 5; + digits.pop_back(); + backInd = digits.size() - 1; + // round up from the end until no more carry over + while( carryOver && backInd >= 0 ){ + if( digits.at( backInd ) != 9 ){ + digits.at( backInd )++; + carryOver = false; + } + else{ + digits.at( backInd ) = 0; + } + backInd--; + } + } + std::ostringstream oss; + if( isNeg ){ + oss << '-'; + } + // insert 1 if carry over persist all the way to the beginning of the number + if( carryOver && backInd == -1 ){ + oss << 1; + } + if( digits.empty() && !carryOver ){ + oss << 0; + } + else{ + int i = 0; + for( i; i < numInt && i < digits.size(); i++ ){ + oss << digits[i]; + } + // fill string with 0 if the number of digits in digits is less then numInt + if( i < numInt ){ + for( i; i < numInt; i++ ){ + oss << 0; + } + } + if( numInt < digits.size() ){ + oss << '.'; + for( i; i < digits.size(); i++ ){ + oss << digits[i]; + } + } + if( scientificExp != 0 ){ + oss << scientificChar << std::to_string( scientificExp ); + } + } + std::string str = oss.str(); + zend_string* zstr = zend_string_init( str.c_str(), str.length(), 0 ); + zend_string_release( Z_STR_P( param_z )); + ZVAL_NEW_STR( param_z, zstr ); + } + } +} + // output parameters have their reference count incremented so that they do not disappear // while the query is executed and processed. They are saved in the statement so that // their reference count may be decremented later (after results are processed) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt new file mode 100644 index 00000000..2a4932c8 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt @@ -0,0 +1,308 @@ +--TEST-- +Test for inserting into and retrieving from decimal columns of different scale +--SKIPIF-- + +--FILE-- + $num, + "Testing numbers greater than 0 and less than 1:" => $frac); +$scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); + +try { + $conn = connect(); + $tbname = "decimalTable"; + foreach ($numSets as $testName => $numSet) { + echo "\n$testName\n"; + foreach ($numSet as $input) { + $numInt = ceil(log10(abs($input) + 1)); + $decimalTypes = array(); + foreach ($scalesToTest as $scale) { + if ($scale < 39 - $numInt) { + $decimalTypes = array_merge($decimalTypes, array("c$scale" => "decimal(38, $scale)")); + } + } + if (empty($decimalTypes)) { + $decimalTypes = array("c0" => "decimal(38, 0)"); + } + createTable($conn, $tbname, $decimalTypes); + + + $insertValues = array(); + foreach ($decimalTypes as $key => $value) { + $insertValues = array_merge($insertValues, array($key => $input)); + } + insertRow($conn, $tbname, $insertValues); + + $stmt = $conn->query("SELECT * FROM $tbname"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + foreach ($row as $key => $value) { + if ($value != 0) { + echo "$key: $value\n"; + } + } + $conn->exec("TRUNCATE TABLE $tbname"); + } + } + dropTable($conn, $tbname); + unset($conn); +} catch (PDOException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- + +Testing numbers greater than 1: +c0: -10 +c1: -10.0 +c2: -10.00 +c3: -10.000 +c4: -10.0000 +c5: -10.00000 +c7: -10.0000000 +c9: -10.000000000 +c19: -10.0000000000000000000 +c28: -10.0000000000000000000000000000 +c0: 13 +c1: 13.3 +c2: 13.33 +c3: 13.330 +c4: 13.3300 +c5: 13.33000 +c7: 13.3300000 +c9: 13.330000000 +c19: 13.3300000000000000000 +c28: 13.3300000000000000000000000000 +c0: -192 +c1: -191.8 +c2: -191.78 +c3: -191.785 +c4: -191.7846 +c5: -191.78465 +c7: -191.7846470 +c9: -191.784646962 +c19: -191.7846469620226500000 +c28: -191.7846469620226500000000000000 +c0: 833 +c1: 833.3 +c2: 833.33 +c3: 833.330 +c4: 833.3300 +c5: 833.33000 +c7: 833.3300000 +c9: 833.330000000 +c19: 833.3300000000000000000 +c28: 833.3300000000000000000000000000 +c0: -850 +c1: -850.0 +c2: -850.00 +c3: -850.000 +c4: -850.0000 +c5: -850.00000 +c7: -850.0000000 +c9: -850.000000000 +c19: -850.0000000000000600000 +c28: -850.0000000000000600000000000000 +c0: 852 +c1: 851.6 +c2: 851.65 +c3: 851.648 +c4: 851.6484 +c5: 851.64835 +c7: 851.6483516 +c9: 851.648351648 +c19: 851.6483516483516800000 +c28: 851.6483516483516800000000000000 +c0: -316053 +c1: -316053.2 +c2: -316053.16 +c3: -316053.161 +c4: -316053.1605 +c5: -316053.16053 +c7: -316053.1605316 +c9: -316053.160531605 +c19: -316053.1605316053100000000 +c28: -316053.1605316053100000000000000000 +c0: 505506 +c1: 505505.5 +c2: 505505.51 +c3: 505505.506 +c4: 505505.5055 +c5: 505505.50551 +c7: 505505.5055055 +c9: 505505.505505506 +c19: 505505.5055055055055055055 +c28: 505505.5055055055055055055000000000 +c0: -1535020 +c1: -1535020.1 +c2: -1535020.06 +c3: -1535020.062 +c4: -1535020.0615 +c5: -1535020.06150 +c7: -1535020.0615000 +c9: -1535020.061500000 +c19: -1535020.0615000000000000000 +c28: -1535020.0615000000000000000000000000 +c0: 7501301 +c1: 7501300.7 +c2: 7501300.68 +c3: 7501300.675 +c4: 7501300.6750 +c5: 7501300.67501 +c7: 7501300.6750130 +c9: 7501300.675013007 +c19: 7501300.6750130067501300675 +c28: 7501300.6750130067501300675013006750 +c0: -7540010 +c1: -7540010.1 +c2: -7540010.07 +c3: -7540010.068 +c4: -7540010.0675 +c5: -7540010.06754 +c7: -7540010.0675400 +c9: -7540010.067540010 +c19: -7540010.0675400100675400101 +c28: -7540010.0675400100675400100675400101 +c0: 7540450 +c1: 7540450.1 +c2: 7540450.07 +c3: 7540450.068 +c4: 7540450.0675 +c5: 7540450.06754 +c7: 7540450.0675405 +c9: 7540450.067540450 +c19: 7540450.0675404500675404501 +c28: 7540450.0675404500675404500675404500 +c0: -820012820012820 +c1: -820012820012820.0 +c2: -820012820012820.01 +c3: -820012820012820.013 +c4: -820012820012820.0128 +c5: -820012820012820.01282 +c7: -820012820012820.0128200 +c9: -820012820012820.012820013 +c19: -820012820012820.0128200128200128200 +c0: 1122551511225515 +c1: 1122551511225515.1 +c2: 1122551511225515.11 +c3: 1122551511225515.112 +c4: 1122551511225515.1122 +c5: 1122551511225515.11220 +c7: 1122551511225515.1122000 +c9: 1122551511225515.112200000 +c19: 1122551511225515.1122000000000000000 +c0: -1234567892112345678912 +c1: -1234567892112345678912.3 +c2: -1234567892112345678912.35 +c3: -1234567892112345678912.346 +c4: -1234567892112345678912.3456 +c5: -1234567892112345678912.34560 +c7: -1234567892112345678912.3456000 +c9: -1234567892112345678912.345600000 +c0: 123456789012346261234567890123 +c1: 123456789012346261234567890123.5 +c2: 123456789012346261234567890123.46 +c3: 123456789012346261234567890123.463 +c4: 123456789012346261234567890123.4629 +c5: 123456789012346261234567890123.46290 +c7: 123456789012346261234567890123.4629000 +c0: -13775323913775323913775323913775323913 + +Testing numbers greater than 0 and less than 1: +c1: .1 +c2: .10 +c3: .100 +c4: .1000 +c5: .10000 +c7: .1000000 +c9: .100000000 +c19: .1000000000000000000 +c28: .1000000000000000000000000000 +c1: -.1 +c2: -.13 +c3: -.133 +c4: -.1333 +c5: -.13330 +c7: -.1333000 +c9: -.133300000 +c19: -.1333000000000000000 +c28: -.1333000000000000000000000000 +c2: .02 +c3: .019 +c4: .0192 +c5: .01918 +c7: .0191785 +c9: .019178465 +c19: .0191784646962022650 +c28: .0191784646962022650000000000 +c1: -.1 +c2: -.08 +c3: -.083 +c4: -.0833 +c5: -.08333 +c7: -.0833330 +c9: -.083333000 +c19: -.0833330000000000000 +c28: -.0833330000000000000000000000 +c1: .1 +c2: .09 +c3: .085 +c4: .0850 +c5: .08500 +c7: .0850000 +c9: .085000000 +c19: .0850000000000000060 +c28: .0850000000000000060000000000 +c1: -.1 +c2: -.09 +c3: -.085 +c4: -.0852 +c5: -.08516 +c7: -.0851648 +c9: -.085164835 +c19: -.0851648351648351680 +c28: -.0851648351648351680000000000 +c5: .00003 +c7: .0000316 +c9: .000031600 +c19: .0000316000000000000 +c28: .0000316000000000000000000000 +c4: -.0001 +c5: -.00005 +c7: -.0000500 +c9: -.000050000 +c19: -.0000500000000000000 +c28: -.0000500000000000000000000000 +c5: .00002 +c7: .0000154 +c9: .000015350 +c19: .0000153502000000000 +c28: .0000153502000000000000000000 +c5: -.00001 +c7: -.0000075 +c9: -.000007501 +c19: -.0000075013000000000 +c28: -.0000075013000000000000000000 +c5: .00001 +c7: .0000075 +c9: .000007540 +c19: .0000075400100000000 +c28: .0000075400100000000000000000 +c5: -.00001 +c7: -.0000075 +c9: -.000007540 +c19: -.0000075404500000000 +c28: -.0000075404500000000000000000 +c19: .0000000000082000000 +c28: .0000000000082000000000000000 +c19: -.0000000000000011226 +c28: -.0000000000000011225500000000 +c28: .0000000000000000000012345679 +c38: .00000000000000000000123456789000000000 +c28: -.0000000000000000000000012346 +c38: -.00000000000000000000000123456789012346 +c38: .00000000000000000000000000000000137753 diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt new file mode 100644 index 00000000..37125754 --- /dev/null +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt @@ -0,0 +1,307 @@ +--TEST-- +Test for inserting into and retrieving from decimal columns of different scale +--SKIPIF-- + +--FILE-- + $num, + "Testing numbers greater than 0 and less than 1:" => $frac); +$scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); + +try { + $conn = AE\connect(); + $tbname = "decimalTable"; + foreach ($numSets as $testName => $numSet) { + echo "\n$testName\n"; + foreach ($numSet as $input) { + $numInt = ceil(log10(abs($input) + 1)); + $decimalTypes = array(); + foreach ($scalesToTest as $scale) { + if ($scale < 39 - $numInt) { + array_push($decimalTypes, new AE\ColumnMeta("decimal(38, $scale)", "c$scale")); + } + } + if (empty($decimalTypes)) { + $decimalTypes = array(new AE\ColumnMeta("decimal(38, 0)", "c0")); + } + AE\createTable($conn, $tbname, $decimalTypes); + + $insertValues = array(); + foreach ($decimalTypes as $decimalType) { + $insertValues = array_merge($insertValues, array($decimalType->colName => $input)); + } + AE\insertRow($conn, $tbname, $insertValues); + + $stmt = sqlsrv_query($conn, "SELECT * FROM $tbname"); + $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC); + foreach ($row as $key => $value) { + if ($value != 0) { + echo "$key: $value\n"; + } + } + sqlsrv_query($conn, "TRUNCATE TABLE $tbname"); + } + } + dropTable($conn, $tbname); + sqlsrv_close($conn); +} catch (PDOException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- + +Testing numbers greater than 1: +c0: -10 +c1: -10.0 +c2: -10.00 +c3: -10.000 +c4: -10.0000 +c5: -10.00000 +c7: -10.0000000 +c9: -10.000000000 +c19: -10.0000000000000000000 +c28: -10.0000000000000000000000000000 +c0: 13 +c1: 13.3 +c2: 13.33 +c3: 13.330 +c4: 13.3300 +c5: 13.33000 +c7: 13.3300000 +c9: 13.330000000 +c19: 13.3300000000000000000 +c28: 13.3300000000000000000000000000 +c0: -192 +c1: -191.8 +c2: -191.78 +c3: -191.785 +c4: -191.7846 +c5: -191.78465 +c7: -191.7846470 +c9: -191.784646962 +c19: -191.7846469620226500000 +c28: -191.7846469620226500000000000000 +c0: 833 +c1: 833.3 +c2: 833.33 +c3: 833.330 +c4: 833.3300 +c5: 833.33000 +c7: 833.3300000 +c9: 833.330000000 +c19: 833.3300000000000000000 +c28: 833.3300000000000000000000000000 +c0: -850 +c1: -850.0 +c2: -850.00 +c3: -850.000 +c4: -850.0000 +c5: -850.00000 +c7: -850.0000000 +c9: -850.000000000 +c19: -850.0000000000000600000 +c28: -850.0000000000000600000000000000 +c0: 852 +c1: 851.6 +c2: 851.65 +c3: 851.648 +c4: 851.6484 +c5: 851.64835 +c7: 851.6483516 +c9: 851.648351648 +c19: 851.6483516483516800000 +c28: 851.6483516483516800000000000000 +c0: -316053 +c1: -316053.2 +c2: -316053.16 +c3: -316053.161 +c4: -316053.1605 +c5: -316053.16053 +c7: -316053.1605316 +c9: -316053.160531605 +c19: -316053.1605316053100000000 +c28: -316053.1605316053100000000000000000 +c0: 505506 +c1: 505505.5 +c2: 505505.51 +c3: 505505.506 +c4: 505505.5055 +c5: 505505.50551 +c7: 505505.5055055 +c9: 505505.505505506 +c19: 505505.5055055055055055055 +c28: 505505.5055055055055055055000000000 +c0: -1535020 +c1: -1535020.1 +c2: -1535020.06 +c3: -1535020.062 +c4: -1535020.0615 +c5: -1535020.06150 +c7: -1535020.0615000 +c9: -1535020.061500000 +c19: -1535020.0615000000000000000 +c28: -1535020.0615000000000000000000000000 +c0: 7501301 +c1: 7501300.7 +c2: 7501300.68 +c3: 7501300.675 +c4: 7501300.6750 +c5: 7501300.67501 +c7: 7501300.6750130 +c9: 7501300.675013007 +c19: 7501300.6750130067501300675 +c28: 7501300.6750130067501300675013006750 +c0: -7540010 +c1: -7540010.1 +c2: -7540010.07 +c3: -7540010.068 +c4: -7540010.0675 +c5: -7540010.06754 +c7: -7540010.0675400 +c9: -7540010.067540010 +c19: -7540010.0675400100675400101 +c28: -7540010.0675400100675400100675400101 +c0: 7540450 +c1: 7540450.1 +c2: 7540450.07 +c3: 7540450.068 +c4: 7540450.0675 +c5: 7540450.06754 +c7: 7540450.0675405 +c9: 7540450.067540450 +c19: 7540450.0675404500675404501 +c28: 7540450.0675404500675404500675404500 +c0: -820012820012820 +c1: -820012820012820.0 +c2: -820012820012820.01 +c3: -820012820012820.013 +c4: -820012820012820.0128 +c5: -820012820012820.01282 +c7: -820012820012820.0128200 +c9: -820012820012820.012820013 +c19: -820012820012820.0128200128200128200 +c0: 1122551511225515 +c1: 1122551511225515.1 +c2: 1122551511225515.11 +c3: 1122551511225515.112 +c4: 1122551511225515.1122 +c5: 1122551511225515.11220 +c7: 1122551511225515.1122000 +c9: 1122551511225515.112200000 +c19: 1122551511225515.1122000000000000000 +c0: -1234567892112345678912 +c1: -1234567892112345678912.3 +c2: -1234567892112345678912.35 +c3: -1234567892112345678912.346 +c4: -1234567892112345678912.3456 +c5: -1234567892112345678912.34560 +c7: -1234567892112345678912.3456000 +c9: -1234567892112345678912.345600000 +c0: 123456789012346261234567890123 +c1: 123456789012346261234567890123.5 +c2: 123456789012346261234567890123.46 +c3: 123456789012346261234567890123.463 +c4: 123456789012346261234567890123.4629 +c5: 123456789012346261234567890123.46290 +c7: 123456789012346261234567890123.4629000 +c0: -13775323913775323913775323913775323913 + +Testing numbers greater than 0 and less than 1: +c1: .1 +c2: .10 +c3: .100 +c4: .1000 +c5: .10000 +c7: .1000000 +c9: .100000000 +c19: .1000000000000000000 +c28: .1000000000000000000000000000 +c1: -.1 +c2: -.13 +c3: -.133 +c4: -.1333 +c5: -.13330 +c7: -.1333000 +c9: -.133300000 +c19: -.1333000000000000000 +c28: -.1333000000000000000000000000 +c2: .02 +c3: .019 +c4: .0192 +c5: .01918 +c7: .0191785 +c9: .019178465 +c19: .0191784646962022650 +c28: .0191784646962022650000000000 +c1: -.1 +c2: -.08 +c3: -.083 +c4: -.0833 +c5: -.08333 +c7: -.0833330 +c9: -.083333000 +c19: -.0833330000000000000 +c28: -.0833330000000000000000000000 +c1: .1 +c2: .09 +c3: .085 +c4: .0850 +c5: .08500 +c7: .0850000 +c9: .085000000 +c19: .0850000000000000060 +c28: .0850000000000000060000000000 +c1: -.1 +c2: -.09 +c3: -.085 +c4: -.0852 +c5: -.08516 +c7: -.0851648 +c9: -.085164835 +c19: -.0851648351648351680 +c28: -.0851648351648351680000000000 +c5: .00003 +c7: .0000316 +c9: .000031600 +c19: .0000316000000000000 +c28: .0000316000000000000000000000 +c4: -.0001 +c5: -.00005 +c7: -.0000500 +c9: -.000050000 +c19: -.0000500000000000000 +c28: -.0000500000000000000000000000 +c5: .00002 +c7: .0000154 +c9: .000015350 +c19: .0000153502000000000 +c28: .0000153502000000000000000000 +c5: -.00001 +c7: -.0000075 +c9: -.000007501 +c19: -.0000075013000000000 +c28: -.0000075013000000000000000000 +c5: .00001 +c7: .0000075 +c9: .000007540 +c19: .0000075400100000000 +c28: .0000075400100000000000000000 +c5: -.00001 +c7: -.0000075 +c9: -.000007540 +c19: -.0000075404500000000 +c28: -.0000075404500000000000000000 +c19: .0000000000082000000 +c28: .0000000000082000000000000000 +c19: -.0000000000000011226 +c28: -.0000000000000011225500000000 +c28: .0000000000000000000012345679 +c38: .00000000000000000000123456789000000000 +c28: -.0000000000000000000000012346 +c38: -.00000000000000000000000123456789012346 +c38: .00000000000000000000000000000000137753 \ No newline at end of file From 9acfab9ec2bc0595944794e34315a8bf0748b568 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Wed, 15 Nov 2017 16:38:27 -0800 Subject: [PATCH 02/10] include vector library --- source/shared/core_stmt.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 7a59cdd0..a00cbefd 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -20,6 +20,9 @@ #include "core_sqlsrv.h" #include +#include +#include + namespace { // certain drivers using this layer will call for repeated or out of order field retrievals. To allow this, we cache the From 04d509ef9d7c2506530dd913d83a811c851a58d6 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Thu, 16 Nov 2017 10:09:10 -0800 Subject: [PATCH 03/10] change values in tests to only up to 38 decimal digits --- test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt | 2 +- test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt index 2a4932c8..ea486d1d 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt @@ -7,7 +7,7 @@ Test for inserting into and retrieving from decimal columns of different scale require_once("MsCommon_mid-refactor.inc"); $num = array("-10.0", "13.33", "-191.78464696202265", "833.33", "-850.00000000000006", "851.64835164835168", "-316053.16053160531", "505505.5055055055055055055", "-1535020.0615", "7501300.675013006750130067501300675", "-7540010.067540010067540010067540010067", "7540450.0675404500675404500675404500", "-820012820012820.01282001282001282001282", "1122551511225515.1122", "-1234567892112345678912.3456", "123456789012346261234567890123.4629", "-13775323913775323913775323913775323913"); -$frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.000000000000000000000000000000001377532"); +$frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.00000000000000000000000000000001377532"); $numSets = array("Testing numbers greater than 1:" => $num, "Testing numbers greater than 0 and less than 1:" => $frac); $scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt index 37125754..533eab4d 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt @@ -7,7 +7,7 @@ Test for inserting into and retrieving from decimal columns of different scale require_once('MsHelper.inc'); $num = array("-10.0", "13.33", "-191.78464696202265", "833.33", "-850.00000000000006", "851.64835164835168", "-316053.16053160531", "505505.5055055055055055055", "-1535020.0615", "7501300.675013006750130067501300675", "-7540010.067540010067540010067540010067", "7540450.0675404500675404500675404500", "-820012820012820.01282001282001282001282", "1122551511225515.1122", "-1234567892112345678912.3456", "123456789012346261234567890123.4629", "-13775323913775323913775323913775323913"); -$frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.000000000000000000000000000000001377532"); +$frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.00000000000000000000000000000001377532"); $numSets = array("Testing numbers greater than 1:" => $num, "Testing numbers greater than 0 and less than 1:" => $frac); $scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); From 4fb2c80e697a1eaafd362143fbbcab7cdac10601 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Thu, 16 Nov 2017 11:32:24 -0800 Subject: [PATCH 04/10] change expected output --- test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt | 2 +- test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt index ea486d1d..70972619 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt @@ -305,4 +305,4 @@ c28: .0000000000000000000012345679 c38: .00000000000000000000123456789000000000 c28: -.0000000000000000000000012346 c38: -.00000000000000000000000123456789012346 -c38: .00000000000000000000000000000000137753 +c38: .00000000000000000000000000000001377532 diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt index 533eab4d..105bb106 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt @@ -304,4 +304,4 @@ c28: .0000000000000000000012345679 c38: .00000000000000000000123456789000000000 c28: -.0000000000000000000000012346 c38: -.00000000000000000000000123456789012346 -c38: .00000000000000000000000000000000137753 \ No newline at end of file +c38: .00000000000000000000000000000001377532 \ No newline at end of file From 90029e7e54d7b1c2674b259e8de769560ff924ae Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Fri, 17 Nov 2017 10:21:03 -0800 Subject: [PATCH 05/10] add test for scientific notation --- source/shared/core_stmt.cpp | 35 +- .../pdo_ae_insert_scientificNot.phpt | 405 ++++++++++++++++++ .../sqlsrv_ae_insert_scientificNot.phpt | 403 +++++++++++++++++ 3 files changed, 826 insertions(+), 17 deletions(-) create mode 100644 test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt create mode 100644 test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index a00cbefd..26f9b13e 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -531,7 +531,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ break; case IS_STRING: { - if( stmt->conn->ce_option.enabled && ( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC )){ + if ( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC ) { adjustInputPrecision( param_z, decimal_digits ); } @@ -2662,25 +2662,26 @@ bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ) { } void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits ) { - // 38 is the maximum precision supported for sql decimal types - // 6 is a composition of: 1 for '.'; 1 for sign of the number; - // 1 for 'e' or 'E' (scientific notation); 1 for sign of scientific exponent; 2 for length of scientific exponent - size_t maxDecimalLen = 38 + 6; - SQLSRV_ASSERT( Z_STRLEN_P( param_z ) < maxDecimalLen, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); + size_t maxDecimalPrecision = 38; + // maxDecimalStrLen is the maximum length of a stringified decimal number + // 6 is derived from: 1 for '.'; 1 for sign of the number; 1 for 'e' or 'E' (scientific notation); + // 1 for sign of scientific exponent; 2 for length of scientific exponent + size_t maxDecimalStrLen = maxDecimalPrecision + 6; + SQLSRV_ASSERT( Z_STRLEN_P( param_z ) < maxDecimalStrLen, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); std::vector digits; char* ptr = ZSTR_VAL( Z_STR_P( param_z )); bool isNeg = false; char scientificChar = ' '; - int scientificExp = 0; + short scientificExp = 0; // parse digits in param_z into the vector digits if( *ptr == '+' || *ptr == '-' ){ - if( *ptr = '-' ){ + if( *ptr == '-' ){ isNeg = true; } ptr++; } - size_t numInt = 0; - size_t numDec = 0; + short numInt = 0; + short numDec = 0; while( isdigit( *ptr )){ digits.push_back( *ptr - '0' ); ptr++; @@ -2707,28 +2708,28 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit ptr++; } while( isdigit( *ptr )){ - scientificExp = scientificExp * 10 + *ptr - '0'; + scientificExp = scientificExp * 10 + ( *ptr - '0' ); ptr++; } - SQLSRV_ASSERT( scientificExp <=38, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); + SQLSRV_ASSERT( scientificExp <= maxDecimalPrecision, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); if( isNegExp ){ scientificExp = scientificExp * -1; } } SQLSRV_ASSERT( *ptr == '\0', "Invalid input decimal: Invalid character found in the decimal string." ); // if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision - if(( int )numDec > scientificExp ){ - int decToRemove = numDec - scientificExp - decimal_digits; + if( numDec > scientificExp ){ + short decToRemove = numDec - scientificExp - decimal_digits; if( decToRemove > 0 ){ bool carryOver = false; - int backInd = 0; + short backInd = 0; // pop digits from the vector until there is only 1 more decimal place than required decimal_digits while( decToRemove != 1 && !digits.empty() ){ digits.pop_back(); decToRemove--; } if( !digits.empty() ){ - // check if the last digit to be popped is greater than 5, if so, the digit before is needs to round up + // check if the last digit to be popped is greater than 5, if so, the digit before it needs to round up carryOver = digits.back() >= 5; digits.pop_back(); backInd = digits.size() - 1; @@ -2756,7 +2757,7 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit oss << 0; } else{ - int i = 0; + short i = 0; for( i; i < numInt && i < digits.size(); i++ ){ oss << digits[i]; } diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt new file mode 100644 index 00000000..ccc86530 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt @@ -0,0 +1,405 @@ +--TEST-- +Test for inserting into and retrieving from decimal columns of different scale +--SKIPIF-- + +--FILE-- + $posExp, + "Testing numbers greater than 0 and less than 1:" => $negExp); +$scalesToTest = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 19); + +try { + $conn = connect(); + $tbname = "decimalTable"; + + foreach ($numSets as $testName => $numSet) { + echo "\n$testName\n"; + foreach ($numSet as $input) { + $numInt = ceil(log10(abs($input) + 1)); + $decimalTypes = array(); + foreach ($scalesToTest as $scale) { + if ($scale < 39 - $numInt) { + $decimalTypes = array_merge($decimalTypes, array("c$scale" => "decimal(38, $scale)")); + } + } + if (empty($decimalTypes)) { + $decimalTypes = array("c0" => "decimal(38, 0)"); + } + createTable($conn, $tbname, $decimalTypes); + + $insertValues = array(); + foreach ($decimalTypes as $key => $value) { + if (isColEncrypted()) { + $insertValues = array_merge($insertValues, array($key => strval($input))); + } else { + $insertValues = array_merge($insertValues, array($key => $input)); + } + } + insertRow($conn, $tbname, $insertValues, "prepareBindParam"); + + $stmt = $conn->query("SELECT * FROM $tbname"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + foreach ($row as $key => $value) { + if ($value != 0) { + echo "$key: $value\n"; + } + } + $conn->exec("TRUNCATE TABLE $tbname"); + } + } + dropTable($conn, $tbname); + unset($conn); +} catch (PDOException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- + +Testing numbers greater than 1: +c0: 10 +c1: 10.0 +c2: 10.00 +c3: 10.000 +c4: 10.0000 +c5: 10.00000 +c6: 10.000000 +c7: 10.0000000 +c8: 10.00000000 +c9: 10.000000000 +c19: 10.0000000000000000000 +c0: -13 +c1: -13.3 +c2: -13.33 +c3: -13.330 +c4: -13.3300 +c5: -13.33000 +c6: -13.330000 +c7: -13.3300000 +c8: -13.33000000 +c9: -13.330000000 +c19: -13.3300000000000000000 +c0: 192 +c1: 191.8 +c2: 191.78 +c3: 191.785 +c4: 191.7846 +c5: 191.78465 +c6: 191.784647 +c7: 191.7846470 +c8: 191.78464696 +c9: 191.784646962 +c19: 191.7846469620200000000 +c0: -833 +c1: -833.3 +c2: -833.33 +c3: -833.330 +c4: -833.3300 +c5: -833.33000 +c6: -833.330000 +c7: -833.3300000 +c8: -833.33000000 +c9: -833.330000000 +c19: -833.3300000000000000000 +c0: 850 +c1: 850.0 +c2: 850.00 +c3: 850.000 +c4: 850.0000 +c5: 850.00000 +c6: 850.000000 +c7: 850.0000000 +c8: 850.00000000 +c9: 850.000000000 +c19: 850.0000000000000000000 +c0: -852 +c1: -851.6 +c2: -851.65 +c3: -851.648 +c4: -851.6484 +c5: -851.64835 +c6: -851.648352 +c7: -851.6483516 +c8: -851.64835165 +c9: -851.648351648 +c19: -851.6483516483500000000 +c0: 316000 +c1: 316000.0 +c2: 316000.00 +c3: 316000.000 +c4: 316000.0000 +c5: 316000.00000 +c6: 316000.000000 +c7: 316000.0000000 +c8: 316000.00000000 +c9: 316000.000000000 +c19: 316000.0000000000000000000 +c0: -500000 +c1: -500000.0 +c2: -500000.00 +c3: -500000.000 +c4: -500000.0000 +c5: -500000.00000 +c6: -500000.000000 +c7: -500000.0000000 +c8: -500000.00000000 +c9: -500000.000000000 +c19: -500000.0000000000000000000 +c0: 1535020 +c1: 1535020.0 +c2: 1535020.00 +c3: 1535020.000 +c4: 1535020.0000 +c5: 1535020.00000 +c6: 1535020.000000 +c7: 1535020.0000000 +c8: 1535020.00000000 +c9: 1535020.000000000 +c19: 1535020.0000000000000000000 +c0: -7501300 +c1: -7501300.0 +c2: -7501300.00 +c3: -7501300.000 +c4: -7501300.0000 +c5: -7501300.00000 +c6: -7501300.000000 +c7: -7501300.0000000 +c8: -7501300.00000000 +c9: -7501300.000000000 +c19: -7501300.0000000000000000000 +c0: 7540010 +c1: 7540010.0 +c2: 7540010.00 +c3: 7540010.000 +c4: 7540010.0000 +c5: 7540010.00000 +c6: 7540010.000000 +c7: 7540010.0000000 +c8: 7540010.00000000 +c9: 7540010.000000000 +c19: 7540010.0000000000000000000 +c0: -7540450 +c1: -7540450.0 +c2: -7540450.00 +c3: -7540450.000 +c4: -7540450.0000 +c5: -7540450.00000 +c6: -7540450.000000 +c7: -7540450.0000000 +c8: -7540450.00000000 +c9: -7540450.000000000 +c19: -7540450.0000000000000000000 +c0: 8200000000000 +c1: 8200000000000.0 +c2: 8200000000000.00 +c3: 8200000000000.000 +c4: 8200000000000.0000 +c5: 8200000000000.00000 +c6: 8200000000000.000000 +c7: 8200000000000.0000000 +c8: 8200000000000.00000000 +c9: 8200000000000.000000000 +c19: 8200000000000.0000000000000000000 +c0: -11225500 +c1: -11225500.0 +c2: -11225500.00 +c3: -11225500.000 +c4: -11225500.0000 +c5: -11225500.00000 +c6: -11225500.000000 +c7: -11225500.0000000 +c8: -11225500.00000000 +c9: -11225500.000000000 +c19: -11225500.0000000000000000000 +c0: 1234567890 +c1: 1234567890.0 +c2: 1234567890.00 +c3: 1234567890.000 +c4: 1234567890.0000 +c5: 1234567890.00000 +c6: 1234567890.000000 +c7: 1234567890.0000000 +c8: 1234567890.00000000 +c9: 1234567890.000000000 +c19: 1234567890.0000000000000000000 +c0: -12345679 +c1: -12345678.9 +c2: -12345678.90 +c3: -12345678.901 +c4: -12345678.9012 +c5: -12345678.90124 +c6: -12345678.901235 +c7: -12345678.9012350 +c8: -12345678.90123500 +c9: -12345678.901235000 +c19: -12345678.9012350000000000000 +c0: 13775320000 +c1: 13775320000.0 +c2: 13775320000.00 +c3: 13775320000.000 +c4: 13775320000.0000 +c5: 13775320000.00000 +c6: 13775320000.000000 +c7: 13775320000.0000000 +c8: 13775320000.00000000 +c9: 13775320000.000000000 +c19: 13775320000.0000000000000000000 + +Testing numbers greater than 0 and less than 1: +c0: -10 +c1: -10.0 +c2: -10.00 +c3: -10.000 +c4: -10.0000 +c5: -10.00000 +c6: -10.000000 +c7: -10.0000000 +c8: -10.00000000 +c9: -10.000000000 +c19: -10.0000000000000000000 +c1: .1 +c2: .13 +c3: .133 +c4: .1333 +c5: .13330 +c6: .133300 +c7: .1333000 +c8: .13330000 +c9: .133300000 +c19: .1333000000000000000 +c2: -.02 +c3: -.019 +c4: -.0192 +c5: -.01918 +c6: -.019178 +c7: -.0191785 +c8: -.01917846 +c9: -.019178465 +c19: -.0191784646962020000 +c1: .1 +c2: .08 +c3: .083 +c4: .0833 +c5: .08333 +c6: .083333 +c7: .0833330 +c8: .08333300 +c9: .083333000 +c19: .0833330000000000000 +c1: -.1 +c2: -.09 +c3: -.085 +c4: -.0850 +c5: -.08500 +c6: -.085000 +c7: -.0850000 +c8: -.08500000 +c9: -.085000000 +c19: -.0850000000000000000 +c1: .1 +c2: .09 +c3: .085 +c4: .0852 +c5: .08516 +c6: .085165 +c7: .0851648 +c8: .08516484 +c9: .085164835 +c19: .0851648351648350000 +c1: -.3 +c2: -.32 +c3: -.316 +c4: -.3160 +c5: -.31600 +c6: -.316000 +c7: -.3160000 +c8: -.31600000 +c9: -.316000000 +c19: -.3160000000000000000 +c2: .01 +c3: .005 +c4: .0050 +c5: .00500 +c6: .005000 +c7: .0050000 +c8: .00500000 +c9: .005000000 +c19: .0050000000000000000 +c4: -.0002 +c5: -.00015 +c6: -.000154 +c7: -.0001535 +c8: -.00015350 +c9: -.000153502 +c19: -.0001535020000000000 +c3: .001 +c4: .0008 +c5: .00075 +c6: .000750 +c7: .0007501 +c8: .00075013 +c9: .000750130 +c19: .0007501300000000000 +c3: -.001 +c4: -.0008 +c5: -.00075 +c6: -.000754 +c7: -.0007540 +c8: -.00075400 +c9: -.000754001 +c19: -.0007540010000000000 +c3: .001 +c4: .0008 +c5: .00075 +c6: .000754 +c7: .0007540 +c8: .00075405 +c9: .000754045 +c19: .0007540450000000000 +c0: -82 +c1: -82.0 +c2: -82.00 +c3: -82.000 +c4: -82.0000 +c5: -82.00000 +c6: -82.000000 +c7: -82.0000000 +c8: -82.00000000 +c9: -82.000000000 +c19: -82.0000000000000000000 +c4: .0001 +c5: .00011 +c6: .000112 +c7: .0001123 +c8: .00011226 +c9: .000112255 +c19: .0001122550000000000 +c3: -.001 +c4: -.0012 +c5: -.00123 +c6: -.001235 +c7: -.0012346 +c8: -.00123457 +c9: -.001234568 +c19: -.0012345678900000000 +c4: .0001 +c5: .00012 +c6: .000123 +c7: .0001235 +c8: .00012346 +c9: .000123457 +c19: .0001234567890123500 +c1: -.1 +c2: -.14 +c3: -.138 +c4: -.1378 +c5: -.13775 +c6: -.137753 +c7: -.1377532 +c8: -.13775320 +c9: -.137753200 +c19: -.1377532000000000000 diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt new file mode 100644 index 00000000..b5b9d6c5 --- /dev/null +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt @@ -0,0 +1,403 @@ +--TEST-- +Test for inserting into and retrieving from decimal columns of different scale +--SKIPIF-- + +--FILE-- + $posExp, +"Testing numbers greater than 0 and less than 1:" => $negExp); +$scalesToTest = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 19); + +try { + $conn = AE\connect(); + $tbname = "decimalTable"; + foreach ($numSets as $testName => $numSet) { + echo "\n$testName\n"; + foreach ($numSet as $input) { + $numInt = ceil(log10(abs($input) + 1)); + $decimalTypes = array(); + foreach ($scalesToTest as $scale) { + if ($scale < 39 - $numInt) { + array_push($decimalTypes, new AE\ColumnMeta("decimal(38, $scale)", "c$scale")); + } + } + if (empty($decimalTypes)) { + $decimalTypes = array(new AE\ColumnMeta("decimal(38, 0)", "c0")); + } + AE\createTable($conn, $tbname, $decimalTypes); + + $insertValues = array(); + foreach ($decimalTypes as $decimalType) { + $scale = intval(ltrim($decimalType->colName, "c")); + array_push($insertValues, array($input, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_DECIMAL(38, $scale))); + } + $insertSql = "INSERT INTO $tbname VALUES(" . AE\getSeqPlaceholders(count($insertValues)) . ")"; + $stmt = sqlsrv_prepare($conn, $insertSql, $insertValues); + sqlsrv_execute($stmt); + + $stmt = sqlsrv_query($conn, "SELECT * FROM $tbname"); + $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC); + foreach ($row as $key => $value) { + if ($value != 0) { + echo "$key: $value\n"; + } + } + sqlsrv_query($conn, "TRUNCATE TABLE $tbname"); + } + } + dropTable($conn, $tbname); + sqlsrv_close($conn); +} catch (PDOException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- + +Testing numbers greater than 1: +c0: 10 +c1: 10.0 +c2: 10.00 +c3: 10.000 +c4: 10.0000 +c5: 10.00000 +c6: 10.000000 +c7: 10.0000000 +c8: 10.00000000 +c9: 10.000000000 +c19: 10.0000000000000000000 +c0: -13 +c1: -13.3 +c2: -13.33 +c3: -13.330 +c4: -13.3300 +c5: -13.33000 +c6: -13.330000 +c7: -13.3300000 +c8: -13.33000000 +c9: -13.330000000 +c19: -13.3300000000000000000 +c0: 192 +c1: 191.8 +c2: 191.78 +c3: 191.785 +c4: 191.7846 +c5: 191.78465 +c6: 191.784647 +c7: 191.7846470 +c8: 191.78464696 +c9: 191.784646962 +c19: 191.7846469620226500000 +c0: -833 +c1: -833.3 +c2: -833.33 +c3: -833.330 +c4: -833.3300 +c5: -833.33000 +c6: -833.330000 +c7: -833.3300000 +c8: -833.33000000 +c9: -833.330000000 +c19: -833.3300000000000000000 +c0: 850 +c1: 850.0 +c2: 850.00 +c3: 850.000 +c4: 850.0000 +c5: 850.00000 +c6: 850.000000 +c7: 850.0000000 +c8: 850.00000000 +c9: 850.000000000 +c19: 850.0000000000000600000 +c0: -852 +c1: -851.6 +c2: -851.65 +c3: -851.648 +c4: -851.6484 +c5: -851.64835 +c6: -851.648352 +c7: -851.6483516 +c8: -851.64835165 +c9: -851.648351648 +c19: -851.6483516483516800000 +c0: 316000 +c1: 316000.0 +c2: 316000.00 +c3: 316000.000 +c4: 316000.0000 +c5: 316000.00000 +c6: 316000.000000 +c7: 316000.0000000 +c8: 316000.00000000 +c9: 316000.000000000 +c19: 316000.0000000000000000000 +c0: -500000 +c1: -500000.0 +c2: -500000.00 +c3: -500000.000 +c4: -500000.0000 +c5: -500000.00000 +c6: -500000.000000 +c7: -500000.0000000 +c8: -500000.00000000 +c9: -500000.000000000 +c19: -500000.0000000000000000000 +c0: 1535020 +c1: 1535020.0 +c2: 1535020.00 +c3: 1535020.000 +c4: 1535020.0000 +c5: 1535020.00000 +c6: 1535020.000000 +c7: 1535020.0000000 +c8: 1535020.00000000 +c9: 1535020.000000000 +c19: 1535020.0000000000000000000 +c0: -7501300 +c1: -7501300.0 +c2: -7501300.00 +c3: -7501300.000 +c4: -7501300.0000 +c5: -7501300.00000 +c6: -7501300.000000 +c7: -7501300.0000000 +c8: -7501300.00000000 +c9: -7501300.000000000 +c19: -7501300.0000000000000000000 +c0: 7540010 +c1: 7540010.0 +c2: 7540010.00 +c3: 7540010.000 +c4: 7540010.0000 +c5: 7540010.00000 +c6: 7540010.000000 +c7: 7540010.0000000 +c8: 7540010.00000000 +c9: 7540010.000000000 +c19: 7540010.0000000000000000000 +c0: -7540450 +c1: -7540450.0 +c2: -7540450.00 +c3: -7540450.000 +c4: -7540450.0000 +c5: -7540450.00000 +c6: -7540450.000000 +c7: -7540450.0000000 +c8: -7540450.00000000 +c9: -7540450.000000000 +c19: -7540450.0000000000000000000 +c0: 8200000000000 +c1: 8200000000000.0 +c2: 8200000000000.00 +c3: 8200000000000.000 +c4: 8200000000000.0000 +c5: 8200000000000.00000 +c6: 8200000000000.000000 +c7: 8200000000000.0000000 +c8: 8200000000000.00000000 +c9: 8200000000000.000000000 +c19: 8200000000000.0000000000000000000 +c0: -11225500 +c1: -11225500.0 +c2: -11225500.00 +c3: -11225500.000 +c4: -11225500.0000 +c5: -11225500.00000 +c6: -11225500.000000 +c7: -11225500.0000000 +c8: -11225500.00000000 +c9: -11225500.000000000 +c19: -11225500.0000000000000000000 +c0: 1234567890 +c1: 1234567890.0 +c2: 1234567890.00 +c3: 1234567890.000 +c4: 1234567890.0000 +c5: 1234567890.00000 +c6: 1234567890.000000 +c7: 1234567890.0000000 +c8: 1234567890.00000000 +c9: 1234567890.000000000 +c19: 1234567890.0000000000000000000 +c0: -12345679 +c1: -12345678.9 +c2: -12345678.90 +c3: -12345678.901 +c4: -12345678.9012 +c5: -12345678.90123 +c6: -12345678.901235 +c7: -12345678.9012346 +c8: -12345678.90123460 +c9: -12345678.901234600 +c19: -12345678.9012346000000000000 +c0: 13775320000 +c1: 13775320000.0 +c2: 13775320000.00 +c3: 13775320000.000 +c4: 13775320000.0000 +c5: 13775320000.00000 +c6: 13775320000.000000 +c7: 13775320000.0000000 +c8: 13775320000.00000000 +c9: 13775320000.000000000 +c19: 13775320000.0000000000000000000 + +Testing numbers greater than 0 and less than 1: +c0: -10 +c1: -10.0 +c2: -10.00 +c3: -10.000 +c4: -10.0000 +c5: -10.00000 +c6: -10.000000 +c7: -10.0000000 +c8: -10.00000000 +c9: -10.000000000 +c19: -10.0000000000000000000 +c1: .1 +c2: .13 +c3: .133 +c4: .1333 +c5: .13330 +c6: .133300 +c7: .1333000 +c8: .13330000 +c9: .133300000 +c19: .1333000000000000000 +c2: -.02 +c3: -.019 +c4: -.0192 +c5: -.01918 +c6: -.019178 +c7: -.0191785 +c8: -.01917846 +c9: -.019178465 +c19: -.0191784646962022650 +c1: .1 +c2: .08 +c3: .083 +c4: .0833 +c5: .08333 +c6: .083333 +c7: .0833330 +c8: .08333300 +c9: .083333000 +c19: .0833330000000000000 +c1: -.1 +c2: -.09 +c3: -.085 +c4: -.0850 +c5: -.08500 +c6: -.085000 +c7: -.0850000 +c8: -.08500000 +c9: -.085000000 +c19: -.0850000000000000060 +c1: .1 +c2: .09 +c3: .085 +c4: .0852 +c5: .08516 +c6: .085165 +c7: .0851648 +c8: .08516484 +c9: .085164835 +c19: .0851648351648351680 +c1: -.3 +c2: -.32 +c3: -.316 +c4: -.3160 +c5: -.31600 +c6: -.316000 +c7: -.3160000 +c8: -.31600000 +c9: -.316000000 +c19: -.3160000000000000000 +c2: .01 +c3: .005 +c4: .0050 +c5: .00500 +c6: .005000 +c7: .0050000 +c8: .00500000 +c9: .005000000 +c19: .0050000000000000000 +c4: -.0002 +c5: -.00015 +c6: -.000154 +c7: -.0001535 +c8: -.00015350 +c9: -.000153502 +c19: -.0001535020000000000 +c3: .001 +c4: .0008 +c5: .00075 +c6: .000750 +c7: .0007501 +c8: .00075013 +c9: .000750130 +c19: .0007501300000000000 +c3: -.001 +c4: -.0008 +c5: -.00075 +c6: -.000754 +c7: -.0007540 +c8: -.00075400 +c9: -.000754001 +c19: -.0007540010000000000 +c3: .001 +c4: .0008 +c5: .00075 +c6: .000754 +c7: .0007540 +c8: .00075405 +c9: .000754045 +c19: .0007540450000000000 +c0: -82 +c1: -82.0 +c2: -82.00 +c3: -82.000 +c4: -82.0000 +c5: -82.00000 +c6: -82.000000 +c7: -82.0000000 +c8: -82.00000000 +c9: -82.000000000 +c19: -82.0000000000000000000 +c4: .0001 +c5: .00011 +c6: .000112 +c7: .0001123 +c8: .00011226 +c9: .000112255 +c19: .0001122550000000000 +c3: -.001 +c4: -.0012 +c5: -.00123 +c6: -.001235 +c7: -.0012346 +c8: -.00123457 +c9: -.001234568 +c19: -.0012345678900000000 +c4: .0001 +c5: .00012 +c6: .000123 +c7: .0001235 +c8: .00012346 +c9: .000123457 +c19: .0001234567890123460 +c1: -.1 +c2: -.14 +c3: -.138 +c4: -.1378 +c5: -.13775 +c6: -.137753 +c7: -.1377532 +c8: -.13775320 +c9: -.137753200 +c19: -.1377532000000000000 From bb1e1c01685d427fcc38b090ce94ca4aa0df9ecb Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Fri, 17 Nov 2017 12:30:14 -0800 Subject: [PATCH 06/10] do not throw an error if the decimal string is ill formed; let SQL Server handle it --- source/shared/core_stmt.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 26f9b13e..fec2c4b8 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -2716,10 +2716,11 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit scientificExp = scientificExp * -1; } } - SQLSRV_ASSERT( *ptr == '\0', "Invalid input decimal: Invalid character found in the decimal string." ); + // if ptr is not pointing to a null terminator at this point, that means the decimal string input is invalid + // no to change to string and let SQL Server handle the invalid decimal string // if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision - if( numDec > scientificExp ){ - short decToRemove = numDec - scientificExp - decimal_digits; + if(*ptr == '\0' && ( int )numDec > scientificExp ){ + int decToRemove = numDec - scientificExp - decimal_digits; if( decToRemove > 0 ){ bool carryOver = false; short backInd = 0; From fc7c4fb89bbf1dfeee18588085cef20b68f779df Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Fri, 17 Nov 2017 13:27:45 -0800 Subject: [PATCH 07/10] return if the length of the decimal string is greater than maxDecimalStrLen --- source/shared/core_stmt.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index fec2c4b8..a3aba2fa 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -2666,8 +2666,11 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit // maxDecimalStrLen is the maximum length of a stringified decimal number // 6 is derived from: 1 for '.'; 1 for sign of the number; 1 for 'e' or 'E' (scientific notation); // 1 for sign of scientific exponent; 2 for length of scientific exponent + // if the length is greater than maxDecimalStrLen, do not change the string size_t maxDecimalStrLen = maxDecimalPrecision + 6; - SQLSRV_ASSERT( Z_STRLEN_P( param_z ) < maxDecimalStrLen, "Input decimal overflow: sql decimal type only supports up to a precision of 38." ); + if (Z_STRLEN_P(param_z) > maxDecimalStrLen) { + return; + } std::vector digits; char* ptr = ZSTR_VAL( Z_STR_P( param_z )); bool isNeg = false; @@ -2717,7 +2720,7 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit } } // if ptr is not pointing to a null terminator at this point, that means the decimal string input is invalid - // no to change to string and let SQL Server handle the invalid decimal string + // do not change the string and let SQL Server handle the invalid decimal string // if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision if(*ptr == '\0' && ( int )numDec > scientificExp ){ int decToRemove = numDec - scientificExp - decimal_digits; From f815043a05d498fa746409307e091f6e92dd7043 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Fri, 17 Nov 2017 16:57:23 -0800 Subject: [PATCH 08/10] change description of inputs --- test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt | 4 ++-- test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt | 4 ++-- test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt | 4 ++-- test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt index 70972619..8f4690d6 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt @@ -8,8 +8,8 @@ require_once("MsCommon_mid-refactor.inc"); $num = array("-10.0", "13.33", "-191.78464696202265", "833.33", "-850.00000000000006", "851.64835164835168", "-316053.16053160531", "505505.5055055055055055055", "-1535020.0615", "7501300.675013006750130067501300675", "-7540010.067540010067540010067540010067", "7540450.0675404500675404500675404500", "-820012820012820.01282001282001282001282", "1122551511225515.1122", "-1234567892112345678912.3456", "123456789012346261234567890123.4629", "-13775323913775323913775323913775323913"); $frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.00000000000000000000000000000001377532"); -$numSets = array("Testing numbers greater than 1:" => $num, - "Testing numbers greater than 0 and less than 1:" => $frac); +$numSets = array("Testing numbers greater than 1 or less than -1:" => $num, + "Testing numbers between 1 and -1:" => $frac); $scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); try { diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt index ccc86530..a077e1a7 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt @@ -8,8 +8,8 @@ require_once("MsCommon_mid-refactor.inc"); $posExp = array(-0.00e+01, 10.0E+00, -1.333e+1, 1.9178464696202265E+2, -8.3333e+2, 8.5000000000000006E+2, -8.5164835164835168E+2, 3.16E+05, -5E+05, 1.53502e+006, -7.5013e+006, 7.54001e+006, -7.54045e+006, 820.0E+10, -1.12255E+7, 1.23456789E+9, -1.23456789012346E+7, 1.377532E+10); $negExp = array(0.00e-01, -10.0E-00, 1.333e-1, -1.9178464696202265E-2, 8.3333e-2, -8.5000000000000006E-2, 8.5164835164835168E-2, -3.16E-01, 5E-03, -1.53502e-004, 7.5013e-004, -7.54001e-004, 7.54045e-004, -820.0E-1, 1.12255E-4, -1.23456789E-3, 1.23456789012346E-4, -1.377532E-1); -$numSets = array("Testing numbers greater than 1:" => $posExp, - "Testing numbers greater than 0 and less than 1:" => $negExp); +$numSets = array("Testing numbers greater than 1 or less than -1:" => $posExp, + "Testing numbers between 1 and -1:" => $negExp); $scalesToTest = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 19); try { diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt index 105bb106..2bde7ae7 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt @@ -8,8 +8,8 @@ require_once('MsHelper.inc'); $num = array("-10.0", "13.33", "-191.78464696202265", "833.33", "-850.00000000000006", "851.64835164835168", "-316053.16053160531", "505505.5055055055055055055", "-1535020.0615", "7501300.675013006750130067501300675", "-7540010.067540010067540010067540010067", "7540450.0675404500675404500675404500", "-820012820012820.01282001282001282001282", "1122551511225515.1122", "-1234567892112345678912.3456", "123456789012346261234567890123.4629", "-13775323913775323913775323913775323913"); $frac = array("-0.000", "0.100", "-0.1333", "0.019178464696202265", "-0.083333", "0.085000000000000006", "-0.085164835164835168", "0.0000316", "-0.00005", "0.0000153502", "-0.0000075013", "0.00000754001", "-0.00000754045", "0.000000000008200", "-0.00000000000000112255", "0.00000000000000000000123456789", "-0.00000000000000000000000123456789012346", "0.00000000000000000000000000000001377532"); -$numSets = array("Testing numbers greater than 1:" => $num, - "Testing numbers greater than 0 and less than 1:" => $frac); +$numSets = array("Testing numbers greater than 1 or less than -1:" => $num, + "Testing numbers between 1 and -1:" => $frac); $scalesToTest = array(0, 1, 2, 3, 4, 5, 7, 9, 19, 28, 38); try { diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt index b5b9d6c5..bfe77a98 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt @@ -8,8 +8,8 @@ require_once('MsHelper.inc'); $posExp = array("-0.00e+01", "10.0E+00", "-1.333e+1", "1.9178464696202265E+2", "-8.3333e+2", "8.5000000000000006E+2", "-8.5164835164835168E+2", "3.16E+05", "-5E+05", "1.53502e+006", "-7.5013e+006", "7.54001e+006", "-7.54045e+006", "820.0E+10", "-1.12255E+7", "1.23456789E+9", "-1.23456789012346E+7", "1.377532E+10"); $negExp = array("0.00e-01", "-10.0E-00", "1.333e-1", "-1.9178464696202265E-2", "8.3333e-2", "-8.5000000000000006E-2", "8.5164835164835168E-2", "-3.16E-01", "5E-03", "-1.53502e-004", "7.5013e-004", "-7.54001e-004", "7.54045e-004", "-820.0E-1", "1.12255E-4", "-1.23456789E-3", "1.23456789012346E-4", "-1.377532E-1"); -$numSets = array("Testing numbers greater than 1:" => $posExp, -"Testing numbers greater than 0 and less than 1:" => $negExp); +$numSets = array("Testing numbers greater than 1 or less than -1:" => $posExp, + "Testing numbers between 1 and -1:" => $negExp); $scalesToTest = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 19); try { From 20480812c2308b6744aebd73a0eda8c64debbb24 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 20 Nov 2017 16:06:39 -0800 Subject: [PATCH 09/10] remove include string in core_stmt.cpp --- source/shared/core_stmt.cpp | 1 - test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt | 4 ++-- test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt | 4 ++-- test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt | 4 ++-- test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt | 4 ++-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index a3aba2fa..a6195dda 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -19,7 +19,6 @@ #include "core_sqlsrv.h" -#include #include #include diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt index 8f4690d6..4a51b434 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_decimal.phpt @@ -56,7 +56,7 @@ try { ?> --EXPECT-- -Testing numbers greater than 1: +Testing numbers greater than 1 or less than -1: c0: -10 c1: -10.0 c2: -10.00 @@ -212,7 +212,7 @@ c5: 123456789012346261234567890123.46290 c7: 123456789012346261234567890123.4629000 c0: -13775323913775323913775323913775323913 -Testing numbers greater than 0 and less than 1: +Testing numbers between 1 and -1: c1: .1 c2: .10 c3: .100 diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt index a077e1a7..14205f95 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_scientificNot.phpt @@ -60,7 +60,7 @@ try { ?> --EXPECT-- -Testing numbers greater than 1: +Testing numbers greater than 1 or less than -1: c0: 10 c1: 10.0 c2: 10.00 @@ -249,7 +249,7 @@ c8: 13775320000.00000000 c9: 13775320000.000000000 c19: 13775320000.0000000000000000000 -Testing numbers greater than 0 and less than 1: +Testing numbers between 1 and -1: c0: -10 c1: -10.0 c2: -10.00 diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt index 2bde7ae7..656c6025 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_decimal.phpt @@ -55,7 +55,7 @@ try { ?> --EXPECT-- -Testing numbers greater than 1: +Testing numbers greater than 1 or less than -1: c0: -10 c1: -10.0 c2: -10.00 @@ -211,7 +211,7 @@ c5: 123456789012346261234567890123.46290 c7: 123456789012346261234567890123.4629000 c0: -13775323913775323913775323913775323913 -Testing numbers greater than 0 and less than 1: +Testing numbers between 1 and -1: c1: .1 c2: .10 c3: .100 diff --git a/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt index bfe77a98..16c78696 100644 --- a/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt +++ b/test/functional/sqlsrv/sqlsrv_ae_insert_scientificNot.phpt @@ -58,7 +58,7 @@ try { ?> --EXPECT-- -Testing numbers greater than 1: +Testing numbers greater than 1 or less than -1: c0: 10 c1: 10.0 c2: 10.00 @@ -247,7 +247,7 @@ c8: 13775320000.00000000 c9: 13775320000.000000000 c19: 13775320000.0000000000000000000 -Testing numbers greater than 0 and less than 1: +Testing numbers between 1 and -1: c0: -10 c1: -10.0 c2: -10.00 From 0b135b1529bdc19a7bc318c64f7488246d2b9d75 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Tue, 21 Nov 2017 13:17:39 -0800 Subject: [PATCH 10/10] if not scientific notation, only parse up to decimal_digits --- source/shared/core_stmt.cpp | 38 ++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index a6195dda..5f0975df 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -2673,8 +2673,12 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit std::vector digits; char* ptr = ZSTR_VAL( Z_STR_P( param_z )); bool isNeg = false; + bool isScientificNot = false; char scientificChar = ' '; short scientificExp = 0; + if( strchr( ptr, 'e' ) || strchr( ptr, 'E' )){ + isScientificNot = true; + } // parse digits in param_z into the vector digits if( *ptr == '+' || *ptr == '-' ){ if( *ptr == '-' ){ @@ -2691,16 +2695,29 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit } if( *ptr == '.' ){ ptr++; - while( isdigit(*ptr) ){ - digits.push_back( *ptr - '0' ); - ptr++; - numDec++; + if( !isScientificNot ){ + while( isdigit( *ptr ) && numDec < decimal_digits + 1 ){ + digits.push_back( *ptr - '0' ); + ptr++; + numDec++; + } + // make sure the rest of the number are digits + while( isdigit( *ptr )){ + ptr++; + } + } + else { + while( isdigit( *ptr )){ + digits.push_back( *ptr - '0' ); + ptr++; + numDec++; + } } } - if( *ptr == 'e' || *ptr == 'E' ){ - scientificChar = *ptr; - } - if( scientificChar != ' ' ){ + if( isScientificNot ){ + if ( *ptr == 'e' || *ptr == 'E' ) { + scientificChar = *ptr; + } ptr++; bool isNegExp = false; if( *ptr == '+' || *ptr == '-' ){ @@ -2720,8 +2737,11 @@ void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digit } // if ptr is not pointing to a null terminator at this point, that means the decimal string input is invalid // do not change the string and let SQL Server handle the invalid decimal string + if ( *ptr != '\0' ) { + return; + } // if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision - if(*ptr == '\0' && ( int )numDec > scientificExp ){ + if( numDec > scientificExp ){ int decToRemove = numDec - scientificExp - decimal_digits; if( decToRemove > 0 ){ bool carryOver = false;