Extending Bun with custom types

Bun uses database/sql to work with different DBMS and so you can extend it with custom types using sql.Scanner and driver.Valuer interfaces.

In this tutorial we will write a simple type to work with time that does not have a date:

1. const timeFormat = "15:04:05.999999999"

3. type Time struct {
4. time.Time
5. }

sql.Scanner

sql.ScannerCustom types - 图1open in new window assigns a value from a database driver. The value can be one of the following types:

  • int64
  • float64
  • bool
  • []byte
  • string
  • time.Time
  • nil - for NULL values
1. var _ sql.Scanner = (*Time)(nil)

3. // Scan scans the time parsing it if necessary using timeFormat.
4. func (tm *Time) Scan(src interface{}) (err error) {
5. switch src := src.(type) {
6. case time.Time:
7. *tm = NewTime(src)
8. return nil
9. case string:
10. tm.Time, err = time.ParseInLocation(timeFormat, src, time.UTC)
11. return err
12. case []byte:
13. tm.Time, err = time.ParseInLocation(timeFormat, string(src), time.UTC)
14. return err
15. case nil:
16. tm.Time = time.Time{}
17. return nil
18. default:
19. return fmt.Errorf("unsupported data type: %T", src)
20. }
21. }

You can find the full example at GitHubCustom types - 图2open in new window.

driver.Valuer

driver.ValuerCustom types - 图3open in new window returns a value for a database driver. The value must be one of the following types:

  • int64
  • float64
  • bool
  • []byte
  • string
  • time.Time
  • nil - for NULL values
1. var _ driver.Valuer = (*Time)(nil)

3. // Scan scans the time parsing it if necessary using timeFormat.
4. func (tm Time) Value() (driver.Value, error) {
5. return tm.UTC().Format(timeFormat), nil
6. }

You can find the full example at GitHubCustom types - 图4open in new window.

Conclusion

You can easily extend Bun with custom types to fully utilize your DBMS capabilities, for example, bunbigCustom types - 图5open in new window adds support for big.Int to Bun.

See also: