nullable

package module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2024 License: GPL-3.0 Imports: 7 Imported by: 0

README

Go Nullable

golangci-lint mod-verify gosec staticcheck test

  • Provide Go database null value for any data type as JSON thanks to the Golang generic features.
  • Support data type github.com/google/uuid.UUID.
  • Make possible to scan and store any structs' type into json and jsonb Postgresql data type.
  • Support JSON marshaling and unMarshaling with conventional Javascript/Typescript value null instead of Valid:true/false, Type:value as database/sql does.

Usages

From the Go test
type Test[T any] struct {
	ID     int64                   `json:"id"`
	Name   *nullable.Of[string]    `json:"name"`
	DateTo *nullable.Of[time.Time] `json:"dateTo"`
	Data   *nullable.Of[T]         `json:"data"`
}

type testedType = struct {
	String string                      `json:"string"`
	Bool   *nullable.Of[bool]          `json:"bool"`
	Int    int64                       `json:"int"`
	JSON   *nullable.Of[nullable.JSON] `json:"json"`
}

data1 := testedType{
	String: "a string",
	Bool:   nullable.FromValue(true),
	Int:    768,
}

data1.JSON = nullable.FromValue[nullable.JSON](data1)

obj1 := Test[testedType]{
	Name:   nullable.FromValue("PLOP"),
	DateTo: nullable.FromValue(time.Now()),
	Data:   nullable.FromValue(data1),
}

obj2 := Test[testedType]{
	Name:   nullable.Null[string](),     // Null value
	DateTo: nullable.Null[time.Time](),  // Null value
	Data:   nullable.Null[testedType](), // Null value
}
Database Insertion

Comes from a test on Postgresql database with Time and JSON insertion :

// The model of the db table test
type Test[T nullable.JSON] struct {
	ID int64 `json:"id"`
	// This string can be null
	Name *nullable.Of[string] `json:"name"`
	// This timestamp can be null
	DateTo *nullable.Of[time.Time] `json:"dateTo"`
	// You can any interface you want in data
	Data *nullable.Of[T] `json:"data"`
}

type dataType = struct {
	String string `json:"string"`
	Bool   bool   `json:"bool"`
	Int    int64  `json:"int"`
}

data := dataType{
	String: "This is a string",
	Bool:   true,
	Int:    768,
}

obj := Test[dataType]{
	Name:      nullable.FromValue("My name"),
	DateTo:    nullable.FromValue(time.Now()),
	Data:      nullable.FromValue(data),
}

_, err = daoService.NamedExec("INSERT INTO test (name, date_to, data) VALUES (:name, :date_to, :data)", obj)

if err != nil…
Custom primitive data type

The library Nullable persist and read the structured types as JSON in/from the database but it is possible to workaround this feature.

Suppose you need the custom type type PhoneNumber string. If you use this type as is :

type Coordinate struct {
	Email      *nullable.Of[string]            `json:"email,omitempty"`
	Phone      *nullable.Of[phonenum.PhoneNum] `json:"phone"`
}

persisting a Coordinate in database will persist the field Phone in JSON, what we don't want. In order to persist (resp. read) the type Phone as a string, you must implement the native Go database driver.Valuer (reps. sql.Scanner) :

// Value implements the driver.Valuer interface.
func (pn *PhoneNumb) Value() (driver.Value, error) {
	return driver.Value(string(*pn)), nil
}

// Scan implements the sql.Scanner interface.
func (pn *PhoneNum) Scan(v any) error {
	switch val := v.(type) {
	case int, int64, uint64:
		*pn = PhoneNum(strconv.Itoa(val.(int)))
	case string:
		*pn = PhoneNum(val)
	default:
		return errors.New(fmt.Sprintf("can not scan phone number from type %T", val))
	}

	return nil
}

This Scanner can even read an integer from the database to fulfill the phone number as a string.

Notes

Similar Project

This project is inspired from gonull that fails scanning/storing some Postgresql type like enum, timestamp and json/jsonb.

Go Tests for Postgresql

Go tests storing and scanning data from/to Postgresql database are in progress and will be available soon in an other git repository.

Documentation

Overview

package nullable provides Go nullable data type. This package handles database values as sql.Nullxxx does but with generic features. This package make possible to scan and store any structs' type to json and jsonb Postgresql data type thanks to github.com/jmoiron/sqlx for example.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type JSON

type JSON = any

JSON permits to handle Postgresl Json[b] type

type NullableI

type NullableI[T bool | int | int16 | int32 | int64 | string | uuid.UUID | float64 | JSON] interface {
	// IsNull returns true if itself is nil or the value is nil/null
	IsNull() bool
	// GetValue implements the getter.
	GetValue() *T
	// SetValue implements the setter.
	SetValue(T)
	// SetValueP implements the setter by pointer.
	SetValueP(*T)
	// SetNull set to null.
	SetNull()
	// MarshalJSON implements the encoding json interface.
	MarshalJSON() ([]byte, error)
	// UnmarshalJSON implements the decoding json interface.
	UnmarshalJSON([]byte) error
	// Value implements the driver.Valuer interface.
	Value() (driver.Value, error)
	// Scan implements the sql.Scanner interface.
	Scan(v any) error
}

type Of

type Of[T bool | int | int16 | int32 | int64 | string | uuid.UUID | float64 | JSON] struct {
	//nolint: tagliatelle // Internal use
	Val *T `json:"nullable_value" db:"_"`
}

func FromValue

func FromValue[T bool | int | int16 | int32 | int64 | string | uuid.UUID | float64 | JSON](b T) *Of[T]

FromValue is a Nullable constructor from the given value thanks to Go generics' inference.

func Null

func Null[T bool | int | int16 | int32 | int64 | string | uuid.UUID | float64 | JSON]() *Of[T]

Null is a Nullable constructor with Null value.

func (*Of[T]) GetValue

func (n *Of[T]) GetValue() *T

GetValue implements the getter.

func (*Of[T]) IsNull added in v0.1.1

func (n *Of[T]) IsNull() bool

IsNull returns true iff the value is nil

func (*Of[T]) MarshalJSON

func (n *Of[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding json interface.

func (*Of[T]) Scan

func (n *Of[T]) Scan(v any) error

Scan implements the sql.Scanner interface. This method decodes a JSON-encoded value into the struct.

func (*Of[T]) SetNull

func (n *Of[T]) SetNull()

SetNull set to null.

func (*Of[T]) SetValue

func (n *Of[T]) SetValue(b T)

SetValue implements the setter.

func (*Of[T]) SetValueP

func (n *Of[T]) SetValueP(ref *T)

SetValueP implements the setter by pointer. If ref is not nil, calls SetValue(*ref) If ref is nil, calls SetNull()

func (*Of[T]) UnmarshalJSON

func (n *Of[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the decoding json interface.

func (*Of[T]) Value

func (n *Of[T]) Value() (driver.Value, error)

Value implements the driver.Valuer interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL