nakagami / firebirdsql

Firebird RDBMS sql driver for Go (golang)
MIT License
224 stars 60 forks source link

Panic selecting TIME(STAMP) WITH TIME ZONE Firebird 4.0 #128

Closed xhit closed 2 years ago

xhit commented 3 years ago

When I make a SELECT with a column TIME WITH TIME ZONE or TIMESTAMP WITH TIME ZONE I get this panic:

panic: runtime error: index out of range [3] with length 2

goroutine 1 [running]:
encoding/binary.bigEndian.Uint32(...)
        /home/santiago/sdk/go1.16.5/src/encoding/binary/binary.go:112
github.com/nakagami/firebirdsql.bytes_to_bint32(...)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/utils.go:85
github.com/nakagami/firebirdsql.(*xSQLVAR)._parseTimezone(0xc0000bdbc8, 0xc00020627c, 0x2, 0x4, 0x0)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/xsqlvar.go:221 +0x74
github.com/nakagami/firebirdsql.(*xSQLVAR).parseTimeTz(0xc0000bdbc8, 0xc000206278, 0x6, 0x8, 0x8, 0x8, 0x0)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/xsqlvar.go:290 +0xe7
github.com/nakagami/firebirdsql.(*xSQLVAR).value(0xc0000bdbc8, 0xc000206278, 0x6, 0x8, 0x0, 0x0, 0x5a16cf, 0x4, 0xc0000ef000, 0x20, ...)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/xsqlvar.go:475 +0xdbd
github.com/nakagami/firebirdsql.(*wireProtocol).opFetchResponse(0xc0000d6000, 0x100000002, 0xc0001fc0e0, 0x1, 0x1, 0x0, 0x0, 0xc000012850, 0x1)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/wireprotocol.go:1043 +0xa36
github.com/nakagami/firebirdsql.(*firebirdsqlRows).Next(0xc000097500, 0xc000012850, 0x1, 0x1, 0x0, 0xc000028280)
        /home/santiago/goprojects/pkg/mod/github.com/nakagami/firebirdsql@v0.9.2-0.20210625232756-620d823b8097/rows.go:94 +0x505
database/sql.(*Rows).nextLocked(0xc000028280, 0xc000010000)
        /home/santiago/sdk/go1.16.5/src/database/sql/sql.go:2865 +0xbf
database/sql.(*Rows).Next.func1()
        /home/santiago/sdk/go1.16.5/src/database/sql/sql.go:2843 +0x3c
database/sql.withLock(0x5e6fe8, 0xc0000282b0, 0xc0000bddc8)
        /home/santiago/sdk/go1.16.5/src/database/sql/sql.go:3294 +0x69
database/sql.(*Rows).Next(0xc000028280, 0x0)
        /home/santiago/sdk/go1.16.5/src/database/sql/sql.go:2842 +0x87
database/sql.(*Row).Scan(0xc00000e210, 0xc0000bdf08, 0x1, 0x1, 0x0, 0x0)
        /home/santiago/sdk/go1.16.5/src/database/sql/sql.go:3231 +0xf6
main.main()
        /home/santiago/goprojects/src/firebird_types/main.go:46 +0x4fc

Firebird version: 4.0.0 on docker container Go version: 1.16.5 OS: Ubuntu 20.04 x64

Example code:

package main

import (
    "database/sql"
    "fmt"
    "net/url"
    "time"

    _ "github.com/nakagami/firebirdsql"
)

func main() {
    //connecto to Firebird
    db, err := connectFirebird("localhost", "firebird/data/testdb", "usr", "password", 3052)
    if err != nil {
        panic(err)
    }

    defer db.Close()

    tableName := "ISSUE128"

    queryDrop := `drop table "` + tableName + `";`

    db.Exec(queryDrop)

    queryCreate := `CREATE TABLE ` + tableName + ` ("time_column" TIME WITH TIME ZONE);`

    _, err = db.Exec(queryCreate)
    if err != nil {
        panic(err)
    }

    defer db.Exec(queryDrop)

    queryInsert := "insert into " + tableName + " (\"time_column\") values (?)"

    _, err = db.Exec(queryInsert, time.Now())
    if err != nil {
        panic(err)
    }

    var t time.Time

    // this select panic for TIME WITH TIME ZONE and TIMESTAMP WITH TIME ZONE
    if err := db.QueryRow(`SELECT "time_column" FROM ` + tableName).Scan(&t); err != nil {
        panic(err)
    }

    fmt.Println(t)

}

//connectFirebird : Conectar a la base de datos Firebird
func connectFirebird(host, database, username, password string, port int) (*sql.DB, error) {

    // default port
    if port == 0 {
        port = 3050
    }

    //Password in firebird dsn not accept spaces, so escape with url.QueryEscape

    // in form user:pass@host:port/database
    dsn := fmt.Sprintf("%s:%s@%s:%d/%s", username, url.QueryEscape(password), host, port, database)

    db, err := sql.Open("firebirdsql", dsn)

    if err != nil {
        return nil, err
    }

    err = db.Ping()

    if err != nil {
        return nil, err
    }

    return db, nil
}
nakagami commented 3 years ago

Fix and no more panic. But the timezone data in the database is not being retrieved. The time (with timezone) seems to be correct. Still trying to figure out how to make it work correctly.

nakagami commented 2 years ago

pyfirebirdsql's work https://github.com/nakagami/pyfirebirdsql/commit/2986786c68d2c802880de2e3b8a3d3cac6723203