Profile
Back to NewsBack
GitHub Trending 7 min
Reader Mode
nakagami/firebirdsql: Firebird RDBMS sql driver for Go (golang)

nakagami/firebirdsql: Firebird RDBMS sql driver for Go (golang)

12 hours ago

firebirdsql (Go firebird sql driver)

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[&param2=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.go for 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 | | | | max_inline_blob_size | Max size of BLOBs the server may send inline with fetch/execute2 responses (protocol 19 / Firebird 5.0.3+). 0 disables. Alias: inline_blob_size. | 65536 | Negotiated only when the server picks protocol ≥ 19 | | max_blob_cache_size | Client cache budget for inline BLOBs (bytes). Alias: blob_cache_size. | 10485760 | Also sent as DPB on attach when protocol ≥ 19 |

Wire protocols 16–19

The driver advertises Firebird wire protocols 10–19. The server chooses the highest mutually supported version.

  • Protocol 16+ (Firebird 4.0+): batch DML via PrepareBatch on the driver connection (sql.Conn.Raw). After op_batch_msg, protocol 17+ uses op_batch_sync; create/release still use op_ping. BLOB/array bind parameters are rejected. In autocommit mode, a successful Exec calls commit-retaining; any per-row error rolls back.
  • Protocol 18 (Firebird 5.0+): scrollable cursors. Use QueryScrollable on the driver connection via sql.Conn.Raw (not database/sql Rows, which is forward-only). Orientations: ScrollNext, ScrollPrior, ScrollFirst, ScrollLast, ScrollAbsolute, ScrollRelative.
  • Protocol 19 (Firebird 5.0.3+): inline BLOBs — small BLOB payloads arrive with fetch/sql responses and are served from a client cache on first read.
ExecImmediate runs SQL with op_execute_immediate (no OpExecute trailers). Reach it the same way through sql.Conn.Raw.

Inspect the negotiated version with ProtocolVersion() (same Raw pattern as WireCipher).

Local live tests

Protocol 18/19 integration tests use a read-only EMPLOYEE database:

localhost:3055
E:\Projects_2026\firebirdsql\EMPLOYEE.FDB

They skip automatically when that instance is unreachable.

Writable batch / ExecImmediate tests use GetTestDSN + firebirdsql_createdb on localhost:3050 (override with FIREBIRD_PORT) and also dial-skip when unreachable. Do not run DML against EMPLOYEE.

Optional 100k batch-vs-exec compare:

FIREBIRDSQL_BENCH=1 go test -run TestCompareBatchVsExecInsert100k -timeout 15m

Large batches may need BatchOptions.BufferBytes above the server default so all flushed rows fit until Exec.

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:

  1. If ?timezone= is set on the DSN, that location is used.
  2. Otherwise time.Local is 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.

GORM for Firebird

See https://github.com/flylink888/gorm-firebird

Chat with me