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"
  2. type Time struct {
  3. time.Time
  4. }

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

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)
  2. // Scan scans the time parsing it if necessary using timeFormat.
  3. func (tm Time) Value() (driver.Value, error) {
  4. return tm.UTC().Format(timeFormat), nil
  5. }

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: