Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Query API Reference


Overview

A Query describes what to retrieve from the database: which rows match, which columns to return, how to order and paginate them, and how to combine data across tables. Queries are constructed with QueryBuilder and consumed by Database::select, Database::select_raw, and Database::select_join.

For an introductory walkthrough, see the Querying Guide.


Query Struct

#![allow(unused)]
fn main() {
pub struct Query {
    columns: Select,
    pub distinct_by: Vec<String>,
    pub eager_relations: Vec<String>,
    pub filter: Option<Filter>,
    pub group_by: Vec<String>,
    pub having: Option<Filter>,
    pub joins: Vec<Join>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
    pub order_by: Vec<(String, OrderDirection)>,
}
}
FieldTypeDescription
columnsSelectSelect::All or Select::Columns(Vec<String>)
distinct_byVec<String>Columns used to deduplicate results
eager_relationsVec<String>Foreign-key relations to load eagerly
filterOption<Filter>WHERE-clause expression
group_byVec<String>GROUP BY columns for aggregate queries
havingOption<Filter>HAVING filter applied to aggregated groups
joinsVec<Join>Join clauses (only valid via select_join)
limitOption<usize>Maximum number of records to return
offsetOption<usize>Number of records to skip
order_byVec<(String, OrderDirection)>Multi-column ordering

Use Query::builder() to obtain a QueryBuilder.


QueryBuilder

Field Selection

MethodEffect
.all()Selects all columns (Select::All)
.field(name)Adds a single column to the selection
.fields(iter)Adds multiple columns

The primary key is always included by Database::select::<T> even when not explicitly listed.

Filters

MethodEffect
.filter(Option<Filter>)Replaces the current filter
.and_where(Filter)Combines with existing filter using AND
.or_where(Filter)Combines with existing filter using OR

See Filters in the Querying Guide and JSON Filters for the full filter API.

Joins

MethodJoin type
.inner_join(table, left_col, right_col)INNER
.left_join(table, left_col, right_col)LEFT
.right_join(table, left_col, right_col)RIGHT
.full_join(table, left_col, right_col)FULL

Queries containing joins must be executed via Database::select_join. Calling Database::select::<T> with a joined query returns QueryError::JoinInsideTypedSelect.

Eager Loading

#![allow(unused)]
fn main() {
.with("posts")
}

Adds a foreign-key relation to load eagerly. Each relation is loaded once via a batch fetch keyed by the foreign-key column.

Distinct

#![allow(unused)]
fn main() {
.distinct(&["name"])
.distinct(&["category", "vendor"])
}

Sets distinct_by to the supplied list of column names. Rows are deduplicated by the tuple of values across those columns; the first row encountered for each distinct tuple is retained. Passing an empty slice is a no-op.

Semantics:

  • Columns are looked up on the source record (ValuesSource::This).
  • Missing columns are treated as Value::Null, so listing an unknown column collapses every row into a single result.
  • Deduplication runs before ordering, offset, and limit.
  • The selected fields (Select::Columns) do not need to include the distinct_by columns.

Aggregations

#![allow(unused)]
fn main() {
.group_by(&["category"])
.having(Filter::gt("count", Value::Uint64(10u64.into())))
}
MethodEffect
.group_by(&[col...])Sets group_by to the supplied list of columns
.having(Filter)Sets the HAVING filter applied to aggregated groups

Aggregations operate over the rows that survive WHERE and DISTINCT. Each group of rows sharing the same group_by tuple produces one AggregatedRow. The aggregate functions to compute are described by AggregateFunction; their results are returned as AggregatedValue entries inside the row.

The HAVING filter is evaluated after aggregation, against the grouping keys and aggregate results.

Ordering

MethodEffect
.order_by_asc(column)Appends ascending sort by column
.order_by_desc(column)Appends descending sort by column

Multiple order_by_* calls produce stable multi-key sorts; later keys break ties from earlier keys.

Pagination

MethodEffect
.limit(usize)Caps the number of records returned
.offset(usize)Skips the first N records

Aggregate Types

Types used to describe and return aggregated query results. All three are re-exported from the wasm-dbms-api, ic-dbms-api, and ic-dbms-client preludes.

AggregateFunction

#![allow(unused)]
fn main() {
pub enum AggregateFunction {
    Count(Option<String>),
    Sum(String),
    Avg(String),
    Min(String),
    Max(String),
}
}

Describes one aggregate function to compute over a group of rows.

VariantSQL equivalentNotes
Count(None)COUNT(*)Counts every row in the group
Count(Some(c))COUNT(c)Counts non-null values of column c
Sum(c)SUM(c)Sum of c across the group
Avg(c)AVG(c)Arithmetic mean of c
Min(c)MIN(c)Minimum value of c
Max(c)MAX(c)Maximum value of c

AggregatedRow

#![allow(unused)]
fn main() {
pub struct AggregatedRow {
    pub group_keys: Vec<Value>,
    pub values: Vec<AggregatedValue>,
}
}

A single row of aggregated output. group_keys holds the values of the group_by columns that identify the group; values holds the aggregate results in the same order as the AggregateFunction list supplied with the query.

AggregatedValue

#![allow(unused)]
fn main() {
pub enum AggregatedValue {
    Count(u64),
    Sum(Value),
    Avg(Value),
    Min(Value),
    Max(Value),
}
}

Carries the result of one aggregate function. Count is always a u64; the remaining variants wrap a Value whose concrete variant depends on the source column’s data type.


Execution Order

The select pipeline applies the query elements in this order — matching standard SQL semantics:

  1. WHEREfilter is applied while scanning records (or via an index plan).
  2. DISTINCTdistinct_by deduplicates the surviving rows.
  3. GROUP BY / aggregates — when group_by is set, surviving rows are bucketed by the grouping tuple and the requested AggregateFunctions are computed per bucket, producing AggregatedRows.
  4. HAVINGhaving filters the aggregated groups.
  5. Eager loading — relations declared by with(...) are batch-fetched (non-aggregate selects only).
  6. Column selection — non-selected columns are dropped from each row.
  7. ORDER BYorder_by keys are applied in declared order.
  8. OFFSET / LIMIT — applied last when order_by or distinct_by is set; otherwise applied during the scan for early termination.

When neither order_by nor distinct_by is present, the engine applies offset/limit during iteration to avoid materialising the entire result set.


Errors

All variants come from QueryError. Most are surfaced at planning time (before any rows are scanned) so callers fail fast.

Aggregate-specific (Database::aggregate)

ConditionVariant
SUM or AVG references a non-numeric columnInvalidQuery("aggregate requires numeric column: '<col>'")
Aggregate references a column not on the tableUnknownColumn(<col>)
GROUP BY references a column not on the tableUnknownColumn(<col>)
HAVING references unknown column or agg{N}InvalidQuery("HAVING references unknown column or aggregate: '<col>'")
ORDER BY references unknown agg{N}InvalidQuery("ORDER BY references unknown aggregate output: '<col>'")
LIKE used inside a HAVING clauseInvalidQuery("LIKE is not supported in HAVING")
JSON filter used inside a HAVING clauseInvalidQuery("JSON filters are not supported in HAVING")
Query carries joins on an aggregate callInvalidQuery("joins are not supported in aggregate queries")
Query carries eager_relations on an aggregateInvalidQuery("eager relations are not supported in aggregate queries")

Non-aggregate select paths

ConditionVariant
group_by or having set on select / select_raw / select_joinAggregateClauseInSelect (use Database::aggregate)
Query carries joins on a typed select::<T> callJoinInsideTypedSelect