Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nimdeveloper/better-duck/llms.txt
Use this file to discover all available pages before exploring further.
better-duck-diesel supports the full Diesel 2.3 query DSL for DuckDB — inserts with RETURNING,
filtered and ordered selects, targeted updates, and conditional deletes all work exactly as they do
with any other Diesel backend. The examples below walk through each operation step by step using a
products table.
Defining a Table
Schema types are declared with the standard diesel::table! macro. Column types use the Diesel
SQL type vocabulary (Integer, Text, Double, and so on). DuckDB-specific types require an
extra use better_duck_diesel::sql_types::*; inside the macro body — see the Types
page for details.
use better_duck_diesel::DuckDbConnection;
use diesel::{connection::SimpleConnection, prelude::*};
diesel::table! {
products (id) {
id -> Integer,
name -> Text,
price -> Double,
}
}
The table macro generates the products::table, products::id, products::name, and
products::price symbols used in every query below.
INSERT with RETURNING
Use diesel::insert_into to build an INSERT statement. Chain .returning(...) to select which
columns the database should echo back, then call .get_results (multiple rows) or .get_result
(exactly one row) to execute and deserialise the output.
let mut conn = DuckDbConnection::establish(":memory:")?;
conn.batch_execute(
"CREATE TABLE products (
id INTEGER PRIMARY KEY,
name VARCHAR NOT NULL,
price DOUBLE NOT NULL
)",
)?;
let inserted: Vec<(i32, String)> = diesel::insert_into(products::table)
.values(&vec![
(products::id.eq(1), products::name.eq("widget"), products::price.eq(9.99)),
(products::id.eq(2), products::name.eq("gadget"), products::price.eq(24.50)),
])
.returning((products::id, products::name))
.get_results(&mut conn)?;
// inserted == [(1, "widget"), (2, "gadget")]
When you don’t need the returned data, call .execute(&mut conn)? instead — it returns the number
of rows affected as a usize.
SELECT with Filter and Ordering
Queries start from the generated table symbol. Chain .filter(...) for a WHERE clause,
.order(...) for ORDER BY, and .select(...) to name the projection. Call .load to materialise
all rows into a Vec, or .first to take the first row only.
// Rows cheaper than $20, sorted by name A-Z.
let cheap: Vec<(i32, String, f64)> = products::table
.filter(products::price.lt(20.0))
.order(products::name.asc())
.select((products::id, products::name, products::price))
.load(&mut conn)?;
To count rows without fetching them:
let total: i64 = products::table.count().first(&mut conn)?;
UPDATE
diesel::update accepts either a full table or a filtered subquery. Chain .set(...) with a
single column assignment or a tuple of assignments, then call .execute for the affected-row count.
// Raise the price of product id=1 to $11.99.
diesel::update(products::table.filter(products::id.eq(1)))
.set(products::price.eq(11.99))
.execute(&mut conn)?;
// Update multiple columns at once.
diesel::update(products::table.filter(products::id.eq(2)))
.set((
products::name.eq("super gadget"),
products::price.eq(29.99),
))
.execute(&mut conn)?;
DELETE
diesel::delete mirrors the update form: target either the whole table or a filtered subset.
// Remove product id=2.
diesel::delete(products::table.filter(products::id.eq(2)))
.execute(&mut conn)?;
// Remove all products.
diesel::delete(products::table).execute(&mut conn)?;
Full Self-Contained Example
The snippet below is a complete, runnable main function that combines all four operations:
use better_duck_diesel::DuckDbConnection;
use diesel::{connection::SimpleConnection, prelude::*};
diesel::table! {
products (id) {
id -> Integer,
name -> Text,
price -> Double,
}
}
fn main() -> QueryResult<()> {
let mut conn = DuckDbConnection::establish(":memory:")?;
conn.batch_execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, name VARCHAR NOT NULL, price DOUBLE NOT NULL)",
)?;
// INSERT with RETURNING
let inserted: Vec<(i32, String)> = diesel::insert_into(products::table)
.values(&vec![
(products::id.eq(1), products::name.eq("widget"), products::price.eq(9.99)),
(products::id.eq(2), products::name.eq("gadget"), products::price.eq(24.50)),
])
.returning((products::id, products::name))
.get_results(&mut conn)?;
println!("inserted: {:?}", inserted);
// SELECT with filter and ordering
let cheap: Vec<(i32, String, f64)> = products::table
.filter(products::price.lt(20.0))
.order(products::name.asc())
.select((products::id, products::name, products::price))
.load(&mut conn)?;
println!("cheap products: {:?}", cheap);
// UPDATE
diesel::update(products::table.filter(products::id.eq(1)))
.set(products::price.eq(11.99))
.execute(&mut conn)?;
// DELETE
diesel::delete(products::table.filter(products::id.eq(2)))
.execute(&mut conn)?;
Ok(())
}