Skip to content
/ nullable Public

Go database null values for any data type thanks to the Golang generic features.

License

Notifications You must be signed in to change notification settings

OVYA/nullable

Repository files navigation

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.

About

Go database null values for any data type thanks to the Golang generic features.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages