Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggregator Done() can't return any/interface{} #1045

Closed
eatonphil opened this issue May 13, 2022 · 2 comments · Fixed by #1046
Closed

Aggregator Done() can't return any/interface{} #1045

eatonphil opened this issue May 13, 2022 · 2 comments · Fixed by #1046

Comments

@eatonphil
Copy link
Contributor

eatonphil commented May 13, 2022

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?

@eatonphil
Copy link
Contributor Author

I proposed a change that fixes the problem for me, in #1046.

@rittneje
Copy link
Collaborator

Resolved in commit 3ccccfb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants