using a lib for sql null types (#117)

This commit is contained in:
Marko Gaćeša 2022-12-08 16:30:49 +01:00 committed by GitHub
parent 15163f3daa
commit a0646e9935
7 changed files with 9 additions and 356 deletions

1
go.mod
View File

@ -18,6 +18,7 @@ require (
github.com/google/wire v0.5.0
github.com/gotidy/ptr v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/guregu/null v4.0.0+incompatible
github.com/jmoiron/sqlx v1.3.3
github.com/joho/godotenv v1.3.0
github.com/kelseyhightower/envconfig v1.4.0

2
go.sum
View File

@ -218,6 +218,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/guregu/null v4.0.0+incompatible h1:4zw0ckM7ECd6FNNddc3Fu4aty9nTlpkkzH7dPn4/4Gw=
github.com/guregu/null v4.0.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=

View File

@ -1,118 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package null
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
"strconv"
)
// Bool represents a bool that may be null.
type Bool struct {
sql.NullBool
}
func NewBool(b bool) Bool {
return Bool{
sql.NullBool{
Bool: b,
Valid: true,
},
}
}
// FromBool returns a null Bool if the parameter is false or a true Bool.
func FromBool(b bool) Bool {
if !b {
return Bool{}
}
return NewBool(b)
}
// FromPtrBool returns a null Bool if the parameter is nil, a valid Bool otherwise.
func FromPtrBool(b bool) Bool {
if !b {
return Bool{}
}
return NewBool(b)
}
// ToBool converts null.Bool to a bool.
func (b *Bool) ToBool() bool {
if !b.Valid {
return false
}
return b.Bool
}
// ToPtrBool converts null.Bool to a *bool.
func (b *Bool) ToPtrBool() *bool {
if !b.Valid {
return nil
}
return &b.Bool
}
// UnmarshalJSON implements json.Unmarshaler.
func (b *Bool) UnmarshalJSON(input []byte) error {
var i interface{}
if err := json.Unmarshal(input, &i); err != nil {
return err
}
switch val := i.(type) {
case bool:
b.Bool = val
b.Valid = true
default:
b.Bool = false
b.Valid = false
}
return nil
}
// MarshalJSON implements json.Marshaler.
func (b *Bool) MarshalJSON() ([]byte, error) {
if !b.Valid {
return []byte(null), nil
}
return json.Marshal(b.Bool)
}
// Scan implements sql.Scanner interface
func (b *Bool) Scan(input interface{}) error {
switch val := input.(type) {
case nil:
b.Bool, b.Valid = false, false
case bool:
b.Bool, b.Valid = val, true
case int64:
b.Bool, b.Valid = val != 0, true
case float64:
b.Bool, b.Valid = val != 0.0, true
case []byte:
bb, err := strconv.ParseBool(string(val))
b.Bool, b.Valid = bb, err == nil
case string:
bb, err := strconv.ParseBool(val)
b.Bool, b.Valid = bb, err == nil
default:
return fmt.Errorf("failed to convert %v (%T) to null.Bool", input, input)
}
return nil
}
// Value implements driver.Valuer interface
func (b *Bool) Value() (driver.Value, error) {
if !b.Valid {
return nil, nil
}
return b.Bool, nil
}

View File

@ -1,116 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package null
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
"strconv"
)
// Int64 represents an int64 that may be null.
type Int64 struct {
sql.NullInt64
}
func NewInt64(i int64) Int64 {
return Int64{
sql.NullInt64{
Int64: i,
Valid: true,
},
}
}
// FromInt64 returns a null Int64 if the parameter is zero, a valid Int64 otherwise.
func FromInt64(i int64) Int64 {
if i == 0 {
return Int64{}
}
return NewInt64(i)
}
// FromPtrInt64 returns a null Int64 if the parameter is nil, a valid Int64 otherwise.
func FromPtrInt64(i *int64) Int64 {
if i == nil {
return Int64{}
}
return NewInt64(*i)
}
// ToInt64 converts the null.Int64 to an int64.
func (i *Int64) ToInt64() int64 {
if !i.Valid {
return 0
}
return i.Int64
}
// ToPtrInt64 converts the null.Int64 to an *int64.
func (i *Int64) ToPtrInt64() *int64 {
if !i.Valid {
return nil
}
return &i.Int64
}
// UnmarshalJSON implements json.Unmarshaler.
func (i *Int64) UnmarshalJSON(input []byte) error {
var value interface{}
if err := json.Unmarshal(input, &value); err != nil {
return err
}
switch z := value.(type) {
case float64: // in JSON a number is float64
i.Int64 = int64(z)
i.Valid = true
default:
i.Int64 = 0
i.Valid = false
}
return nil
}
// MarshalJSON implements json.Marshaler.
func (i *Int64) MarshalJSON() ([]byte, error) {
if !i.Valid {
return []byte(null), nil
}
return json.Marshal(i.Int64)
}
// Scan implements sql.Scanner interface
func (i *Int64) Scan(input interface{}) error {
switch val := input.(type) {
case nil:
i.Int64, i.Valid = 0, false
case int64:
i.Int64, i.Valid = val, true
case float64:
i.Int64, i.Valid = int64(val), true
case []byte:
ii, err := strconv.ParseInt(string(val), 10, 64)
i.Int64, i.Valid = ii, err == nil
case string:
ii, err := strconv.ParseInt(val, 10, 64)
i.Int64, i.Valid = ii, err == nil
default:
return fmt.Errorf("failed to convert %v (%T) to null.Int64", input, input)
}
return nil
}
// Value implements driver.Valuer interface
func (i *Int64) Value() (driver.Value, error) {
if !i.Valid {
return nil, nil
}
return i.Int64, nil
}

View File

@ -1,7 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package null
const null = "null"

View File

@ -1,109 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package null
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
)
// String represents a string that may be null.
type String struct {
sql.NullString
}
func NewString(s string) String {
return String{
sql.NullString{
String: s,
Valid: true,
},
}
}
// FromNullableString returns a null String if the parameter is empty, a valid String otherwise.
func FromNullableString(s string) String {
if s == "" {
return String{}
}
return NewString(s)
}
// FromPtrString returns a null String if the parameter is nil, a valid String otherwise.
func FromPtrString(s *string) String {
if s == nil {
return String{}
}
return NewString(*s)
}
// ToString converts the null.String to a string.
func (s *String) ToString() string {
if !s.Valid {
return ""
}
return s.String
}
// ToPtrString converts the null.String to a *string.
func (s *String) ToPtrString() *string {
if !s.Valid {
return nil
}
return &s.String
}
// UnmarshalJSON implements json.Unmarshaler.
func (s *String) UnmarshalJSON(input []byte) error {
var val interface{}
if err := json.Unmarshal(input, &val); err != nil {
return err
}
switch z := val.(type) {
case string:
s.String = z
s.Valid = true
default:
s.String = ""
s.Valid = false
}
return nil
}
// MarshalJSON implements json.Marshaler.
func (s *String) MarshalJSON() ([]byte, error) {
if !s.Valid {
return []byte(null), nil
}
return json.Marshal(s.String)
}
// Scan implements sql.Scanner interface
func (s *String) Scan(input interface{}) error {
switch val := input.(type) {
case nil:
s.String, s.Valid = "", false
case []byte:
s.String, s.Valid = string(val), true
case string:
s.String, s.Valid = val, true
default:
return fmt.Errorf("failed to convert %v (%T) to null.String", input, input)
}
return nil
}
// Value implements driver.Valuer interface
func (s *String) Value() (driver.Value, error) {
if !s.Valid {
return nil, nil
}
return s.String, nil
}

View File

@ -9,11 +9,11 @@ import (
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/internal/store/database/dbtx"
"github.com/harness/gitness/internal/store/database/null"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/Masterminds/squirrel"
"github.com/guregu/null"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
@ -50,8 +50,8 @@ type pullReq struct {
TargetRepoID int64 `db:"pullreq_target_repo_id"`
TargetBranch string `db:"pullreq_target_branch"`
MergedBy null.Int64 `db:"pullreq_merged_by"`
Merged null.Int64 `db:"pullreq_merged"`
MergedBy null.Int `db:"pullreq_merged_by"`
Merged null.Int `db:"pullreq_merged"`
MergeStrategy null.String `db:"pullreq_merge_strategy"`
AuthorUID string `db:"author_uid"`
@ -333,9 +333,9 @@ func mapPullReq(pr *pullReq) *types.PullReq {
SourceBranch: pr.SourceBranch,
TargetRepoID: pr.TargetRepoID,
TargetBranch: pr.TargetBranch,
MergedBy: pr.MergedBy.ToPtrInt64(),
Merged: pr.Merged.ToPtrInt64(),
MergeStrategy: pr.MergeStrategy.ToPtrString(),
MergedBy: pr.MergedBy.Ptr(),
Merged: pr.Merged.Ptr(),
MergeStrategy: pr.MergeStrategy.Ptr(),
Author: types.PrincipalInfo{},
Merger: nil,
}