go-duckdb

repository·main·Indexed 22 days ago

https://github.com/marcboeker/go-duckdb

A Go SQL driver for DuckDB that conforms to the standard database/sql interface, supporting both in-memory and persistent databases. It includes a high-performance Appender API for bulk data ingestion and an opt-in Apache Arrow interface for efficient data transfer. Note: Starting with v2.5.0, this project has moved to the official DuckDB repository at github.com/duckdb/duckdb-go.

Tokens
21K
Snippets
76
Records
92
Agent score
77%

What's inside go-duckdb

  1. Manage memory and ensure data persistence via Close()

    main

    Because DuckDB runs in-process, all memory allocations occur within the host Go application. For long-running applications, you must call the appropriate Close() functions to prevent memory leaks and ensure data integrity.

    Crucially, for persistent DuckDB databases, calling Close() on the database or connector is required to synchronize changes from the Write-Ahead Log (WAL) to the persistent storage. Failure to do so may result in data loss.

    db, err := sql.Open("duckdb", "")
    defer db.Close()
    
    conn, err := db.Conn(context.Background())
    defer conn.Close()
    
    rows, err := conn.QueryContext(context.Background(), "SELECT 42")
    rows.Close()
    
    appender, err := duckdb.NewAppenderFromConn(conn, "", "test")
    defer appender.Close()
    
    c, err := duckdb.NewConnector("", nil)
    defer c.Close()
  2. Handle JSON type scanning changes

    main

    In v2, scanning DuckDB JSON types directly into string or []byte via Rows.Scan is no longer supported.

    To handle JSON data, you should:

    1. Scan into any (driver.Value).
    2. Scan into the driver's Composite type.

    If you must use string or []byte, cast the column in your SQL query to ::VARCHAR or ::BLOB.

  3. Manage connection lifetime and temporary objects

    main

    Temporary objects (like temporary tables) are scoped to a connection. Because Go's database/sql uses a connection pool, an idle connection might not be closed immediately, causing temporary tables to persist longer than expected.

    To ensure connections are closed and cleaned up immediately, disable idle connection pooling:

    db.SetMaxIdleConns(0)
  4. Link a custom static DuckDB library

    main

    If you need a custom-built DuckDB static library (e.g., with specific extensions), use the duckdb_use_static_lib build tag.

    Example for Darwin ARM64:

    CGO_ENABLED=1 CPPFLAGS="-DDUCKDB_STATIC_BUILD" CGO_LDFLAGS="-lduckdb_bundle -lc++ -L/path/to/libs" go build -tags=duckdb_use_static_lib
  5. Migrate to the official DuckDB Go client

    main

    Starting with v2.5.0, this project has moved to the official DuckDB repository. To migrate, update your import paths from github.com/marcboeker/go-duckdb to github.com/duckdb/duckdb-go.

    import "github.com/duckdb/duckdb-go"
  6. Link DuckDB as a dynamic library

    main

    To reduce binary size, you can dynamically link against an existing libduckdb library (e.g., .so on Linux or .dylib on macOS) instead of using the bundled static library. Use the duckdb_use_lib build tag.

    Linux

    CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go
    LD_LIBRARY_PATH=/path/to/libs ./main

    macOS

    CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go
    DYLD_LIBRARY_PATH=/path/to/libs ./main
    # On Linux.
    CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go
    LD_LIBRARY_PATH=/path/to/libs ./main
  7. Setup DuckDB on Windows

    main

    To compile this package on Windows, you need gcc and the necessary runtime libraries. Using msys64 is recommended:

    1. Install msys64 via their installer.
    2. Open an msys64 shell and run:
      pacman -S mingw-w64-ucrt-x86_64-gcc
    3. Add gcc to your PATH. In PowerShell, you can do this temporarily with:
      $env:PATH = "C:\msys64\ucrt64\bin:$env:PATH"
    pacman -S mingw-w64-ucrt-x86_64-gcc
  8. Enable and use the DuckDB Apache Arrow Interface

    main

    The Apache Arrow interface is an opt-in feature starting with v2. To use it, you must build your application with the duckdb_arrow build tag.

    Warning: Arrow connections are not safe for concurrent use and do not benefit from database/sql connection pooling.

    To use it:

    1. Build with: go build -tags="duckdb_arrow"
    2. Obtain an Arrow instance via NewArrowFromConn(conn).
    3. Use arrow.QueryContext(...) to get a reader and iterate through records.
    go build -tags="duckdb_arrow"
    c, err := duckdb.NewConnector("", nil)
    defer c.Close()
    
    conn, err := c.Connect(context.Background())
    defer conn.Close()
    
    // Obtain the Arrow from the connection.
    arrow, err := duckdb.NewArrowFromConn(conn)
    
    rdr, err := arrow.QueryContext(context.Background(), "SELECT * FROM generate_series(1, 10)")
    defer rdr.Release()
    
    for rdr.Next() {
      // Process each record.
    }
  9. Manage DuckDB Data Types and Values

    main

    The mapping package provides low-level primitives for handling DuckDB data types and values.

    Types without internal pointers

    These types can be created using New... helper functions and accessed via ...Members functions. Examples include:

    • Date, Time, Timestamp, Interval
    • HugeInt, UHugeInt, Decimal
    • StringT, Blob, Bit, BigNum

    Types with internal pointers (Manual Memory Management Required)

    Some types contain internal pointers and require explicit destruction to avoid memory leaks. These include:

    • Column, Result (High-level containers)
    • Vector, SelectionVector (Data buffers)
    • Database, Connection, ClientContext (Session objects)
    • PreparedStatement, Appender (Execution objects)
    • DataChunk, Value (Data units)

    Note: For types like Blob, Bit, and BigNum, you MUST use the provided Destroy... functions (e.g., DestroyBlob) to free the underlying memory.

    // Example of creating a Date (Type without internal pointers)
    d := mapping.NewDate(2023, 10, 27)
    
    // Example of destroying a Blob (Type with internal pointers)
    // mapping.DestroyBlob(myBlob)