Firebird RDBMS https://firebirdsql.org SQL driver for Go
- Firebird 2.5 or higher
- Golang 1.20 or higher
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
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=adminThe 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)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.
- 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).
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 |
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.
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.