- 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
- Java (JDBC)
- Overview
- Define Connections
- Run Queries
- Import Data
- Handle Results
- Define Functions
- Profile and Monitor
- 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
- 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 Rust client offers two ways to get data into DuckDB in bulk: the high-performance Appender for inserting rows from Rust, and reading directly from data files such as Parquet, CSV, and JSON. Both are described below.
Appender
The Appender is the fastest way to insert rows generated in Rust. Create one from a Connection with appender(), passing the target table name, then push rows with append_row(). Each row is a parameter list built with the params! macro:
use duckdb::{params, Connection, Result};
fn insert_rows(conn: &Connection) -> Result<()> {
conn.execute_batch("CREATE TABLE foo (a INTEGER, b INTEGER)")?;
let mut app = conn.appender("foo")?;
app.append_row(params![1, 2])?;
app.append_row(params![3, 4])?;
app.flush()?;
Ok(())
}
To append many rows from an iterator of parameter lists in one call, use append_rows():
app.append_rows([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])?;
Additional constructors target tables outside the default schema: appender_to_db() takes a schema name, and appender_to_catalog_and_db() takes a catalog and a schema. To append only a subset of columns, use appender_with_columns() (and its schema and catalog variants).
Warning DuckDB buffers appended rows and checks constraints such as
NOT NULLand foreign keys only when the buffer is flushed, soappend_row()may returnOkfor a row that later fails a constraint check. Always callflush()explicitly and check its result before the appender is dropped. Dropping an appender flushes any remaining rows, but errors raised during that implicit flush are discarded, because Rust'sDropcannot report them.
The Appender is Sync but not Send, because it borrows the Connection. To append from another thread, move the Connection to that thread and create the appender there.
Appending Inside a Transaction
For a large load, wrapping the appends in a transaction lets DuckDB commit them as a single unit. Begin a transaction with Connection::transaction(), set it to commit on drop, and create the appender from the transaction. The following bulk-inserts ten million rows, adapted from the crate's appender example:
use duckdb::{params, Connection, DropBehavior, Result};
fn main() -> Result<()> {
let mut db = Connection::open_in_memory()?;
db.execute_batch(
"CREATE TABLE test (id INTEGER NOT NULL, area CHAR(6), age TINYINT NOT NULL, active TINYINT NOT NULL)",
)?;
let row_count = 10_000_000;
{
let mut tx = db.transaction()?;
tx.set_drop_behavior(DropBehavior::Commit);
let mut app = tx.appender("test")?;
for i in 0..row_count {
app.append_row(params![i, "123456", 15, 1])?;
}
} // the appender flushes and the transaction commits here, on drop
let count = db.query_row("SELECT count(*) FROM test", [], |row| row.get::<_, i64>(0))?;
assert_eq!(count, row_count);
Ok(())
}
A Transaction rolls back by default when dropped. Call set_drop_behavior(DropBehavior::Commit) to commit on drop instead, or call tx.commit() explicitly. The other DropBehavior variants are Rollback (the default), Ignore (leave the transaction open), and Panic.
Appending Arrow Data
With the appender-arrow feature enabled, an Appender can append an Apache Arrow RecordBatch directly, which avoids building a parameter list per row. See Handle Results for the Arrow integration.
Reading Data Files
DuckDB can read many file formats directly in SQL, which is often the simplest way to load external data. The Rust client just sends the query. For Parquet, enable the parquet feature and query read_parquet(), adapted from the crate's parquet example:
use duckdb::{Connection, Result};
use duckdb::arrow::record_batch::RecordBatch;
let conn = Connection::open_in_memory()?;
conn.execute_batch("INSTALL parquet; LOAD parquet;")?;
let batches: Vec<RecordBatch> = conn
.prepare("SELECT * FROM read_parquet(?)")?
.query_arrow(["data.parquet"])?
.collect();
The same pattern reads CSV with read_csv() and JSON with read_json() (the latter needs the json feature). 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 execute().
Further Reading
- Appender — the engine-level Appender interface that the Rust
Appenderwraps. - Data Import — DuckDB's full set of bulk-loading options.
- Handle Results — the Apache Arrow integration used to append and read record batches.
- Run Queries — sending the
CREATE TABLEandCOPYstatements the file readers build on.