Skip to main content
Reading data through DuckDbConnection. All of this is Diesel’s portable query builder — it works on the DuckDB backend with no extra imports. For DuckDB-only predicates and scalar functions in .filter / .select, add the DSL prelude.

Loading rows

.load() materializes a result set into a Vec. The target type is a tuple of the selected columns’ Rust types, or a #[derive(Queryable)] struct:

Selecting specific columns

.select narrows the projection. A single column loads as its scalar type; a tuple loads as a tuple:
Prefer .select(T::as_select()) with a #[derive(Selectable)] struct over positional Queryable tuples when a query grows: it binds columns by name, so reordering struct fields can’t silently mis-map. See Schema.

Filtering, ordering, limiting

Portable predicates (eq, ne, lt, le, gt, ge, like, between, is_null, eq_any, …) and count/sum/avg/min/max are all Diesel’s own and need no DuckDB imports.
For DuckDB-only predicates — ILIKE, SIMILAR TO, GLOB, regex operators, IS DISTINCT FROM — add use better_duck_diesel::dsl::*;. See Operators. DuckDB-only scalar functions (e.g. lower, date_trunc) can also appear in .filter/.select — see the DSL overview.

Single rows

Counting and aggregating

Mind DuckDB’s aggregate type widening. SUM/AVG over a DOUBLE column widen to DOUBLE (load as Option<f64>), but SUM over an INTEGER column widens to HUGEINT, which the BigInt adapter rejects. Aggregate a DOUBLE column, or CAST the sum, rather than summing an integer column directly into an i64.
DuckDB-specific aggregates (median, stddev, string_agg, array_agg, …) live in the DSL — see Aggregates.

Nullable columns

A Nullable<T> column binds Some(v)/None and loads as Option<T> — SQL NULL reads back as None. Selecting a normally-non-null expression .nullable() (e.g. an aggregate over an empty set, or the right side of a LEFT JOIN) is the other common source of Option:

Joins

Teach Diesel the relationship with joinable! + allow_tables_to_appear_in_same_query!, then inner_join / left_join. inner_join keeps only matched rows; left_join keeps every left row and makes the right side .nullable():

Boxed queries & pagination

.into_boxed() erases the query type so you can apply limit/offset decided at runtime — the usual pagination pattern:
To see the SQL a query emits for the DuckDB backend — numbered $1 placeholders and double-quoted identifiers — render it with diesel::debug_query:
The better_duck_diesel::query_builder module (re-exported as qb) also exposes DuckDbQueryBuilder and the Comma / In fragment helpers for building SQL fragments by hand.

See also

Operators

ILIKE, GLOB, regex, and null-safe comparison.

Aggregates

DuckDB aggregates for grouped selects.

Insert

Write rows back.

Type mapping

Which Rust type each column loads as.