go-pduckdb is a PureGO driver for DuckDB
A DuckDB module for Go which doesn't require CGO. Uses purego to interface with DuckDB's native library.
Existing DuckDB drivers for Go rely on CGO and compile or link DuckDB into your binary. go-pduckdb is an independent implementation that takes a different approach: no CGO, loading the DuckDB shared library at runtime via purego. This gives you:
CGO_ENABLED=0builds — no C toolchain, simple cross-compilation, works in build environments where CGO is unavailable or unwanted- Fast builds and small binaries — DuckDB is not compiled or linked into your binary
- DuckDB upgrades without recompiling — swap the shared library (e.g.
brew upgrade duckdb) and your existing binary uses it
In short, go-pduckdb moves the DuckDB dependency from build time to run time — your program needs libduckdb present on the machine where it runs (see Installation).
- Pure Go implementation - no CGO required
- Support for most DuckDB data types including DATE, TIME, TIMESTAMP, and DECIMAL
- SQL query execution and result handling
- Database access through standard database/sql interface
- Clear error reporting and propagation
- Cross-platform compatibility
- Parameter binding with automatic type conversion
- Support for prepared statements with parameter type inference
- Transaction support
See docs/COMPATIBILITY.md for the full supported feature matrix (platforms, database/sql interfaces, and data types).
See docs/CONFIGURATION.md for opening a database with DuckDB configuration options.
Disclaimer: go-pduckdb is just glue, not a secure wrapper. Design your own security model when you use this package. A DuckDB DSN carries engine configuration — extension loading, filesystem reach, machine resources — so what may set it is a decision for the integrating application.
go get github.com/fpt/go-pduckdbAlso, make sure to install DuckDB on your platform:
brew install duckdbTypically, /opt/homebrew/lib/libduckdb.dylib is installed.
curl -sSL https://github.com/duckdb/duckdb/releases/download/v1.5.4/libduckdb-linux-amd64.zip -o archive.zip
sudo unzip -j archive.zip libduckdb.so -d /usr/local/lib
sudo ldconfig
rm archive.zipYou can find a download URL in official releases of DuckDB.
Assets starting with libduckdb- contains glibc build of libduckdb.so.
For other Linux, Check official instruction: Building DuckDB.
Download the DuckDB CLI from the official website and place the DLL in your system path.
Note: Windows support relies on an ABI-level workaround for purego's lack of struct-by-value arguments on Windows — see the Windows workaround section in the compatibility docs.
go-pduckdb searches for the DuckDB library in several locations. You can configure the search path using environment variables:
DUCKDB_LIBRARY_PATH- specify the exact path to the DuckDB library fileDYLD_LIBRARY_PATH- on macOS, specify directories to search for the DuckDB libraryLD_LIBRARY_PATH- on Linux, specify directories to search for the DuckDB library
Example usage:
# Specify exact library path
DUCKDB_LIBRARY_PATH=/path/to/libduckdb.dylib ./your_program
# Or specify directory to search (macOS)
DYLD_LIBRARY_PATH=/path/to/lib ./your_program
# Or specify directory to search (Linux)
LD_LIBRARY_PATH=/path/to/lib ./your_programIf no environment variables are set, the library will be searched in standard system locations.
go-pduckdb implements the Go standard database/sql interface, allowing you to work with DuckDB like any other SQL database in Go:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/fpt/go-pduckdb" // Import for driver registration
)
func main() {
// Open a database connection
db, err := sql.Open("duckdb", "example.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Create a table
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name VARCHAR,
email VARCHAR
)`)
if err != nil {
log.Fatal(err)
}
// Insert data
_, err = db.Exec(`INSERT INTO users (id, name, email) VALUES (?, ?, ?)`,
1, "John Doe", "john@example.com")
if err != nil {
log.Fatal(err)
}
// Query data
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Process results
for rows.Next() {
var id int
var name, email string
if err := rows.Scan(&id, &name, &email); err != nil {
log.Fatal(err)
}
fmt.Printf("User %d: %s (%s)\n", id, name, email)
}
}For a more comprehensive example, see the database/sql example.
go-pduckdb features a sophisticated type conversion system that automatically handles type conversions for prepared statement parameters:
// Prepare a statement
stmt, err := conn.Prepare("INSERT INTO users (id, name, created_date) VALUES (?, ?, ?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
// Execute with different parameter types
// The driver will automatically convert these to the appropriate types
err = stmt.Execute(
1, // int -> INTEGER
"John Doe", // string -> VARCHAR
time.Date(2025, 5, 3, 0, 0, 0, 0, time.UTC), // time.Time -> DATE
)Supported conversions include:
- Go bool -> DuckDB BOOLEAN
- Go numeric types -> DuckDB numeric types with range validation
- Go string -> Various DuckDB types based on content
- Go []byte -> DuckDB BLOB
- Go time.Time -> DuckDB DATE, TIME, or TIMESTAMP
- Custom Date, Time, and Interval types for precise control
For more examples, check the example directory.
go-pduckdb registers itself as a driver named "duckdb" with the standard database/sql package, supporting:
- Connection management (Open, Close)
- Query execution (Exec, Query)
- Prepared statements
- Transactions
- Context handling
- Parameter binding
go-pduckdb also provides a native API for more direct interaction with DuckDB:
- DuckDB: Represents a database instance
- DuckDBConnection: Handles connections to the database
- DuckDBResult: Manages query results
- DuckDBDate, DuckDBTime, DuckDBTimestamp: Date and time types
go-pduckdb provides native Go type conversions for DuckDB's date and time types:
// Get date value
dateVal, hasValue := result.ValueDate(columnIndex, rowIndex)
if hasValue {
fmt.Println("Date:", dateVal.Format("2006-01-02"))
}
// Get timestamp value
tsVal, hasValue := result.ValueTimestamp(columnIndex, rowIndex)
if hasValue {
fmt.Println("Timestamp:", tsVal.Format("2006-01-02 15:04:05.000000"))
}Result values are read through DuckDB's data-chunk / vector API — the modern,
non-deprecated path — rather than the deprecated duckdb_value_* accessors.
This covers:
- All scalar types, including BLOB and INTERVAL (previously blocked by purego's struct-return limits), plus DECIMAL, UUID, HUGEINT and ENUM.
- The nested types LIST, ARRAY, STRUCT and MAP, decoded to
[]any,map[string]anyand[]duckdb.MapEntryrespectively (recursively, so lists of structs etc. work).
See docs/COMPATIBILITY.md for the full per-type matrix.
These currently scan as NULL:
- UNION
- BIT / VARINT
- TIME WITH TIME ZONE
- purego v0.10.0 or newer. v0.10.0 added struct-by-value argument support on
Linux; the driver needs it to call
duckdb_fetch_chunk(which takes theduckdb_resultstruct by value) on non-macOS platforms. Earlier purego releases only allowed struct arguments on darwin.
This project follows the standard Go project layout:
go-pduckdb/
├── driver.go # database/sql driver implementation
├── error.go # Error handling
├── pduckdb.go # Core public API (DuckDB, Connect, Close)
├── *_test.go # Unit + integration tests
├── example/ # Example programs
│ ├── columntypes/ # Column type demonstration
│ ├── databasesql/ # database/sql usage examples
│ ├── databasesql2/ # Additional database/sql examples
│ ├── enhancedtypes/ # Enhanced type support examples
│ ├── json/ # JSON handling examples
│ ├── multistatement/ # Multi-statement examples
│ └── simple/ # Simple API usage examples
└── internal/ # Internal implementation
├── convert/ # Parameter type-conversion utilities
├── duckdb/ # Low-level DuckDB bindings
│ ├── library.go # Library loading (purego Dlopen)
│ ├── db.go # C function registration
│ ├── conn.go # Connection handling
│ ├── statement.go # Prepared statements + parameter binding
│ ├── result.go # Result reading (data-chunk cache)
│ ├── chunk.go # Data-chunk / vector decoding
│ └── type.go # DuckDB type definitions
└── integ/ # Integration test infrastructure
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
This project is licensed under the MIT License - see the LICENSE file for details.