I use https://github.com/lib/pq for getting data from postgres. For extracting data I use third-party struct, which has field with protobuf Timestamp https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb#Timestamp So So the issue is make work Scan from time.Time to timestamppb.Timestamp
type Stuff struct { //this struct non-local, this is from third-party package
Date *timestamppb.Timestamp
}
stuff = Stuff{}
scanErr := rows.Scan(&stuff.Date)
I tried to Scan to struct witch implements sql.Scanner interface. That was easy. I just implement Scan function like this:
type Test struct {
}
func (t Test) Scan(src any) error {
//convert value
}
but it doesn't work with timestamppb.Timestamp because it is non-local type. So then I tried to define local type and do the same trick
type TimestampPb timestamppb.Timestamp
func (t TimestampPb) Scan(src any) error {
//convert value
}
but this trick didn't work. Moreover I have warning "'Scan' passes a lock by the value: type 'TimestampPb' contains 'protoimpl.MessageState' contains 'sync.Mutex' which is 'sync.Locker'" This either doesn't work if I specify pointer for TimestampPb
func (t *TimestampPb) Scan(src any) error {
So I wonder, how can I make timestamppb.Timestamp instance of sql.Scanner. Is it possible?
Update 1 Little example that I tried
type TimestampPb timestamppb.Timestamp
type test struct { //third-party struct, I can't modify it
date *timestamppb.Timestamp
}
func (s *Storage) test() {
rows, _ := s.db.Query("SELECT \"test_date\" FROM \"test_table\" LIMIT 1")
defer func() {
if rows != nil {
_ = rows.Close()
}
}()
for rows.Next() {
//var value *TimestampPb //works
//var value TimestampPb //works
//var value *timestamppb.Timestamp //doesn't work
//testStruct := test{} //doesn't work
//err := rows.Scan(&value) //scan to variable
//err := rows.Scan(&testStruct.date) //scan to field in the struct
if err != nil {
log.Fatalln(err)
}
}
}
func (tpb *TimestampPb) Scan(src any) error {
return nil
}
I wonder if I can use case with the testStruct?
The pq
library doesn't support *timestamppb.Timestamp
as a timestamp type. See the supported date types in the documentation.
A different type will need to be for scanning.
Often I do that by using an auxiliary type in the function. For example:
type row struct {
Date *timestamppb.Timestamp
// ... more fields here
}
func Query() ([]row, error) {
rows, err := s.db.Query(...)
if err != nil {
return nil, err
}
defer rows.Close()
var rows []row
for rows.Next() {
type aux struct {
Date time.Time
// ... more fields here
}
var value aux
if err := rows.Scan(&value); err != nil {
return nil, err
}
rows = append(rows, row{
Date: timestamppb.New(value.Date),
// ... more fields here
}
}
return rows, nil
}