ID: 29064 Updated by: [EMAIL PROTECTED] Reported By: daniel dot beet at accuratesoftware dot com Status: Assigned Bug Type: Sybase-ct (ctlib) related Operating System: Windows 2000 Server PHP Version: 4.3.7 Assigned To: thekid New Comment:
Btw: Sybase-CT behaves like PHP itself (due to the afforementioned reason): $ php -r 'var_dump(12345678901234567890123456789012.123456);' float(1.23456789012E+31) (the above result depends on the precision set: $ php -dprecision=20 -r 'var_dump((float)"12345678901234567890123456789012.123456");' float(1.234567890123457E+31) Previous Comments: ------------------------------------------------------------------------ [2004-07-11 13:19:19] [EMAIL PROTECTED] Thank you for your bug report, first of all. > /* numerics can overflow real and long types, return as a string */ I would not like to do that in general. I use the convert_scalar_to_number() function (defined in Zend/zend_operators.c) on numerics to achieve the "best" result, which is: * If the scale is 0 and if it fits into an int, make it an int, make it a float otherwise (this simulates PHP's behaviour when overflowing LONG_MAX) * If the scale is not 0, make it a float Now what I forgot was that a float may not be able to hold the number returned, therefore my suggestion would be to check for float overflow and thus add this to the above list: * If a float overflow is detected, make it a string How's that? ------------------------------------------------------------------------ [2004-07-08 17:08:17] daniel dot beet at accuratesoftware dot com Sorry, forgot the SQL to insert test data: insert into test (test_decimal, test_numeric, test_money, test_bigint, test_int, test_smallmoney, test_smallint, test_tinyint, test_real, test_double) values (12345678901234567890123456789012.123456, 12345678901234567890123456.123456789012, 123456789012345.1234, 12345678901234567890123456789012345678, 1234567890, 123456.1234, 12345, 123, 123456789.12345679, 123456789.12345679 ) insert into test (test_decimal, test_numeric, test_money, test_bigint, test_int, test_smallmoney, test_smallint, test_tinyint, test_real, test_double) values (-12345678901234567890123456789012.123456, -12345678901234567890123456.123456789012, -123456789012345.1234, -12345678901234567890123456789012345678, -1234567890, -123456.1234, -12345, 255, -123456789.12345679, -123456789.12345679 ) ------------------------------------------------------------------------ [2004-07-08 17:06:18] daniel dot beet at accuratesoftware dot com Description: ------------ PHP's Sybase CT library converts all numeric datatypes to ints of floats, so numbers outside their ranges lose precision. The following patchs fix these issues in a similar way to mssql and oci8 libs: Compare: (<)php-4.3.7\ext\sybase_ct\php_sybase_ct.1.14.2.3.h (3780 bytes) with: (>)php-4.3.7\ext\sybase_ct\php_sybase_ct.h (3929 bytes) 94c94 < int max_length, numeric; --- > int max_length, numeric, precision, scale; Compare: (<)php-4.3.7\ext\sybase_ct\php_sybase_ct.1.73.2.16.c (65966 bytes) with: (>)php-4.3.7\ext\sybase_ct\php_sybase_ct.c (68800 bytes) 1233,1235c1233,1242 < case CS_MONEY4_TYPE: < result->datafmt[i].maxlength = 24; < result->numerics[i] = 2; --- > result->datafmt[i].maxlength = 24; > result->numerics[i] = 5; > result->datafmt[i].precision = 19; > result->datafmt[i].scale = 4; > break; > case CS_MONEY4_TYPE: > result->datafmt[i].maxlength = 13; > result->numerics[i] = 2; > result->datafmt[i].precision = 10; > result->datafmt[i].scale = 4; 1244,1246c1251,1254 < result->datafmt[i].maxlength = result->datafmt[i].precision + 3; < /* numeric(10) vs numeric(10, 1) */ < result->numerics[i] = (result->datafmt[i].scale == 0) ? 3 : 2; --- > /* numerics can overflow real and long types, return > as a string */ > result->datafmt[i].maxlength = > result->datafmt[i].precision + 3; > /* numeric(10) vs numeric(10, 1) */ > result->numerics[i] = (result->datafmt[i].scale == 0) > ? 4 : 5; 1277c1285,1287 < result->fields[i].numeric = result->numerics[i]; --- > result->fields[i].numeric = (result->numerics[i] > 0) ? 1 : 0; > result->fields[i].precision = result->datafmt[i].precision; > result->fields[i].scale = result->datafmt[i].scale; 1862,1864c1872,1876 < case CS_NUMERIC_TYPE: < case CS_DECIMAL_TYPE: < return "real"; --- > return "real"; > break; > case CS_NUMERIC_TYPE: > case CS_DECIMAL_TYPE: > return "numeric"; 1929a1941,1942 > add_property_long(return_value, "precision", result->fields[field_offset].precision); > add_property_long(return_value, "scale", result->fields[field_offset].scale); Hope that helps! Thanks, Dan. Reproduce code: --------------- Create a table test: create table test ( test_decimal decimal(38, 6) null, test_numeric numeric(38, 12) null, test_money money null, test_bigint decimal(38, 0) null, test_int int null, test_smallmoney smallmoney null, test_smallint smallint null, test_tinyint tinyint null, test_real float null, test_double float null ) execute the following script via php cli exe: <?php sybase_min_server_severity(11); sybase_min_client_severity(11); $conn = sybase_connect('yourserver', 'sa', 'password'); echo "Connection OK<br />\n"; sybase_select_db('test', $conn); echo "DB selected OK<br />\n"; $result = sybase_query('select test_decimal, test_numeric, test_money, test_smallmoney, test_bigint, test_int, test_smallint, test_tinyint, test_real, test_double from test', $conn); echo "Query OK<br />\n"; $f = sybase_num_fields($result); for ($i = 0; $i < $f; $i++) { $array[] = sybase_fetch_field($result, $i); } while ($ar = sybase_fetch_assoc($result)) { $array[] = $ar; } echo "Results fetched OK<br />\n"; echo '<pre>'; var_export($array); echo '</pre>'; sybase_close($conn); ?> Expected result: ---------------- Connection OK<br /> DB selected OK<br /> Query OK<br /> Results fetched OK<br /> <pre>array ( 0 => class stdClass { var $name = 'test_decimal'; var $max_length = 40; var $column_source = ''; var $numeric = 1; var $type = 'numeric'; var $precision = 38; var $scale = 6; }, 1 => class stdClass { var $name = 'test_numeric'; var $max_length = 40; var $column_source = ''; var $numeric = 1; var $type = 'numeric'; var $precision = 38; var $scale = 12; }, 2 => class stdClass { var $name = 'test_money'; var $max_length = 21; var $column_source = ''; var $numeric = 1; var $type = 'money'; var $precision = 19; var $scale = 4; }, 3 => class stdClass { var $name = 'test_smallmoney'; var $max_length = 12; var $column_source = ''; var $numeric = 1; var $type = 'money'; var $precision = 10; var $scale = 4; }, 4 => class stdClass { var $name = 'test_bigint'; var $max_length = 40; var $column_source = ''; var $numeric = 1; var $type = 'numeric'; var $precision = 38; var $scale = 0; }, 5 => class stdClass { var $name = 'test_int'; var $max_length = 11; var $column_source = ''; var $numeric = 1; var $type = 'int'; var $precision = 0; var $scale = 0; }, 6 => class stdClass { var $name = 'test_smallint'; var $max_length = 6; var $column_source = ''; var $numeric = 1; var $type = 'int'; var $precision = 0; var $scale = 0; }, 7 => class stdClass { var $name = 'test_tinyint'; var $max_length = 3; var $column_source = ''; var $numeric = 1; var $type = 'int'; var $precision = 0; var $scale = 0; }, 8 => class stdClass { var $name = 'test_real'; var $max_length = 23; var $column_source = ''; var $numeric = 1; var $type = 'real'; var $precision = 0; var $scale = 0; }, 9 => class stdClass { var $name = 'test_double'; var $max_length = 23; var $column_source = ''; var $numeric = 1; var $type = 'real'; var $precision = 0; var $scale = 0; }, 10 => array ( 'test_decimal' => '12345678901234567890123456789012.123456', 'test_numeric' => '12345678901234567890123456.123456789012', 'test_money' => '123456789012345.1234', 'test_smallmoney' => 123456.1234, 'test_bigint' => '12345678901234567890123456789012345678', 'test_int' => 1234567890, 'test_smallint' => 12345, 'test_tinyint' => 123, 'test_real' => 123456792, 'test_double' => 123456792, ), 11 => array ( 'test_decimal' => '-12345678901234567890123456789012.123456', 'test_numeric' => '-12345678901234567890123456.123456789012', 'test_money' => '-123456789012345.1234', 'test_smallmoney' => -123456.1234, 'test_bigint' => '-12345678901234567890123456789012345678', 'test_int' => -1234567890, 'test_smallint' => -12345, 'test_tinyint' => 255, 'test_real' => -123456792, 'test_double' => -123456792, ), )</pre> Actual result: -------------- Connection OK<br /> DB selected OK<br /> Query OK<br /> Results fetched OK<br /> <pre>array ( 0 => class stdClass { var $name = 'test_decimal'; var $max_length = 40; var $column_source = ''; var $numeric = 2; var $type = 'real'; }, 1 => class stdClass { var $name = 'test_numeric'; var $max_length = 40; var $column_source = ''; var $numeric = 2; var $type = 'real'; }, 2 => class stdClass { var $name = 'test_money'; var $max_length = 23; var $column_source = ''; var $numeric = 2; var $type = 'money'; }, 3 => class stdClass { var $name = 'test_smallmoney'; var $max_length = 23; var $column_source = ''; var $numeric = 2; var $type = 'money'; }, 4 => class stdClass { var $name = 'test_bigint'; var $max_length = 40; var $column_source = ''; var $numeric = 3; var $type = 'real'; }, 5 => class stdClass { var $name = 'test_int'; var $max_length = 11; var $column_source = ''; var $numeric = 1; var $type = 'int'; }, 6 => class stdClass { var $name = 'test_smallint'; var $max_length = 6; var $column_source = ''; var $numeric = 1; var $type = 'int'; }, 7 => class stdClass { var $name = 'test_tinyint'; var $max_length = 3; var $column_source = ''; var $numeric = 1; var $type = 'int'; }, 8 => class stdClass { var $name = 'test_real'; var $max_length = 23; var $column_source = ''; var $numeric = 2; var $type = 'real'; }, 9 => class stdClass { var $name = 'test_double'; var $max_length = 23; var $column_source = ''; var $numeric = 2; var $type = 'real'; }, 10 => array ( 'test_decimal' => 1.2345678901235E+31, 'test_numeric' => 1.2345678901235E+25, 'test_money' => 123456789012350, 'test_smallmoney' => 123456.12, 'test_bigint' => 1.2345678901235E+37, 'test_int' => 1234567890, 'test_smallint' => 12345, 'test_tinyint' => 123, 'test_real' => 123456792, 'test_double' => 123456792, ), 11 => array ( 'test_decimal' => -1.2345678901235E+31, 'test_numeric' => -1.2345678901235E+25, 'test_money' => -123456789012350, 'test_smallmoney' => -123456.12, 'test_bigint' => -1.2345678901235E+37, 'test_int' => -1234567890, 'test_smallint' => -12345, 'test_tinyint' => 255, 'test_real' => -123456792, 'test_double' => -123456792, ), )</pre> ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=29064&edit=1