In Go, is there a difference how char and varchar datatypes of database are scanned?

Viewed 376

I have a struct Person which has a field AccessMode of type uint. In the Postgres database, the datatype of AccessMode is char(8). I have written a custom Scan function for AccessMode which implements the Scanner Interface. In the Scan function, it is received as a uint8 slice representation of ASCII string.
When the datatype of AccessMode is changed to varchar(8) in database, it is received as a string in the scan function. Following is the code

type Access uint

type Person struct {
   AccessMode Access
}

func (a *Access) Scan(val interface{}) error {
  if bb, ok := val.([]byte); ok {                                                                                                             
    return a.UnmarshalText(bb)
  }
  return errors.New("scan failed: data is not a byte slice")
}

Why is it received as a string in case of varchar and uint slice in case of char datatype in the scan function?

1 Answers

This is dependent on the driver you're using, to avoid an issue like this you need to consult the driver's documentation. For example the github.com/lib/pq driver provides a section on data types in their documentation.

  • integer types smallint, integer, and bigint are returned as int64
  • floating-point types real and double precision are returned as float64
  • character types char, varchar, and text are returned as string
  • temporal types date, time, timetz, timestamp, and timestamptz are returned as time.Time
  • the boolean type is returned as bool
  • the bytea type is returned as []byte

You'll note in the above that lib/pq claims to return values of the char type as string, which is true, however it may seem like it is not. By that I mean that in postgres there's the char type, and then there is the "char" type, and lib/pq in the above refers to the latter.

If possible you should avoid using char and varchar and instead use text.

When is char a char?

When you declare a column as having type char or char(n), the actual type of the column will be bpchar (blank-padded, fixed-length character type), which is not handled by lib/pq (although it probably should be).

When you declare a column as having type "char", the actual type of the column will be "char" (a single-byte character type intended for internal use), this is handled by lib/pq and returned as string.


To shield yourself in this specific case you can use a type switch.

func (a *Access) Scan(src interface{}) error {
    switch val := src.([]byte) {                                                                                                             
    case []byte:
        return a.UnmarshalText(val)
    case string:
        return a.UnmarshalText([]byte(val))
    }
    return errors.New("scan failed: data is not a byte slice")
}
Related