Appender (better_duck_core::Appender) is DuckDB’s fast bulk-ingest path — far faster than
row-by-row INSERT. Create one from a Connection. This page covers
the everyday lifecycle; see appender advanced for projected columns,
catalog/query appenders, raw chunk ingestion, and poisoning.
Lifecycle at a glance
1
Create
conn.appender("items", "main")? — (table, schema).2
Append rows
One
append call per column value, left to right, row by row.3
Flush
save() (flush, keep using) or finish() (flush, close, report errors).Appending rows
append<T: AppendAble>(&mut self, row: &mut T) -> Result<()> (#[must_use]). Every
DuckValue and the primitive Rust types implement
AppendAble. Internally the call opens a row,
appends the value, and closes the row with a guard that ends the row even on an early return or
panic — so a half-written row can never bleed into the next call.
Flushing: save vs finish vs drop
Appenders and the connection
- An
Appenderkeeps its originating connection alive and appends inside any transaction open on that connection. - A second connection (
try_clone) can read the rows aftersave(). Connection::closeerrors while an appender from that connection is still alive; the connection closes once the last appender drops.
See also
- Appender advanced — projected columns, catalog/query appenders, raw chunk ingestion, and poisoning semantics.
- ToRow derive — append a struct as a row.
- Data chunks — build a chunk and bulk-append it.