Skip to content

Commit

Permalink
Merge pull request #134 from denizzzka/money_support
Browse files Browse the repository at this point in the history
Adds money.currency support
  • Loading branch information
denizzzka authored Feb 24, 2019
2 parents 46d6f40 + c33af62 commit 0b0b210
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ Features
* Immutable query result for simplify multithreading
* Async queries support
* Reading of the text query results to native D text types
* Representation of the binary query results to native D types
* Representation of binary arguments and binary query results as native D types
* Text types
* Integer and decimal types
* Money type (into money.currency, https://github.com/qznc/d-money)
* Some data and time types
* JSON type (stored into vibe.data.json.Json)
* JSONB type (ditto)
Expand Down
3 changes: 2 additions & 1 deletion dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"targetPath": "bin",
"dependencies": {
"derelict-pq": "~>4.0.0-alpha.2",
"vibe-d:data": "~>0.8.3-beta.1"
"vibe-d:data": "~>0.8.3-beta.1",
"money": "~>2.3.0"
},
"targetType": "sourceLibrary",
"-ddoxTool": "scod",
Expand Down
27 changes: 25 additions & 2 deletions src/dpq2/conv/from_d_types.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import std.bitmanip: nativeToBigEndian;
import std.datetime.date: Date, DateTime, TimeOfDay;
import std.datetime.systime: SysTime;
import std.datetime.timezone: LocalTime, TimeZone, UTC;
import std.traits: isImplicitlyConvertible, isNumeric, OriginalType, Unqual;
import std.traits: isImplicitlyConvertible, isNumeric, isInstanceOf, OriginalType, Unqual;
import std.typecons : Nullable;
import std.uuid: UUID;
import vibe.data.json: Json;
import money: currency;

/// Converts Nullable!T to Value
Value toValue(T)(T v)
Expand Down Expand Up @@ -48,6 +49,28 @@ if(isNumeric!(T))
return Value(v.nativeToBigEndian.dup, detectOidTypeFromNative!T, false, ValueFormat.BINARY);
}

/// Convert money.currency to PG value
///
/// Caution: here is no check of fractional precision while conversion!
/// See also: PostgreSQL's "lc_monetary" description and "money" package description
Value toValue(T)(T v)
if(isInstanceOf!(currency, T) && T.amount.sizeof == 8)
{
return Value(v.amount.nativeToBigEndian.dup, OidType.Money, false, ValueFormat.BINARY);
}

unittest
{
import dpq2.conv.to_d_types: PGTestMoney;

const pgtm = PGTestMoney(-123.45);

Value v = pgtm.toValue;

assert(v.oidType == OidType.Money);
assert(v.as!PGTestMoney == pgtm);
}

/**
Converts types implicitly convertible to string to PG Value.
Note that if string is null it is written as an empty string.
Expand All @@ -73,7 +96,7 @@ if(is(T : immutable(ubyte)[]))
return Value(v, detectOidTypeFromNative!(ubyte[]), false, ValueFormat.BINARY);
}

///
/// Constructs Value from boolean
Value toValue(T : bool)(T v) @trusted
if (!is(T == Nullable!R, R))
{
Expand Down
5 changes: 5 additions & 0 deletions src/dpq2/conv/native_tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public void _integration_test( string connParam ) @system
// to return times in other than UTC time zone but fixed time zone so make the test reproducible in databases with other TZ
conn.exec("SET TIMEZONE TO +02");

conn.exec("SET lc_monetary = 'en_US.UTF-8'");

QueryParams params;
params.resultFormat = ValueFormat.BINARY;

Expand Down Expand Up @@ -106,13 +108,16 @@ public void _integration_test( string connParam ) @system

alias C = testIt; // "C" means "case"

import dpq2.conv.to_d_types: PGTestMoney;

C!PGboolean(true, "boolean", "true");
C!PGboolean(false, "boolean", "false");
C!(Nullable!PGboolean)(Nullable!PGboolean.init, "boolean", "NULL");
C!(Nullable!PGboolean)(Nullable!PGboolean(true), "boolean", "true");
C!PGsmallint(-32_761, "smallint", "-32761");
C!PGinteger(-2_147_483_646, "integer", "-2147483646");
C!PGbigint(-9_223_372_036_854_775_806, "bigint", "-9223372036854775806");
C!PGTestMoney(PGTestMoney(-123.45), "money", "'-$123.45'");
C!PGreal(-12.3456f, "real", "-12.3456");
C!PGdouble_precision(-1234.56789012345, "double precision", "-1234.56789012345");
C!PGtext("first line\nsecond line", "text", "'first line\nsecond line'");
Expand Down
38 changes: 38 additions & 0 deletions src/dpq2/conv/to_d_types.d
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,41 @@ if( is( T == Json ) )
auto v = Value([1], OidType.Int4);
assertThrown!ValueConvException(v.binaryValueAs!Json);
}

import money: currency, roundingMode;

/// Returns money type
///
/// Caution: here is no check of fractional precision while conversion!
/// See also: PostgreSQL's "lc_monetary" description and "money" package description
T binaryValueAs(T)(in Value v) @trusted
if( isInstanceOf!(currency, T) && T.amount.sizeof == 8 )
{
import std.format: format;

if(v.data.length != T.amount.sizeof)
throw new AE(
ET.SIZE_MISMATCH,
format(
"%s length (%d) isn't equal to D money type %s size (%d)",
v.oidType.to!string,
v.data.length,
typeid(T).to!string,
T.amount.sizeof
)
);

T r;

r.amount = v.data[0 .. T.amount.sizeof].bigEndianToNative!long;

return r;
}

package alias PGTestMoney = currency!("TEST_CURR", 2); //TODO: roundingMode.UNNECESSARY

unittest
{
auto v = Value([1], OidType.Money);
assertThrown!ValueConvException(v.binaryValueAs!PGTestMoney);
}

0 comments on commit 0b0b210

Please sign in to comment.