Closed
Description
Hey, I'm trying to write some aggregator functions that work on any data type. Here's a minimal example:
package main
import (
"database/sql"
"fmt"
"log"
sqlite "github.com/mattn/go-sqlite3"
)
type mode struct {
counts map[any]int
top any
topCount int
}
func newMode() *mode {
return &mode{
counts: map[any]int{},
}
}
func (m *mode) Step(x any) {
m.counts[x]++
c := m.counts[x]
if c > m.topCount {
m.top = x
m.topCount = c
}
}
func (m *mode) Done() any {
return m.top
}
func main() {
sql.Register("sqlite3_custom", &sqlite.SQLiteDriver{
ConnectHook: func(conn *sqlite.SQLiteConn) error {
if err := conn.RegisterAggregator("mode", newMode, true); err != nil {
return err
}
return nil
},
})
db, err := sql.Open("sqlite3_custom", ":memory:")
if err != nil {
log.Fatal("Failed to open database:", err)
}
defer db.Close()
_, err = db.Exec("create table foo (department integer, profits integer)")
if err != nil {
log.Fatal("Failed to create table:", err)
}
_, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115), (2, 20)")
if err != nil {
log.Fatal("Failed to insert records:", err)
}
rows, err := db.Query("select mode(profits) from foo")
if err != nil {
log.Fatal("MODE query error:", err)
}
defer rows.Close()
for rows.Next() {
//var dept int64
var dev string
if err := rows.Scan( &dev); err != nil {
log.Fatal(err)
}
fmt.Printf("mode=%s\n", dev)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
}
When I run this though I get a panic:
panic: reflect: Elem of invalid type interface {}
goroutine 1 [running]:
reflect.(*rtype).Elem(0x64b2a0?)
/usr/local/go/src/reflect/type.go:965 +0x134
github.com/mattn/go-sqlite3.callbackRet({0x64bd60, 0x5fdfe0})
/home/phil/tmp/sqlite3-test/vendor/github.com/mattn/go-sqlite3/callback.go:366 +0x17c
github.com/mattn/go-sqlite3.(*SQLiteConn).RegisterAggregator(0xc000076180, {0x61a96a, 0x4}, {0x5f6fa0, 0x625d60}, 0x1)
/home/phil/tmp/sqlite3-test/vendor/github.com/mattn/go-sqlite3/sqlite3.go:776 +0xbaf
main.main.func1(0xc0000161a0?)
/home/phil/tmp/sqlite3-test/main2.go:40 +0x39
github.com/mattn/go-sqlite3.(*SQLiteDriver).Open(0xc00006a040, {0x61b08b, 0x8})
/home/phil/tmp/sqlite3-test/vendor/github.com/mattn/go-sqlite3/sqlite3.go:1765 +0x3c79
database/sql.dsnConnector.Connect(...)
/usr/local/go/src/database/sql/sql.go:761
database/sql.(*DB).conn(0xc00010ea90, {0x64b7a0, 0xc00001e0e0}, 0x1)
/usr/local/go/src/database/sql/sql.go:1395 +0x782
database/sql.(*DB).exec(0x4d770e?, {0x64b7a0, 0xc00001e0e0}, {0x6244c3, 0x36}, {0x0, 0x0, 0x0}, 0x40?)
/usr/local/go/src/database/sql/sql.go:1657 +0x5d
database/sql.(*DB).ExecContext(0x61c4c1?, {0x64b7a0, 0xc00001e0e0}, {0x6244c3, 0x36}, {0x0, 0x0, 0x0})
/usr/local/go/src/database/sql/sql.go:1635 +0xe5
database/sql.(*DB).Exec(...)
/usr/local/go/src/database/sql/sql.go:1653
main.main()
/home/phil/tmp/sqlite3-test/main2.go:53 +0x172
I changed Done()
to return a string here and did:
func (m *mode) Done() string {
return fmt.Sprintf("%v", m.top)
}
And that works. But I don't want to force every result to a string here.
Are you open to patching the callbackRet
function to support returning any
from Done()
? Or is that just not possible?
Metadata
Metadata
Assignees
Labels
No labels