diff --git a/db-test/gorm_test.go b/db-test/gorm_test.go index 8cbc956..a7c1453 100644 --- a/db-test/gorm_test.go +++ b/db-test/gorm_test.go @@ -70,7 +70,7 @@ func TestCompositeType(t *testing.T) { } // Create custom composite type. - if err := gormDB.Exec("DROP TYPE IF EXISTS d3money; CREATE TYPE d3money AS (amount DECIMAL, currency VARCHAR);").Error; err != nil { + if err := gormDB.Exec("DROP TYPE IF EXISTS d3money; CREATE TYPE d3money AS (amount DECIMAL, currency INTEGER);").Error; err != nil { t.Fatalf("Failed to create d3money composite type: %v", err) } @@ -118,7 +118,7 @@ func TestCompositeType(t *testing.T) { // Test direct access to the fields of the composite type. type CompositeFields struct { Amount decimal.Decimal - Currency string + Currency int32 } var a1ReadFields CompositeFields @@ -126,7 +126,7 @@ func TestCompositeType(t *testing.T) { t.Errorf("Failed to query account 1: %v", err) } - a1ExpectedFields := CompositeFields{a1.Balance.Decimal(), a1.Balance.Currency().UniqueCode()} + a1ExpectedFields := CompositeFields{a1.Balance.Decimal(), a1.Balance.Currency().UniqueID()} if !reflect.DeepEqual(a1ReadFields, a1ExpectedFields) { t.Errorf("Queried balance fields %+v don't match expected fields %+v", a1ReadFields, a1ExpectedFields) } @@ -136,7 +136,7 @@ func TestCompositeType(t *testing.T) { t.Errorf("Failed to query account 2: %v", err) } - a2ExpectedFields := CompositeFields{a2.Balance.Decimal(), ""} + a2ExpectedFields := CompositeFields{a2.Balance.Decimal(), 0} if !reflect.DeepEqual(a2ReadFields, a2ExpectedFields) { t.Errorf("Queried balance fields %+v don't match expected fields %+v", a2ReadFields, a2ExpectedFields) } diff --git a/value-databindings.go b/value-databindings.go index baf3dae..ddee22d 100644 --- a/value-databindings.go +++ b/value-databindings.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2022 David Vogel +// Copyright (c) 2021-2023 David Vogel // // This software is released under the MIT License. // https://opensource.org/licenses/MIT @@ -10,6 +10,7 @@ import ( "encoding/binary" "encoding/json" "fmt" + "strconv" "strings" "github.com/shopspring/decimal" @@ -123,7 +124,7 @@ func (v *Value) GobDecode(data []byte) error { func (v Value) Value() (driver.Value, error) { if v.currency != nil { // Output "Amount UniqueCode" pair. - return "(" + v.amount.String() + "," + v.currency.UniqueCode() + ")", nil + return "(" + v.amount.String() + "," + strconv.Itoa(int(v.currency.UniqueID())) + ")", nil } // If there is no currency output only "Amount". @@ -138,13 +139,47 @@ func (v *Value) Scan(value interface{}) error { } trimmed := strings.Trim(str, "()") - replaced := strings.ReplaceAll(trimmed, ",", " ") - amount, newCur, err := parse(strings.TrimRight(replaced, " "), Currencies, nil) + + var amountStr, curStr string + var currency Currency + + // Parse expression. + split := strings.Split(trimmed, ",") + switch len(split) { + case 1: + // String contains an amount string. + amountStr = strings.Trim(split[0], " ") + + case 2: + // String contains an amount string + unique currency code. + amountStr, curStr = strings.Trim(split[0], " "), strings.Trim(split[1], " ") + + if curStr != "" { + // Look in global collection for the currency. + uniqueID64, err := strconv.ParseInt(curStr, 10, 32) + if err != nil { + return fmt.Errorf("failed to parse currency ID from database field: %w", err) + } + uniqueID := int32(uniqueID64) + currency = Currencies.ByUniqueID(uniqueID) + + // If there is no match, return error. + if currency == nil { + return &ErrorCantFindUniqueID{uniqueID} + } + } + + default: + return fmt.Errorf("input string %q contains too many spaces", str) + } + + // Parse amount string. + amount, err := decimal.NewFromString(amountStr) if err != nil { - return fmt.Errorf("failed to parse string %q: %w", str, err) + return err } - v.amount, v.currency = amount, newCur + v.amount, v.currency = amount, currency return nil }