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

Reply via email to