- Installation
- Documentation
- Getting Started
- Connect
- Data Import and Export
- Overview
- Data Sources
- CSV Files
- JSON Files
- Overview
- Creating JSON
- Loading JSON
- Writing JSON
- JSON Type
- JSON Functions
- Format Settings
- Installing and Loading
- SQL to / from JSON
- Caveats
- Multiple Files
- Parquet Files
- Partitioning
- Appender
- INSERT Statements
- Lakehouse Formats
- Client APIs
- Overview
- ADBC
- C
- Overview
- Startup
- Configuration
- Query
- Data Chunks
- Vectors
- Values
- Types
- Prepared Statements
- Appender
- Table Functions
- Replacement Scans
- API Reference
- C++
- CLI
- Overview
- Arguments
- Dot Commands
- Output Formats
- Editing
- Friendly CLI
- Safe Mode
- Autocomplete
- Syntax Highlighting
- Known Issues
- Go
- Overview
- Connect
- Import Data
- Run Queries
- Handle Results
- Write User Defined Functions
- Profile and Monitor
- Troubleshoot
- Java (JDBC)
- Overview
- Connect
- Import Data
- Run Queries
- Handle Results
- Write User Defined Functions
- Profile and Monitor
- Deploy as Native Image
- Troubleshoot
- Node.js (Neo)
- ODBC
- Python
- Overview
- Data Ingestion
- Conversion between DuckDB and Python
- DB API
- Relational API
- Function API
- Types API
- Expression API
- Spark API
- API Reference
- Known Python Issues
- R
- Rust
- Overview
- Connect
- Import Data
- Run Queries
- Handle Results
- Write User Defined Functions
- Profile and Monitor
- Troubleshoot
- Wasm
- Tertiary Clients
- SQL
- Introduction
- Statements
- Overview
- ANALYZE
- ALTER TABLE
- ALTER VIEW
- ATTACH and DETACH
- CALL
- CHECKPOINT
- COMMENT ON
- COPY
- CREATE INDEX
- CREATE MACRO
- CREATE SCHEMA
- CREATE SECRET
- CREATE SEQUENCE
- CREATE TABLE
- CREATE VIEW
- CREATE TYPE
- DELETE
- DESCRIBE
- DROP
- EXPORT and IMPORT DATABASE
- INSERT
- LOAD / INSTALL
- MERGE INTO
- PIVOT
- Profiling
- SELECT
- SET / RESET
- SET VARIABLE
- SHOW and SHOW DATABASES
- SUMMARIZE
- Transaction Management
- UNPIVOT
- UPDATE
- USE
- VACUUM
- Query Syntax
- SELECT
- FROM and JOIN
- WHERE
- GROUP BY
- GROUPING SETS
- HAVING
- ORDER BY
- LIMIT and OFFSET
- SAMPLE
- Unnesting
- WITH
- WINDOW
- QUALIFY
- VALUES
- FILTER
- Set Operations
- Prepared Statements
- Data Types
- Overview
- Array
- Bitstring
- Blob
- Boolean
- Date
- Enum
- Geometry
- Interval
- List
- Literal Types
- Map
- NULL Values
- Numeric
- Struct
- Text
- Time
- Timestamp
- Time Zones
- Union
- Typecasting
- Variant
- Expressions
- Overview
- CASE Expression
- Casting
- Collations
- Comparisons
- IN Operator
- Logical Operators
- Star Expression
- Subqueries
- TRY
- Functions
- Overview
- Aggregate Functions
- Array Functions
- Bitstring Functions
- Blob Functions
- Date Format Functions
- Date Functions
- Date Part Functions
- Enum Functions
- Geometry Functions
- Interval Functions
- Lambda Functions
- List Functions
- Map Functions
- Nested Functions
- Numeric Functions
- Pattern Matching
- Regular Expressions
- Struct Functions
- Text Functions
- Time Functions
- Timestamp Functions
- Timestamp with Time Zone Functions
- Union Functions
- Utility Functions
- Window Functions
- Constraints
- Indexes
- Meta Queries
- DuckDB's SQL Dialect
- Overview
- Indexing
- Friendly SQL
- Keywords and Identifiers
- Order Preservation
- PostgreSQL Compatibility
- SQL Quirks
- PEG Parser
- Samples
- Configuration
- Extensions
- Overview
- Installing Extensions
- Advanced Installation Methods
- Distributing Extensions
- Versioning of Extensions
- Troubleshooting of Extensions
- Core Extensions
- Overview
- AutoComplete
- Avro
- AWS
- Azure
- Delta
- DuckLake
- Encodings
- Excel
- Full Text Search
- httpfs (HTTP and S3)
- Iceberg
- ICU
- inet
- jemalloc
- Lance
- MotherDuck
- MySQL
- ODBC
- Quack
- PostgreSQL
- Spatial
- SQLite
- TPC-DS
- TPC-H
- UI
- Unity Catalog
- Vortex
- VSS
- Quack Remote Protocol
- Guides
- Overview
- Data Viewers
- Database Integration
- File Formats
- Overview
- CSV Import
- CSV Export
- Directly Reading Files
- Directly Reading DuckDB Databases
- Excel Import
- Excel Export
- JSON Import
- JSON Export
- Parquet Import
- Parquet Export
- Querying Parquet Files
- File Access with the file: Protocol
- Meta Queries
- Describe Table
- EXPLAIN: Inspect Query Plans
- EXPLAIN ANALYZE: Profile Queries
- List Tables
- Summarize
- DuckDB Environment
- Network and Cloud Storage
- Overview
- HTTP Parquet Import
- S3 Parquet Import
- S3 Parquet Export
- S3 Iceberg Import
- S3 Express One
- GCS Import
- Cloudflare R2 Import
- DuckDB over HTTPS / S3
- Fastly Object Storage Import
- SeaweedFS Import
- Tigris Import
- ODBC
- Performance
- Overview
- Environment
- Import
- Schema
- Indexing
- Join Operations
- File Formats
- How to Tune Workloads
- My Workload Is Slow
- Out-of-Memory Issues
- Benchmarks
- Working with Huge Databases
- Python
- Installation
- Executing SQL
- Jupyter Notebooks
- marimo Notebooks
- SQL on Pandas
- Import from Pandas
- Export to Pandas
- Import from Numpy
- Export to Numpy
- SQL on Arrow
- Import from Arrow
- Export to Arrow
- Relational API on Pandas
- Multiple Python Threads
- Integration with Ibis
- Integration with Polars
- Integration with PyTorch
- Using fsspec Filesystems
- SQL Editors
- SQL Features
- AsOf Join
- Full-Text Search
- Graph Queries
- query and query_table Functions
- Merge Statement for SCD Type 2
- Timestamp Issues
- Snippets
- Creating Synthetic Data
- Dutch Railway Datasets
- Sharing Macros
- Analyzing a Git Repository
- Importing Duckbox Tables
- Copying an In-Memory Database to a File
- Calculating a Database Checksum
- Troubleshooting
- Glossary of Terms
- Browsing Offline
- Operations Manual
- Overview
- DuckDB's Footprint
- Installing DuckDB
- Logging
- User Agents
- Securing DuckDB
- Non-Deterministic Behavior
- Limits
- DuckDB Docker Container
- Development
- DuckDB Repositories
- Release Cycle
- Metrics
- Profiling
- Building DuckDB
- Overview
- Build Configuration
- Building Extensions
- Android
- Linux
- macOS
- Raspberry Pi
- Windows
- Python
- R
- Troubleshooting
- Unofficial and Unsupported Platforms
- Benchmark Suite
- Testing
- Internals
- Sitemap
- Live Demo
Overview
The Go client offers two ways to load data into DuckDB in bulk: the high-performance Appender for inserting rows generated in Go, and reading directly from data files such as Parquet, CSV, and JSON. Both are described below. For inserting a handful of rows, an ordinary INSERT statement is fine; the options here are for volume.
Appender
The Appender is the fastest way to insert rows generated in Go, much faster than issuing INSERT statements. It operates on a raw DuckDB connection, so it is created from a Connector rather than a *sql.DB. Obtain a connection from the connector, then create the appender for a target table with duckdb.NewAppenderFromConn(), passing the schema (empty for the default) and table name:
connector, err := duckdb.NewConnector("test.db", nil)
if err != nil {
log.Fatal(err)
}
defer connector.Close()
conn, err := connector.Connect(context.Background())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// The table must already exist.
appender, err := duckdb.NewAppenderFromConn(conn, "", "users")
if err != nil {
log.Fatal(err)
}
defer appender.Close()
// Each argument is one column value, in table column order.
if err := appender.AppendRow("Fred", int32(34)); err != nil {
log.Fatal(err)
}
Each argument to AppendRow() is a column value in table-column order, and its Go type must match the column's DuckDB type; the example passes int32(34) for an INTEGER column. Call AppendRow() once per row.
Warning The appender buffers rows and flushes them to DuckDB in chunks, so a constraint violation such as a
NOT NULLor foreign-key failure may surface only when the buffer is flushed rather than from theAppendRow()call that added the offending row. CallFlush()and check its error to force pending rows in before relying on them, and always check the error fromClose(), which performs a final flush.
The following follows the client's appender example, which creates the table through a *sql.DB opened on the same connector, appends a row, then reads it back:
package main
import (
"context"
"database/sql"
"log"
"github.com/duckdb/duckdb-go/v2"
)
func main() {
c, err := duckdb.NewConnector("", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
con, err := c.Connect(context.Background())
if err != nil {
log.Fatal(err)
}
defer con.Close()
db := sql.OpenDB(c)
defer db.Close()
if _, err := db.Exec(`CREATE TABLE users (name VARCHAR, age INTEGER)`); err != nil {
log.Fatal(err)
}
a, err := duckdb.NewAppenderFromConn(con, "", "users")
if err != nil {
log.Fatal(err)
}
if err := a.AppendRow("Fred", int32(34)); err != nil {
log.Fatal(err)
}
// Close flushes any buffered rows before closing the appender.
if err := a.Close(); err != nil {
log.Fatal(err)
}
var (
name string
age int
)
row := db.QueryRowContext(context.Background(), `SELECT name, age FROM users`)
if err := row.Scan(&name, &age); err != nil {
log.Fatal(err)
}
log.Printf("User: name=%s, age=%d", name, age)
}
Targeting Other Schemas and Column Subsets
The package provides more constructors for targeting other catalogs and schemas, selecting a subset of columns, or transforming batches as they are appended. Each takes a driver.Conn as its first argument:
duckdb.NewAppender(driverConn, catalog, schema, table)appends to all columns of the specified table.duckdb.NewTableAppender(driverConn, query, catalog, schema, table, columnNames)runs anINSERT,DELETE,UPDATE, orMERGE INTOquery for each buffered batch. It infers the input types from the named columns in the target table. Pass an empty column-name slice to use all columns.duckdb.NewQueryAppender(driverConn, query, temporaryTable, columnTypes, columnNames)also runs a query for each batch, but lets the caller specify the temporary input table's name, column types, and column names.
duckdb.NewAppenderWithColumns(driverConn, catalog, schema, table, columnNames) can also append a subset of columns, leaving other columns at their default values or NULL. It is retained mostly for backward compatibility. Prefer NewTableAppender for this case because its query-based appender is more performant.
Reading Data Files
DuckDB can read many file formats directly in SQL, which is often the simplest way to load external data: the Go client just sends the query. Because the Parquet, CSV, and JSON readers are among the bundled extensions, no extra installation is needed. Query a file in place with the matching table function:
rows, err := db.QueryContext(ctx, `SELECT * FROM read_parquet('data.parquet')`)
The same pattern reads CSV with read_csv() and JSON with read_json(). To load a file into a table rather than query it in place, wrap the reader in a CREATE TABLE ... AS SELECT or COPY statement sent with Exec:
_, err := db.ExecContext(ctx, `CREATE TABLE data AS SELECT * FROM read_parquet('data.parquet')`)
Further Reading
- Appender — the engine-level Appender interface that the Go
Appenderwraps. - Data Import — DuckDB's full set of bulk-loading options.
- Run Queries — sending the
CREATE TABLEandCOPYstatements the file readers build on. - Connect — creating the
Connectorand connection the Appender operates on.