Skip to main content
Every statement runs through a Connection (&mut self). This page covers the executors; results and parameters have their own pages. Each executor is #[must_use] — a returned DuckResult carries both an affected-row count and a row iterator, so don’t discard it.

execute_batch — fire-and-forget

execute_batch(sql) -> Result<()> runs one or more ;-separated statements and discards every result. Ideal for DDL and simple DML. DuckDB does the statement splitting (NUL-safe, comment- and dollar-quote-aware) — it is not a Rust-side split(';'), so a semicolon inside a string literal is not a false split.

execute — one statement, a result

execute(sql) -> Result<DuckResult> prepares and runs a single statement and returns a DuckResult. It works for every statement kind: For parameterized SQL use execute_with / execute_with_named.

insert — a homogeneous bind-and-run

insert<T: AppendAble, I: IntoIterator<Item = T>>(sql, values) -> Result<()> binds a homogeneous iterator as consecutive positional parameters and executes once.
insert errors if no rows changed — a statement that changes nothing (e.g. a bare SELECT) is treated as a failed insert and returns Error::DuckDBFailure(_, Some("Failed to insert values")). For heterogeneous binds, or when a zero-change result is valid, use execute_with instead.

Multi-statement batches you run individually

extract_statements(sql) -> Result<ExtractedStatements> parses a ;-separated batch (again, DuckDB parses — no Rust-side split) into statements you prepare and run one at a time. See prepared statements.

Reading the catalog without a query

table_description(schema, table) and table_description_ext(catalog, schema, table) return a TableDescription describing a table’s columns — names, lossless types, and DEFAULT flags — without issuing a query.
table_names(sql, qualified) -> Result<Vec<String>> reports the tables a query reads, using DuckDB’s own parser.
table_names requires syntactically valid SQL. On a parse error, DuckDB’s duckdb_get_table_names throws a C++ exception that unwinds across the FFI boundary and aborts the process — an upstream C-API defect this wrapper cannot intercept. Only pass SQL you know parses. (An interior NUL is still caught safely as Error::NulError.)

Registering a custom type name

register_logical_type(&LogicalType) registers an aliased LogicalType in the connection’s catalog so its alias becomes usable as a SQL type name. Set the alias with LogicalType::set_alias first; a type with no alias is rejected.

See also