fix for right truncation error when precision of the column definition and input is not aligned

This commit is contained in:
v-kaywon 2017-10-18 18:10:30 -07:00
parent def61df42c
commit 02e145d988
3 changed files with 214 additions and 62 deletions

View file

@ -459,7 +459,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
if ( stmt->conn->ce_option.enabled && ( sql_type == SQL_UNKNOWN_TYPE || column_size == SQLSRV_UNKNOWN_SIZE )) {
ae_get_sql_type_info( stmt, param_num, direction, param_z, encoding, sql_type, column_size, decimal_digits TSRMLS_CC );
// change long to double if the sql type is decimal
if ( sql_type == SQL_DECIMAL && Z_TYPE_P( param_z ) == IS_LONG )
if (( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC ) && Z_TYPE_P(param_z) == IS_LONG )
convert_to_double( param_z );
}
else {
@ -516,8 +516,40 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
}
break;
case IS_STRING:
{
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 && 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 ) {
@ -531,13 +563,13 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
}
buffer = Z_STRVAL_P( &wbuffer_z );
buffer_len = Z_STRLEN_P( &wbuffer_z );
core::sqlsrv_add_index_zval(*stmt, &(stmt->param_input_strings), param_num, &wbuffer_z TSRMLS_CC);
core::sqlsrv_add_index_zval( *stmt, &( stmt->param_input_strings ), param_num, &wbuffer_z TSRMLS_CC );
}
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
// PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion,
// we reallocate the string if it's interned
if ( ZSTR_IS_INTERNED( Z_STR_P( param_z ))) {
if( ZSTR_IS_INTERNED( Z_STR_P( param_z ))) {
core::sqlsrv_zval_stringl( param_z, static_cast<const char*>(buffer), buffer_len );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
@ -580,9 +612,9 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
// string types. This will guarantee that there is no silent truncation for
// output parameters.
// if column encryption is enabled, at this point the correct column size has been set by SQLDescribeParam
if( direction == SQL_PARAM_OUTPUT && !stmt->conn->ce_option.enabled ) {
if ( direction == SQL_PARAM_OUTPUT && !stmt->conn->ce_option.enabled ) {
switch( sql_type ) {
switch ( sql_type ) {
case SQL_VARBINARY:
case SQL_VARCHAR:
@ -595,6 +627,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
}
}
}
}
break;
case IS_RESOURCE:
{

View file

@ -0,0 +1,64 @@
--TEST--
prepare with emulate prepare and binding integer
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
try {
$conn = connect();
$tableName = "decimal_table";
createTable($conn, $tableName, array("c1_decimal0" => "decimal", "c2_decimal4" => "decimal(19,4)"));
insertRow($conn, $tableName, array("c1_decimal0" => 0.9, "c2_decimal4" => 0.9));
insertRow($conn, $tableName, array("c1_decimal0" => 9.9, "c2_decimal4" => 9.9));
insertRow($conn, $tableName, array("c1_decimal0" => 999.999, "c2_decimal4" => 999.999));
insertRow($conn, $tableName, array("c1_decimal0" => 99999.99999, "c2_decimal4" => 99999.99999));
$query = "SELECT * FROM $tableName";
$stmt = $conn->query($query);
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($row);
dropTable($conn, $tableName);
unset($stmt);
unset($conn);
} catch (PDOException $e) {
var_dump($e->errorInfo);
}
?>
--EXPECT--
array(4) {
[0]=>
array(2) {
["c1_decimal0"]=>
string(1) "1"
["c2_decimal4"]=>
string(5) ".9000"
}
[1]=>
array(2) {
["c1_decimal0"]=>
string(2) "10"
["c2_decimal4"]=>
string(6) "9.9000"
}
[2]=>
array(2) {
["c1_decimal0"]=>
string(4) "1000"
["c2_decimal4"]=>
string(8) "999.9990"
}
[3]=>
array(2) {
["c1_decimal0"]=>
string(6) "100000"
["c2_decimal4"]=>
string(11) "100000.0000"
}
}

View file

@ -0,0 +1,55 @@
--TEST--
prepare with emulate prepare and binding integer
--SKIPIF--
--FILE--
<?php
require_once("MsHelper.inc");
$conn = AE\connect();
$tableName = "decimal_table";
AE\createTable($conn, $tableName, array(new AE\ColumnMeta("decimal", "c1_decimal0"), new AE\ColumnMeta("decimal(19,4)", "c2_decimal4")));
AE\insertRow($conn, $tableName, array("c1_decimal0" => 0.9, "c2_decimal4" => 0.9));
AE\insertRow($conn, $tableName, array("c1_decimal0" => 9.9, "c2_decimal4" => 9.9));
AE\insertRow($conn, $tableName, array("c1_decimal0" => 999.999, "c2_decimal4" => 999.999));
AE\insertRow($conn, $tableName, array("c1_decimal0" => 99999.99999, "c2_decimal4" => 99999.99999));
$query = "SELECT * FROM $tableName";
$stmt = sqlsrv_query($conn, $query);
while (($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) != NULL) {
//print_r($row);
var_dump($row);
}
dropTable($conn, $tableName);
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
?>
--EXPECT--
array(2) {
["c1_decimal0"]=>
string(1) "1"
["c2_decimal4"]=>
string(5) ".9000"
}
array(2) {
["c1_decimal0"]=>
string(2) "10"
["c2_decimal4"]=>
string(6) "9.9000"
}
array(2) {
["c1_decimal0"]=>
string(4) "1000"
["c2_decimal4"]=>
string(8) "999.9990"
}
array(2) {
["c1_decimal0"]=>
string(6) "100000"
["c2_decimal4"]=>
string(11) "100000.0000"
}