Skip to content

Commit

Permalink
Handle Num, Rat, and FatRat as numbers
Browse files Browse the repository at this point in the history
Num can always use MYSQL_TYPE_DOUBLE.

Rat can sometimes use MYSQL_TYPE_DOUBLE (when it fits into Num) but sometimes needs to be sent as MYSQL_TYPE_NEWDECIMAL. Prefer to use the Double as it should be considerably cheaper (CPU vs bandwidth) despite the extra tests to see fit though I did not use a benchmark to prove that.

FatRat always uses MYSQL_TYPE_NEWDECIMAL. We can assume numbers are very large based on the developers type choice.

Closes #76
  • Loading branch information
rbt committed Nov 28, 2022
1 parent a9bef8a commit 74c1a3e
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 1 deletion.
28 changes: 28 additions & 0 deletions lib/DBDish/mysql/StatementHandle.rakumod
Expand Up @@ -125,6 +125,34 @@ method execute(*@params --> DBDish::StatementHandle) {
.Str.encode;
}
}
when Num {
$st = MYSQL_TYPE_DOUBLE;
my $buf = buf8.new();
$buf.write-num64(0, $_);
$buf;
}
when Rat {
# Similar to Num above but the conversion to Num can be lossy. Fallback to Decimal
# if precision would be lost. Most smaller numbers, like decimalized currency,
# will work as a Double.
#
# TODO: It might be possible to look at Rats denominator length instead.
my Num $cast-num = $_.Num;
my Str $cast-string = $cast-num.Str;
if $_.Str eq $cast-string {
$st = MYSQL_TYPE_DOUBLE;
my $buf = buf8.new();
$buf.write-num64(0, $cast-num);
$buf;
} else {
$st = MYSQL_TYPE_NEWDECIMAL;
.Str.encode;
}
}
when FatRat {
$st = MYSQL_TYPE_NEWDECIMAL;
.Str.encode;
}
when DateTime {
# mysql knows nothing of timezones, all assumed local-time
# but in Windows the parser chokes with the offset,
Expand Down
65 changes: 64 additions & 1 deletion t/24-mysql-types.t
Expand Up @@ -2,7 +2,8 @@ use v6;
use Test;
use DBIish::CommonTesting;

plan 2;
plan 4;

my %con-parms = :database<dbdishtest>, :user<testuser>, :password<testpass>;
%con-parms<host> = %*ENV<MYSQL_HOST> if %*ENV<MYSQL_HOST>;
my $dbh = DBIish::CommonTesting.connect-or-skip('mysql', |%con-parms);
Expand Down Expand Up @@ -77,3 +78,65 @@ subtest 'Very large integers' => {
}
}

subtest 'Numbers are transmitted as numbers' => {
$dbh.execute(
'CREATE TEMPORARY TABLE tmp_num_test AS SELECT ? as num, ? as intrat, ? as decimalrat, ? as fatrat, ? as unspecified',
2.0.Num, 2.0.Rat, 2.2.Rat, 2.2.FatRat, 2.0);

my $sth = $dbh.execute('describe tmp_num_test');
while my $row = $sth.row {
my ($col-name, $datatype-buf) = $row;
my $datatype = $datatype-buf.decode;
if $col-name eq 'fatrat' {
like $datatype, /^decimal/, "$col-name type is decimal";
} else {
is $datatype, 'double', "$col-name type is double";
}
}

$sth = $dbh.execute('SELECT * FROM tmp_num_test');
my ($num, $intrat, $decrat, $fatrat, $unspecified, $string) = $sth.row;
is $num, 2.0.Num, 'Num value roundtriped';
is $intrat, 2.0.Rat, 'Rat (integer) value roundtriped';
is $decrat, 2.2.Rat, 'Rat (decimal) value roundtriped';
is $fatrat, 2.2.FatRat, 'FatRat value roundtriped';
is $unspecified, 2.0.Num, 'Unspecified was treated as Num';
}

subtest 'Large Rats' => {
lives-ok {
$dbh.execute(qq|
CREATE TEMPORARY TABLE test_long_rat (
col1 numeric(64, 30)
)|)
}, 'Table created';

my @values = 10.43, -10.34,
'0.123456789012345678901'.FatRat, '-0.123456789012345678901'.FatRat,
(2 ** 63 + 0.1).FatRat, (-2 ** 63 + 0.1).FatRat,
(2 ** 80 + 0.1).FatRat, (-2 ** 80 + 0.1).FatRat,
'0.123456789012345678901'.FatRat, '-0.123456789012345678901'.FatRat;
my $sth = $dbh.prepare('INSERT INTO test_long_rat (col1) VALUES(?)');

for @values -> $num {
lives-ok {
$sth.execute($num);
}, 'Add value: %d digits'.sprintf($num.chars);
}
$sth.dispose;

$sth = $dbh.execute('SELECT col1 FROM test_long_rat ORDER BY col1');

is $sth.rows, @values.elems, '%d row'.sprintf(@values.elems);

# Compare both source and DB long strings in order by value.
for @values.sort -> $num {
my ($col1) = $sth.row;

isa-ok $col1, Rat;

# FatRat to ensure stringification of Rat (via is eqv operation) doesn't impact
# the value.
is $col1.FatRat, $num.FatRat, 'Value: %d digits'.sprintf($num.chars);
}
}

0 comments on commit 74c1a3e

Please sign in to comment.