Skip to content

Commit

Permalink
fix(time): added nullable Time with better json support (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
cnlangzi authored Apr 26, 2024
1 parent 1ade208 commit a6accdf
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 11 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.7]
### Added
- added `Connector` interface (#43)
- added nullable `Time` with better json support (#44)

### Changed
- renamed `BitBool` with shorter name `Bool` (#44)

## [1.4.6] - 2014-04-23
### Changed
Expand Down
8 changes: 4 additions & 4 deletions bitbool.go → bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"errors"
)

// BitBool is an implementation of a bool for the MySQL type BIT(1).
type BitBool bool
// Bool is an implementation of a bool for the MySQL type BIT(1).
type Bool bool

// Value implements the driver.Valuer interface,
// and turns the BitBool into a bit field (BIT(1)) for MySQL storage.
func (b BitBool) Value() (driver.Value, error) { // skipcq: GO-W1029
func (b Bool) Value() (driver.Value, error) { // skipcq: GO-W1029
if b {
return []byte{1}, nil
} else {
Expand All @@ -20,7 +20,7 @@ func (b BitBool) Value() (driver.Value, error) { // skipcq: GO-W1029

// Scan implements the sql.Scanner interface,
// and turns the bit field incoming from MySQL into a BitBool
func (b *BitBool) Scan(src interface{}) error { // skipcq: GO-W1029
func (b *Bool) Scan(src interface{}) error { // skipcq: GO-W1029
if src == nil {
return nil
}
Expand Down
14 changes: 7 additions & 7 deletions bitbool_test.go → bool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ import (
"github.com/stretchr/testify/require"
)

func TestBitBool(t *testing.T) {
func TestBool(t *testing.T) {
d, err := sql.Open("sqlite3", "file::memory:")
require.NoError(t, err)

_, err = d.Exec("CREATE TABLE `users` (`id` id NOT NULL,`status` BIT(1), PRIMARY KEY (`id`))")
require.NoError(t, err)

result, err := d.Exec("INSERT INTO `users`(`id`, `status`) VALUES(?, ?)", 10, BitBool(true))
result, err := d.Exec("INSERT INTO `users`(`id`, `status`) VALUES(?, ?)", 10, Bool(true))
require.NoError(t, err)

rows, err := result.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rows)

result, err = d.Exec("INSERT INTO `users`(`id`, `status`) VALUES(?, ?)", 11, BitBool(false))
result, err = d.Exec("INSERT INTO `users`(`id`, `status`) VALUES(?, ?)", 11, Bool(false))
require.NoError(t, err)

rows, err = result.RowsAffected()
Expand All @@ -42,25 +42,25 @@ func TestBitBool(t *testing.T) {
require.NoError(t, err)
require.Equal(t, int64(1), rows)

var b1 BitBool
var b1 Bool
err = d.QueryRow("SELECT `status` FROM `users` WHERE id=?", 10).Scan(&b1)
require.NoError(t, err)

require.EqualValues(t, true, b1)

var b2 BitBool
var b2 Bool
err = d.QueryRow("SELECT `status` FROM `users` WHERE id=?", 11).Scan(&b2)
require.NoError(t, err)

require.EqualValues(t, false, b2)

var b3 BitBool
var b3 Bool
err = d.QueryRow("SELECT `status` FROM `users` WHERE id=?", 12).Scan(&b3)
require.NoError(t, err)

require.EqualValues(t, true, b3)

var b4 BitBool
var b4 Bool
err = d.QueryRow("SELECT `status` FROM `users` WHERE id=?", 13).Scan(&b4)
require.NoError(t, err)

Expand Down
65 changes: 65 additions & 0 deletions time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package sqle

import (
"database/sql"
"database/sql/driver"
"encoding/json"
"time"
)

var nullTimeJsonBytes = []byte("null")

const nullTimeJson = "null"

// Time represents a nullable time value.
type Time struct {
sql.NullTime
}

// NewTime creates a new Time object with the given time and valid flag.
func NewTime(t time.Time, valid bool) Time {
return Time{NullTime: sql.NullTime{Time: t, Valid: valid}}
}

// Scan implements the [sql.Scanner] interface.
func (t *Time) Scan(value any) error { // skipcq: GO-W1029
return t.NullTime.Scan(value)
}

// Value implements the [driver.Valuer] interface.
func (t Time) Value() (driver.Value, error) { // skipcq: GO-W1029
return t.NullTime.Value()
}

// Time returns the underlying time.Time value of the Time struct.
func (t *Time) Time() time.Time { // skipcq: GO-W1029
return t.NullTime.Time
}

// MarshalJSON implements the json.Marshaler interface
func (t Time) MarshalJSON() ([]byte, error) { // skipcq: GO-W1029
if t.Valid {
return json.Marshal(t.NullTime.Time)
}
return nullTimeJsonBytes, nil
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (t *Time) UnmarshalJSON(data []byte) error { // skipcq: GO-W1029
if len(data) == 0 || string(data) == nullTimeJson {
t.NullTime.Time = time.Time{}
t.NullTime.Valid = false
return nil
}

var v time.Time
err := json.Unmarshal(data, &v)
if err != nil {
return err
}

t.NullTime.Time = v
t.NullTime.Valid = true

return nil
}
113 changes: 113 additions & 0 deletions time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package sqle

import (
"database/sql"
"encoding/json"
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestTimeInSQL(t *testing.T) {

now := time.Now()
d, err := sql.Open("sqlite3", "file::memory:")
require.NoError(t, err)

_, err = d.Exec("CREATE TABLE `times` (`id` id NOT NULL,`created_at` datetime, PRIMARY KEY (`id`))")
require.NoError(t, err)

result, err := d.Exec("INSERT INTO `times`(`id`) VALUES(?)", 10)
require.NoError(t, err)

rows, err := result.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rows)

result, err = d.Exec("INSERT INTO `times`(`id`, `created_at`) VALUES(?, ?)", 20, now)
require.NoError(t, err)

rows, err = result.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rows)

var t10 Time
err = d.QueryRow("SELECT `created_at` FROM `times` WHERE id=?", 10).Scan(&t10)
require.NoError(t, err)

require.EqualValues(t, false, t10.Valid)

var t20 Time
err = d.QueryRow("SELECT `created_at` FROM `times` WHERE id=?", 20).Scan(&t20)
require.NoError(t, err)

require.EqualValues(t, true, t20.Valid)
require.EqualValues(t, now.UTC(), t20.Time().UTC())

result, err = d.Exec("INSERT INTO `times`(`id`,`created_at`) VALUES(?, ?)", 11, t10)
require.NoError(t, err)

rows, err = result.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rows)

result, err = d.Exec("INSERT INTO `times`(`id`, `created_at`) VALUES(?, ?)", 21, t20)
require.NoError(t, err)

rows, err = result.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rows)

var t11 Time
err = d.QueryRow("SELECT `created_at` FROM `times` WHERE id=?", 11).Scan(&t11)
require.NoError(t, err)

require.EqualValues(t, false, t11.Valid)

var t21 Time
err = d.QueryRow("SELECT `created_at` FROM `times` WHERE id=?", 21).Scan(&t21)
require.NoError(t, err)

require.EqualValues(t, true, t21.Valid)
require.EqualValues(t, now.UTC(), t21.Time().UTC())

}

func TestTimeInJSON(t *testing.T) {

sysTime := time.Now()

bufSysTime, err := json.Marshal(sysTime)
require.NoError(t, err)

sqleTime := NewTime(sysTime, true)

bufSqleTime, err := json.Marshal(sqleTime)
require.NoError(t, err)

require.Equal(t, bufSysTime, bufSqleTime)

var jsSqleTime Time
// Unmarshal sqle.Time from time.Time json bytes
err = json.Unmarshal(bufSysTime, &jsSqleTime)
require.NoError(t, err)

require.True(t, sysTime.Equal(jsSqleTime.Time()))
require.Equal(t, true, jsSqleTime.Valid)

var jsSysTime time.Time
// Unmarshal time.Time from sqle.Time json bytes
err = json.Unmarshal(bufSqleTime, &jsSysTime)
require.NoError(t, err)
require.True(t, sysTime.Equal(jsSysTime))

var nullTime Time
err = json.Unmarshal([]byte("null"), &nullTime)
require.NoError(t, err)
require.Equal(t, false, nullTime.Valid)

bufNull, err := json.Marshal(nullTime)
require.NoError(t, err)
require.Equal(t, []byte("null"), bufNull)
}

0 comments on commit a6accdf

Please sign in to comment.