Skip to content

Commit

Permalink
new DateString variant of Date allows DB storage using TEXT or DATE c…
Browse files Browse the repository at this point in the history
…olumns
  • Loading branch information
rickb777 committed Apr 25, 2019
1 parent 64df880 commit bd8f90e
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 57 deletions.
58 changes: 34 additions & 24 deletions sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ func (d *Date) Scan(value interface{}) (err error) {
return nil
}

if DisableTextStorage {
return d.scanInt(value)
}
return d.scanAny(value)
}

Expand Down Expand Up @@ -57,37 +54,50 @@ func (d *Date) scanString(value string) (err error) {
return err
}

func (d *Date) scanInt(value interface{}) (err error) {
err = nil
switch value.(type) {
case int64:
*d = Date{PeriodOfDays(value.(int64))}
default:
err = fmt.Errorf("%T %+v is not a meaningful date", value, value)
}
return
}

// Value converts the value to an int64. It implements driver.Valuer,
// https://golang.org/pkg/database/sql/driver/#Valuer
func (d Date) ConvertValue(v interface{}) (driver.Value, error) {
switch v.(type) {
case string, []byte:
return d.String(), nil
case time.Time:
return d.UTC(), nil
}
func (d Date) Value() (driver.Value, error) {
return int64(d.day), nil
}

//-------------------------------------------------------------------------------------------------

// DateString alters Date to make database storage use a string column, or
// a similar derived column such as SQL DATE. (Otherwise, Date is stored as
// an integer).
type DateString Date

// Date provides a simple fluent type conversion to the underlying type.
func (d DateString) Date() Date {
return Date(d)
}

// DateString provides a simple fluent type conversion from the underlying type.
func (d Date) DateString() DateString {
return DateString(d)
}

// Scan parses some value. It implements sql.Scanner,
// https://golang.org/pkg/database/sql/#Scanner
func (d *DateString) Scan(value interface{}) (err error) {
if value == nil {
return nil
}
return (*Date)(d).Scan(value)
}

// Value converts the value to an int64. It implements driver.Valuer,
// https://golang.org/pkg/database/sql/driver/#Valuer
//func (d Date) Value() (driver.Value, error) {
// return int64(d.day), nil
//}
func (d DateString) Value() (driver.Value, error) {
return d.Date().String(), nil
}

//-------------------------------------------------------------------------------------------------

// DisableTextStorage reduces the Scan method so that only integers are handled.
// Normally, database types int64, []byte, string and time.Time are supported.
// When set true, only int64 is supported; this mode allows optimisation of SQL
// result processing and would only be used during development.
//
// Deprecated: this is no longer used.
var DisableTextStorage = false
113 changes: 80 additions & 33 deletions sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,24 @@ import (
func TestDateScan(t *testing.T) {
cases := []struct {
v interface{}
disallow bool
expected PeriodOfDays
}{
{int64(0), false, 0},
{int64(1000), false, 1000},
{int64(10000), false, 10000},
{int64(0), true, 0},
{int64(1000), true, 1000},
{int64(10000), true, 10000},
{"0", false, 0},
{"1000", false, 1000},
{"10000", false, 10000},
{"2018-12-31", false, 17896},
{"31/12/2018", false, 17896},
{[]byte("10000"), false, 10000},
{PeriodOfDays(10000).Date().Local(), false, 10000},
{int64(0), 0},
{int64(1000), 1000},
{int64(10000), 10000},
{int64(0), 0},
{int64(1000), 1000},
{int64(10000), 10000},
{"0", 0},
{"1000", 1000},
{"10000", 10000},
{"2018-12-31", 17896},
{"31/12/2018", 17896},
{[]byte("10000"), 10000},
{PeriodOfDays(10000).Date().Local(), 10000},
}

prior := DisableTextStorage

for i, c := range cases {
DisableTextStorage = c.disallow
r := new(Date)
e := r.Scan(c.v)
if e != nil {
Expand All @@ -43,43 +39,89 @@ func TestDateScan(t *testing.T) {
t.Errorf("%d: Got %v, want %d", i, *r, c.expected)
}

var d driver.ValueConverter = *r
//var d driver.Valuer = *r
var d driver.Valuer = *r

_, e = d.ConvertValue(c.v)
q, e := d.Value()
if e != nil {
t.Errorf("%d: Got %v for %d", i, e, c.expected)
}
//if q.(int64) != int64(c.expected) {
// t.Errorf("%d: Got %v, want %d", i, q, c.expected)
//}
if q.(int64) != int64(c.expected) {
t.Errorf("%d: Got %v, want %d", i, q, c.expected)
}
}
}

func TestDateStringScan(t *testing.T) {
cases := []struct {
v interface{}
expected string
}{
{int64(0), "1970-01-01"},
{int64(15000), "2011-01-26"},
{"0", "1970-01-01"},
{"15000", "2011-01-26"},
{"2018-12-31", "2018-12-31"},
{"31/12/2018", "2018-12-31"},
//{[]byte("10000"), ""},
//{PeriodOfDays(10000).Date().Local(), ""},
}

DisableTextStorage = prior
for i, c := range cases {
r := new(DateString)
e := r.Scan(c.v)
if e != nil {
t.Errorf("%d: Got %v for %s", i, e, c.expected)
}
if r.Date().String() != c.expected {
t.Errorf("%d: Got %v, want %s", i, r.Date(), c.expected)
}

var d driver.Valuer = *r

q, e := d.Value()
if e != nil {
t.Errorf("%d: Got %v for %s", i, e, c.expected)
}
if q.(string) != c.expected {
t.Errorf("%d: Got %v, want %s", i, q, c.expected)
}
}
}

func TestDateScanWithJunk(t *testing.T) {
cases := []struct {
v interface{}
disallow bool
expected string
}{
{true, false, "bool true is not a meaningful date"},
{true, true, "bool true is not a meaningful date"},
{true, "bool true is not a meaningful date"},
{true, "bool true is not a meaningful date"},
}

prior := DisableTextStorage

for i, c := range cases {
DisableTextStorage = c.disallow
r := new(Date)
e := r.Scan(c.v)
if e.Error() != c.expected {
t.Errorf("%d: Got %q, want %q", i, e.Error(), c.expected)
}
}
}

func TestDateStringScanWithJunk(t *testing.T) {
cases := []struct {
v interface{}
expected string
}{
{true, "bool true is not a meaningful date"},
{true, "bool true is not a meaningful date"},
}

DisableTextStorage = prior
for i, c := range cases {
r := new(DateString)
e := r.Scan(c.v)
if e.Error() != c.expected {
t.Errorf("%d: Got %q, want %q", i, e.Error(), c.expected)
}
}
}

func TestDateScanWithNil(t *testing.T) {
Expand All @@ -88,7 +130,12 @@ func TestDateScanWithNil(t *testing.T) {
if e != nil {
t.Errorf("Got %v", e)
}
if r != nil {
t.Errorf("Got %v", r)
}

func TestDateAsStringScanWithNil(t *testing.T) {
var r *Date
e := r.Scan(nil)
if e != nil {
t.Errorf("Got %v", e)
}
}

0 comments on commit bd8f90e

Please sign in to comment.