From: daniel dot beet at accuratesoftware dot com
Operating system: Windows 2000 Server
PHP version: 4.3.7
PHP Bug Type: Sybase-ct (ctlib) related
Bug description: Exact numeric/decimal/money datatypes lose precision
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 bug report at http://bugs.php.net/?id=29064&edit=1
--
Try a CVS snapshot (php4): http://bugs.php.net/fix.php?id=29064&r=trysnapshot4
Try a CVS snapshot (php5): http://bugs.php.net/fix.php?id=29064&r=trysnapshot5
Fixed in CVS: http://bugs.php.net/fix.php?id=29064&r=fixedcvs
Fixed in release: http://bugs.php.net/fix.php?id=29064&r=alreadyfixed
Need backtrace: http://bugs.php.net/fix.php?id=29064&r=needtrace
Need Reproduce Script: http://bugs.php.net/fix.php?id=29064&r=needscript
Try newer version: http://bugs.php.net/fix.php?id=29064&r=oldversion
Not developer issue: http://bugs.php.net/fix.php?id=29064&r=support
Expected behavior: http://bugs.php.net/fix.php?id=29064&r=notwrong
Not enough info: http://bugs.php.net/fix.php?id=29064&r=notenoughinfo
Submitted twice: http://bugs.php.net/fix.php?id=29064&r=submittedtwice
register_globals: http://bugs.php.net/fix.php?id=29064&r=globals
PHP 3 support discontinued: http://bugs.php.net/fix.php?id=29064&r=php3
Daylight Savings: http://bugs.php.net/fix.php?id=29064&r=dst
IIS Stability: http://bugs.php.net/fix.php?id=29064&r=isapi
Install GNU Sed: http://bugs.php.net/fix.php?id=29064&r=gnused
Floating point limitations: http://bugs.php.net/fix.php?id=29064&r=float