firebirdsql (Go firebird sql driver)
June 7, 2026 · View on GitHub
Firebird RDBMS https://firebirdsql.org SQL driver for Go
Requirements
- Firebird 2.5 or higher
- Golang 1.20 or higher
Example
package main
import (
"fmt"
"database/sql"
_ "github.com/nakagami/firebirdsql"
)
func main() {
var n int
conn, _ := sql.Open("firebirdsql", "user:password@servername/foo/bar.fdb")
defer conn.Close()
conn.QueryRow("SELECT Count(*) FROM rdb$relations").Scan(&n)
fmt.Println("Relations count=", n)
}
See also driver_test.go
package main
import (
"fmt"
"github.com/nakagami/firebirdsql"
)
func main() {
dsn := "user:password@servername/foo/bar.fdb"
events := []string{"my_event", "order_created"}
fbEvent, _ := firebirdsql.NewFBEvent(dsn)
defer fbEvent.Close()
sbr, _ := fbEvent.Subscribe(events, func(event firebirdsql.Event) { //or use SubscribeChan
fmt.Printf("event: %s, count: %d, id: %d, remote id:%d \n", event.Name, event.Count, event.ID, event.RemoteID)
})
defer sbr.Unsubscribe()
go func() {
fbEvent.PostEvent(events[0])
fbEvent.PostEvent(events[1])
}()
<- make(chan struct{}) //wait
}
See also _example
Connection string
user:password@servername[:port_number]/database_name_or_file[?params1=value1[¶m2=value2]...]
Examples:
# Basic connection
user:password@localhost/path/to/database.fdb
# With wire compression enabled (for better performance over slow networks)
user:password@localhost/path/to/database.fdb?wire_compress=true
# With multiple parameters
user:password@localhost/path/to/database.fdb?wire_crypt=true&wire_compress=true&role=admin
Reserved characters
The DSN is parsed as an RFC 3986 URI (the driver prepends firebird:// automatically).
Reserved characters in the password or database path must be percent-encoded,
otherwise they are misinterpreted as URI delimiters:
| Character | Encoded | Why it breaks things |
|---|---|---|
@ | %40 | ends the userinfo section |
: | %3A | splits user from password |
# | %23 | starts the fragment (silently dropped) |
? | %3F | starts the query string |
/ | %2F | path separator |
& | %26 | separates query parameters |
= | %3D | separates a query key from its value |
% | %25 | the escape character itself |
| space | %20 | invalid in a URI |
Use url.QueryEscape to encode a password:
import "net/url"
pass := url.QueryEscape("p@ss:w#rd") // → "p%40ss%3Aw%23rd"
dsn := "sysdba:" + pass + "@localhost/var/lib/firebird/mydb.fdb"
db, err := sql.Open("firebirdsql", dsn)
Drivers
Two driver names are registered with database/sql:
"firebirdsql"- attach to an existing database."firebirdsql_createdb"- create the database if it does not exist, then attach. See_examples/service_manager.gofor a usage example.
General
- user: login user
- password: login password
- servername: Firebird server's host name or IP address.
- port_number: Port number. default value is 3050.
- database_name_or_file: Database path (or alias name).
Optional
param1, param2... are
| Name | Description | Default | Note |
|---|---|---|---|
| auth_plugin_name | Preferred authentication plugin. Must be a member of auth_plugin_list. | Srp256 | Srp256/Srp/Legacy_Auth are available. |
| auth_plugin_list | Ordered, comma-separated allow-list of acceptable authentication plugins (a subset of the supported Srp256,Srp,Legacy_Auth). The plugin the server selects must be a member, otherwise the connection is refused before any credentials are sent. Omit a plugin to refuse it (e.g. Srp256,Srp to refuse a server-forced downgrade to Legacy_Auth, which would otherwise put a brute-forceable DES crypt(password) hash on the wire). | Srp256,Srp,Legacy_Auth | Legacy_Auth is kept in the default for backward compatibility but is weak; set Srp256,Srp to harden. |
| column_name_to_lower | Force column name to lower | false | For "github.com/jmoiron/sqlx" |
| role | Role name | ||
| timezone | IANA time zone name (e.g. UTC, Europe/Berlin) | Controls client-side decoding of naive DATE/TIME/TIMESTAMP and server session time zone (FB 4+). See "Time and timestamp handling" below. | |
| wire_crypt | Wire-encryption policy, mirroring the server's WireCrypt setting. Tri-state disabled/enabled/required, with boolean aliases false=disabled and true=enabled. enabled encrypts when the server offers a cipher but tolerates plaintext; required fails the connection closed on every non-encrypting handshake outcome (including a legacy plain op_accept for protocol versions ≤ 12 and an op_accept_data with no negotiated cipher), refusing before any credentials are sent. Note: enabled tolerates an active-MITM downgrade to plaintext; on untrusted networks use required. | true (=enabled) | For Firebird 3.0+. Against pre-3.0 / non-encrypting servers, required is refused (use enabled to allow plaintext). |
| wire_crypt_plugin | Ordered, comma-separated allow-list of acceptable wire ciphers, mirroring the server's WireCryptPlugin. Order is client preference; omit a cipher to refuse it (e.g. ChaCha64,ChaCha to refuse the deprecated RC4/Arc4). | ChaCha64,ChaCha,Arc4 | For Firebird 3.0+. Arc4 (RC4) is kept for FB 3.0 compatibility but is cryptographically weak. |
| wire_compress | Enable wire protocol compression. | false | For Firebird 3.0+ (protocol version 13+) |
| charset | Firebird Charecter Set |
Time and timestamp handling
Firebird's DATE, TIME, and TIMESTAMP types store wall-clock components without zone information - by design. When the driver decodes such a column into a Go time.Time, it must attach some *time.Location. Resolution order:
- If
?timezone=<IANA>is set on the DSN, that location is used. - Otherwise
time.Localis used (matching Firebird's Java driver Jaybird, which uses the JVM default).
For cross-host determinism (e.g. app servers in different time zones all reading the same rows), set ?timezone=UTC on the DSN:
user:password@host/path/to/db?timezone=UTC
The same parameter also tells a Firebird 4+ server to evaluate CURRENT_TIMESTAMP and LOCALTIMESTAMP in that zone.
For applications that need explicit zone semantics at the schema level, use the Firebird 4+ types TIME WITH TIME ZONE and TIMESTAMP WITH TIME ZONE. Those columns carry their zone on the wire and are unaffected by the ?timezone= fallback.
Round-trip of naive time.Time values is wall-clock-preserving, not absolute-instant-preserving: inserting a value in zone A and reading it back on a host in zone B will return identical wall-clock components (year, month, day, hour, minute, second, nanosecond), but a.Equal(b) may be false. This matches Firebird's underlying storage model.
Transactions: READ COMMITTED + NOWAIT
Firebird supports NOWAIT transactions (lock conflicts return immediately instead of waiting).
The standard database/sql sql.TxOptions doesn't have a knob for NOWAIT, so this driver exposes a driver-specific isolation level constant:
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: firebirdsql.LevelReadCommittedNoWait,
})
This maps to a transaction TPB containing READ COMMITTED, RECORD VERSION, and NOWAIT.