Skip to main content

QueryBuilder

Struct QueryBuilder 

Source
pub struct QueryBuilder<T, E> { /* private fields */ }
Expand description

A fluent Query Builder for constructing SQL queries.

QueryBuilder provides a type-safe, ergonomic interface for building and executing SQL queries across different database backends. It supports filtering, ordering, pagination, and both SELECT and INSERT operations.

§Type Parameter

  • 'a - Lifetime of the database reference (used for PhantomData)
  • T - The Model type this query operates on
  • E - The connection type (Database or Transaction)

§Fields

  • db - Reference to the database connection pool or transaction
  • table_name - Static string containing the table name
  • columns_info - Metadata about each column in the table
  • columns - List of column names in snake_case format
  • select_columns - Specific columns to select (empty = SELECT *)
  • where_clauses - List of filter functions to apply
  • order_clauses - List of ORDER BY clauses
  • limit - Maximum number of rows to return
  • offset - Number of rows to skip (for pagination)
  • _marker - PhantomData to bind the generic type T

Implementations§

Source§

impl<T, E> QueryBuilder<T, E>
where T: Model + Send + Sync + Unpin + AnyImpl, E: Connection,

Source

pub fn new( tx: E, driver: Drivers, table_name: &'static str, columns_info: Vec<ColumnInfo>, columns: Vec<String>, ) -> Self

Creates a new QueryBuilder instance.

This constructor is typically called internally via db.model::<T>(). You rarely need to call this directly.

§Arguments
  • db - Reference to the database connection
  • table_name - Name of the table to query
  • columns_info - Metadata about table columns
  • columns - List of column names
§Returns

A new QueryBuilder instance ready for query construction

§Example
// Usually called via db.model::<User>()
let query = db.model::<User>();
Source

pub fn filter_subquery<S, SE>( self, col: &'static str, op: Op, subquery: QueryBuilder<S, SE>, ) -> Self
where S: Model + Send + Sync + Unpin + AnyImpl + 'static, SE: Connection + 'static,

Adds a WHERE IN (SUBQUERY) clause to the query.

This allows for filtering a column based on the results of another query.

§Example
let subquery = db.model::<Post>().select("user_id").filter("views", ">", 1000);
db.model::<User>().filter_subquery("id", Op::In, subquery).scan().await?;
Source

pub async fn truncate(self) -> Result<(), Error>

Truncates the table associated with this Model.

This method removes all records from the table. It uses TRUNCATE TABLE for Postgres and MySQL, and DELETE FROM with sequence reset for SQLite.

§Returns
  • Ok(()) - Table truncated successfully
  • Err(sqlx::Error) - Database error occurred
§Example
db.model::<Log>().truncate().await?;
Source

pub fn union(self, other: QueryBuilder<T, E>) -> Self
where T: AnyImpl + 'static, E: 'static,

Combines the results of this query with another query using UNION.

This method allows you to combine the result sets of two queries into a single result set. Duplicate rows are removed by default.

§Arguments
  • other - Another QueryBuilder instance to combine with.
§Example
let q1 = db.model::<User>().filter("age", ">", 18);
let q2 = db.model::<User>().filter("status", "=", "premium");
let results = q1.union(q2).scan().await?;
Source

pub fn union_all(self, other: QueryBuilder<T, E>) -> Self
where T: AnyImpl + 'static, E: 'static,

Combines the results of this query with another query using UNION ALL.

This method allows you to combine the result sets of two queries into a single result set, including all duplicates.

§Arguments
  • other - Another QueryBuilder instance to combine with.
§Example
let q1 = db.model::<User>().filter("age", ">", 18);
let q2 = db.model::<User>().filter("status", "=", "premium");
let results = q1.union_all(q2).scan().await?;
Source

pub fn filter<V>(self, col: &'static str, op: Op, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a WHERE clause to the query.

This method adds a filter condition to the query. Multiple filters can be chained and will be combined with AND operators. The value is bound as a parameter to prevent SQL injection.

§Type Parameters
  • V - The type of the value to filter by. Must be encodable for SQL queries.
§Arguments
  • col - The column name to filter on
  • op - The comparison operator (e.g., “=”, “>”, “LIKE”, “IN”)
  • value - The value to compare against
§Example
query.filter("age", Op::Gte, 18)
// SQL: AND "age" >= 18
Source

pub fn or_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an OR WHERE clause to the query.

§Arguments
  • col - The column name to filter on
  • op - The comparison operator
  • value - The value to compare against
§Example
query.filter("age", Op::Lt, 18).or_filter("active", Op::Eq, false)
// SQL: AND "age" < 18 OR "active" = false
Source

pub fn not_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an AND NOT WHERE clause to the query.

§Arguments
  • col - The column name to filter on
  • op - The comparison operator
  • value - The value to compare against
§Example
query.not_filter("status", Op::Eq, "banned")
// SQL: AND NOT "status" = 'banned'
Source

pub fn or_not_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an OR NOT WHERE clause to the query.

§Arguments
  • col - The column name to filter on
  • op - The comparison operator
  • value - The value to compare against
§Example
query.filter("age", Op::Gt, 18).or_not_filter("status", Op::Eq, "inactive")
// SQL: AND "age" > 18 OR NOT "status" = 'inactive'
Source

pub fn between<V>(self, col: &'static str, start: V, end: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a BETWEEN clause to the query.

§Arguments
  • col - The column name
  • start - The start value of the range
  • end - The end value of the range
§Example
query.between("age", 18, 30)
// SQL: AND "age" BETWEEN 18 AND 30
Source

pub fn or_between<V>(self, col: &'static str, start: V, end: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an OR BETWEEN clause to the query.

§Arguments
  • col - The column name
  • start - The start value of the range
  • end - The end value of the range
§Example
query.between("age", 18, 30).or_between("salary", 5000, 10000)
// SQL: AND "age" BETWEEN 18 AND 30 OR "salary" BETWEEN 5000 AND 10000
Source

pub fn in_list<V>(self, col: &'static str, values: Vec<V>) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an IN list clause to the query.

§Arguments
  • col - The column name
  • values - A vector of values
§Example
query.in_list("status", vec!["active", "pending"])
// SQL: AND "status" IN ('active', 'pending')
Source

pub fn or_in_list<V>(self, col: &'static str, values: Vec<V>) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an OR IN list clause to the query.

Source

pub fn group<F>(self, f: F) -> Self
where F: FnOnce(Self) -> Self,

Groups filters inside parentheses with an AND operator.

This allows for constructing complex WHERE clauses with nested logic.

§Arguments
  • f - A closure that receives a QueryBuilder and returns it with more filters
§Example
db.model::<User>()
    .filter("active", Op::Eq, true)
    .group(|q| q.filter("age", Op::Gt, 18).or_filter("role", Op::Eq, "admin"))
    .scan()
    .await?;
// SQL: AND "active" = true AND (1=1 AND ("age" > 18 OR "role" = 'admin'))
Source

pub fn or_group<F>(self, f: F) -> Self
where F: FnOnce(Self) -> Self,

Groups filters inside parentheses with an OR operator.

§Arguments
  • f - A closure that receives a QueryBuilder and returns it with more filters
§Example
db.model::<User>()
    .filter("active", Op::Eq, true)
    .or_group(|q| q.filter("role", Op::Eq, "admin").filter("age", Op::Gt, 18))
    .scan()
    .await?;
// SQL: AND "active" = true OR (1=1 AND ("role" = 'admin' AND "age" > 18))
Source

pub fn where_raw<V>(self, sql: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw WHERE clause with a placeholder and a single value.

This allows writing raw SQL conditions with a ? placeholder. To use multiple placeholders with different types, chain multiple where_raw calls.

§Arguments
  • sql - Raw SQL string with one ? placeholder (e.g., “age > ?”)
  • value - Value to bind
§Example
db.model::<User>()
    .where_raw("name = ?", "Alice".to_string())
    .where_raw("age >= ?", 18)
    .scan()
    .await?;
// SQL: AND name = 'Alice' AND age >= 18
Source

pub fn or_where_raw<V>(self, sql: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw OR WHERE clause with a placeholder.

Source

pub fn equals<V>(self, col: &'static str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds an equality filter to the query.

This is a convenience wrapper around filter() for simple equality checks. It is equivalent to calling filter(col, "=", value).

§Type Parameters
  • V - The type of the value to compare against.
§Arguments
  • col - The column name to filter on.
  • value - The value to match.
§Example
// Equivalent to filter("age", Op::Eq, 18)
query.equals("age", 18)
Source

pub fn order(self, order: &str) -> Self

Adds an ORDER BY clause to the query.

Specifies the sort order for the query results. Multiple order clauses can be added and will be applied in the order they were added.

§Arguments
  • order - The ORDER BY expression (e.g., “created_at DESC”, “age ASC, name DESC”)
§Example
// Single column ascending (ASC is default)
query.order("age")

// Single column descending
query.order("created_at DESC")

// Multiple columns
query.order("age DESC, username ASC")

// Chain multiple order clauses
query
    .order("priority DESC")
    .order("created_at ASC")
Source

pub fn alias(self, alias: &str) -> Self

Defines a SQL alias for the primary table in the query.

This method allows you to set a short alias for the model’s underlying table. It is highly recommended when writing complex queries with multiple JOIN clauses, preventing the need to repeat the full table name in .filter(), .equals(), or .select().

§Arguments
  • alias - A string slice representing the alias to be used (e.g., “u”, “rp”).
§Example
// Using 'u' as an alias for the User table
let results = db.model::<User>()
    .alias("u")
    .join("role_permissions rp", "rp.role_id = u.role")
    .equals("u.id", user_id)
    .select("u.username, rp.permission_id")
    .scan_as::<UserPermissionDTO>()
    .await?;
Source

pub fn debug(self) -> Self

Placeholder for eager loading relationships (preload).

This method is reserved for future implementation of relationship preloading. Currently, it returns self unchanged to maintain the fluent interface.

§Future Implementation

Will support eager loading of related models to avoid N+1 query problems:

// Future usage example
query.preload("posts").preload("comments")

Activates debug mode for this query.

When enabled, the generated SQL query will be logged using the log crate at the DEBUG level before execution.

§Note

To see the output, you must initialize a logger in your application (e.g., using env_logger) and configure it to display debug logs for bottle_orm.

§Example
db.model::<User>()
    .filter("active", "=", true)
    .debug() // Logs SQL: SELECT * FROM "user" WHERE "active" = $1
    .scan()
    .await?;
Source

pub fn is_null(self, col: &str) -> Self

Adds an IS NULL filter for the specified column.

§Arguments
  • col - The column name to check for NULL
§Example
db.model::<User>()
    .is_null("deleted_at")
    .scan()
    .await?;
// SQL: SELECT * FROM "user" WHERE "deleted_at" IS NULL
Source

pub fn is_not_null(self, col: &str) -> Self

Adds an IS NOT NULL filter for the specified column.

§Arguments
  • col - The column name to check for NOT NULL
§Example
db.model::<User>()
    .is_not_null("email")
    .scan()
    .await?;
// SQL: SELECT * FROM "user" WHERE "email" IS NOT NULL
Source

pub fn with_deleted(self) -> Self

Includes soft-deleted records in query results.

By default, queries on models with a #[orm(soft_delete)] column exclude records where that column is not NULL. This method disables that filter.

§Example
// Get all users including deleted ones
db.model::<User>()
    .with_deleted()
    .scan()
    .await?;
Source

pub fn join(self, table: &str, s_query: &str) -> Self

Adds an INNER JOIN clause to the query.

§Arguments
  • table - The name of the table to join (with optional alias)
  • on - The join condition (e.g., “users.id = posts.user_id”)
§Example
db.model::<User>()
    .join("posts p", "u.id = p.user_id")
    .scan()
    .await?;
// SQL: INNER JOIN "posts" p ON u.id = p.user_id
Source

pub fn join_raw<V>(self, table: &str, on: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a JOIN clause with a placeholder and a bound value.

§Arguments
  • table - The name of the table to join
  • on - The join condition with a ? placeholder
  • value - The value to bind
§Example
db.model::<User>()
    .join_raw("posts p", "p.user_id = u.id AND p.status = ?", "published")
    .scan()
    .await?;
// SQL: JOIN "posts" p ON p.user_id = u.id AND p.status = 'published'
Source

pub fn left_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw LEFT JOIN clause with a placeholder and a bound value.

§Example
query.left_join_raw("posts", "posts.user_id = ?", user_id)
Source

pub fn right_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw RIGHT JOIN clause with a placeholder and a bound value.

§Example
query.right_join_raw("users", "users.id = ?", user_id)
Source

pub fn inner_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw INNER JOIN clause with a placeholder and a bound value.

§Example
query.inner_join_raw("accounts", "accounts.user_id = ?", user_id)
Source

pub fn full_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a raw FULL JOIN clause with a placeholder and a bound value.

§Example
query.full_join_raw("profiles", "profiles.user_id = ?", user_id)
Source

pub fn left_join(self, table: &str, on: &str) -> Self

Adds a LEFT JOIN clause.

§Arguments
  • table - The name of the table to join with
  • on - The join condition (e.g., “users.id = posts.user_id”)
§Example
// Get all users and their posts (if any)
let users_with_posts = db.model::<User>()
    .left_join("posts p", "u.id = p.user_id")
    .scan()
    .await?;
// SQL: LEFT JOIN "posts" p ON u.id = p.user_id
Source

pub fn right_join(self, table: &str, on: &str) -> Self

Adds a RIGHT JOIN clause.

§Arguments
  • table - The name of the table to join with
  • on - The join condition
§Example
db.model::<Post>()
    .right_join("users u", "p.user_id = u.id")
    .scan()
    .await?;
// SQL: RIGHT JOIN "users" u ON p.user_id = u.id
Source

pub fn inner_join(self, table: &str, on: &str) -> Self

Adds an INNER JOIN clause.

§Arguments
  • table - The name of the table to join with
  • on - The join condition
§Example
// Get only users who have posts
let active_users = db.model::<User>()
    .inner_join("posts p", "u.id = p.user_id")
    .scan()
    .await?;
// SQL: INNER JOIN "posts" p ON u.id = p.user_id
Source

pub fn full_join(self, table: &str, on: &str) -> Self

Adds a FULL JOIN clause.

§Arguments
  • table - The name of the table to join with
  • on - The join condition
§Example
query.full_join("profiles pr", "u.id = pr.user_id")
// SQL: FULL JOIN "profiles" pr ON u.id = pr.user_id
Source

pub fn distinct(self) -> Self

Marks the query to return DISTINCT results.

Adds the DISTINCT keyword to the SELECT statement, ensuring that unique rows are returned.

§Example
// Get unique ages of users
let unique_ages: Vec<i32> = db.model::<User>()
    .select("age")
    .distinct()
    .scan()
    .await?;
Source

pub fn group_by(self, columns: &str) -> Self

Adds a GROUP BY clause to the query.

Groups rows that have the same values into summary rows. Often used with aggregate functions (COUNT, MAX, MIN, SUM, AVG).

§Arguments
  • columns - Comma-separated list of columns to group by
§Example
// Count users by age group
let stats: Vec<(i32, i64)> = db.model::<User>()
    .select("age, COUNT(*)")
    .group_by("age")
    .scan()
    .await?;
Source

pub fn having<V>(self, col: &'static str, op: Op, value: V) -> Self
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Adds a HAVING clause to the query.

Used to filter groups created by group_by. Similar to filter (WHERE), but operates on grouped records and aggregate functions.

§Arguments
  • col - The column or aggregate function to filter on
  • op - Comparison operator
  • value - Value to compare against
§Example
// Get ages with more than 5 users
let popular_ages = db.model::<User>()
    .select("age, COUNT(*)")
    .group_by("age")
    .having("COUNT(*)", Op::Gt, 5)
    .scan()
    .await?;
Source

pub async fn count(self) -> Result<i64, Error>

Returns the COUNT of rows matching the query.

A convenience method that automatically sets SELECT COUNT(*) and returns the result as an i64.

§Returns
  • Ok(i64) - The count of rows
  • Err(sqlx::Error) - Database error
§Example
let user_count = db.model::<User>().count().await?;
Source

pub async fn sum<N>(self, column: &str) -> Result<N, Error>
where N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,

Returns the SUM of the specified column.

Calculates the sum of a numeric column.

§Arguments
  • column - The column to sum
§Example
let total_age: i64 = db.model::<User>().sum("age").await?;
Source

pub async fn avg<N>(self, column: &str) -> Result<N, Error>
where N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,

Returns the AVG of the specified column.

Calculates the average value of a numeric column.

§Arguments
  • column - The column to average
§Example
let avg_age: f64 = db.model::<User>().avg("age").await?;
Source

pub async fn min<N>(self, column: &str) -> Result<N, Error>
where N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,

Returns the MIN of the specified column.

Finds the minimum value in a column.

§Arguments
  • column - The column to check
§Example
let min_age: i32 = db.model::<User>().min("age").await?;
Source

pub async fn max<N>(self, column: &str) -> Result<N, Error>
where N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,

Returns the MAX of the specified column.

Finds the maximum value in a column.

§Arguments
  • column - The column to check
§Example
let max_age: i32 = db.model::<User>().max("age").await?;
Source

pub fn pagination( self, max_value: usize, default: usize, page: usize, value: isize, ) -> Result<Self, Error>

Applies pagination with validation and limits.

This is a convenience method that combines limit() and offset() with built-in validation and maximum value enforcement for safer pagination.

§Arguments
  • max_value - Maximum allowed items per page
  • default - Default value if value exceeds max_value
  • page - Zero-based page number
  • value - Requested items per page
§Returns
  • Ok(Self) - The updated QueryBuilder with pagination applied
  • Err(Error) - If value is negative
§Pagination Logic
  1. Validates that value is non-negative
  2. If value > max_value, uses default instead
  3. Calculates offset as: value * page
  4. Sets limit to value
§Example
// Page 0 with 10 items (page 1 in 1-indexed systems)
query.pagination(100, 20, 0, 10)?  // LIMIT 10 OFFSET 0

// Page 2 with 25 items (page 3 in 1-indexed systems)
query.pagination(100, 20, 2, 25)?  // LIMIT 25 OFFSET 50

// Request too many items, falls back to default
query.pagination(100, 20, 0, 150)? // LIMIT 20 OFFSET 0 (150 > 100)

// Error: negative value
query.pagination(100, 20, 0, -10)? // Returns Error
Source

pub fn select(self, columns: &str) -> Self

Selects specific columns to return.

By default, queries use SELECT * to return all columns. This method allows you to specify exactly which columns should be returned.

Note: Columns are pushed exactly as provided, without automatic snake_case conversion, allowing for aliases and raw SQL fragments.

§Arguments
  • columns - Comma-separated list of column names to select
§Example
// Select single column
query.select("id")

// Select multiple columns
query.select("id, username, email")

// Select with SQL functions and aliases (now supported)
query.select("COUNT(*) as total_count")
Source

pub fn omit(self, columns: &str) -> Self

Excludes specific columns from the query results.

This is the inverse of select(). Instead of specifying which columns to include, you specify which columns to exclude. All other columns will be returned.

§Arguments
  • columns - Comma-separated list of column names to exclude
§Priority

If both select() and omit() are used, select() takes priority.

§Example
// Exclude password from results
let user = db.model::<User>()
    .omit("password")
    .first()
    .await?;

// Exclude multiple fields
let user = db.model::<User>()
    .omit("password, secret_token")
    .first()
    .await?;

// Using with generated field constants (autocomplete support)
let user = db.model::<User>()
    .omit(user_fields::PASSWORD)
    .first()
    .await?;
Source

pub fn offset(self, offset: usize) -> Self

Sets the query offset (pagination).

Specifies the number of rows to skip before starting to return rows. Commonly used in combination with limit() for pagination.

§Arguments
  • offset - Number of rows to skip
§Example
// Skip first 20 rows
query.offset(20)

// Pagination: page 3 with 10 items per page
query.limit(10).offset(20)  // Skip 2 pages = 20 items
Source

pub fn limit(self, limit: usize) -> Self

Sets the maximum number of records to return.

Limits the number of rows returned by the query. Essential for pagination and preventing accidentally fetching large result sets.

§Arguments
  • limit - Maximum number of rows to return
§Example
// Return at most 10 rows
query.limit(10)

// Pagination: 50 items per page
query.limit(50).offset(page * 50)
Source

pub fn insert<'b>( &'b mut self, model: &'b T, ) -> BoxFuture<'b, Result<(), Error>>

Inserts a new record into the database based on the model instance.

This method serializes the model into a SQL INSERT statement with proper type handling for primitives, dates, UUIDs, and other supported types.

§Type Binding Strategy

The method uses string parsing as a temporary solution for type binding. Values are converted to strings via the model’s to_map() method, then parsed back to their original types for proper SQL binding.

§Supported Types for Insert
  • Integers: i32, i64 (INTEGER, BIGINT)
  • Boolean: bool (BOOLEAN)
  • Float: f64 (DOUBLE PRECISION)
  • Text: String (TEXT, VARCHAR)
  • UUID: Uuid (UUID) - All versions 1-7 supported
  • DateTime: DateTime<Utc> (TIMESTAMPTZ)
  • NaiveDateTime: (TIMESTAMP)
  • NaiveDate: (DATE)
  • NaiveTime: (TIME)
§Arguments
  • model - Reference to the model instance to insert
§Returns
  • Ok(&Self) - Reference to self for method chaining
  • Err(sqlx::Error) - Database error during insertion
§Example
 
use chrono::Utc;

let new_user = User {
    id: Uuid::new_v4(),
    username: "john_doe".to_string(),
    email: "john@example.com".to_string(),
    age: 25,
    active: true,
    created_at: Utc::now(),
};

db.model::<User>().insert(&new_user).await?;
Source

pub fn batch_insert<'b>( &'b mut self, models: &'b [T], ) -> BoxFuture<'b, Result<(), Error>>

Inserts multiple records into the database in a single batch operation.

This is significantly faster than performing individual inserts in a loop as it generates a single SQL statement with multiple VALUES groups.

§Type Binding Strategy

Similar to the single record insert, this method uses string parsing for type binding. It ensures that all columns defined in the model are included in the insert statement, providing NULL for any missing optional values.

§Arguments
  • models - A slice of model instances to insert
§Returns
  • Ok(()) - Successfully inserted all records
  • Err(sqlx::Error) - Database error during insertion
§Example
let users = vec![
    User { username: "alice".to_string(), ... },
    User { username: "bob".to_string(), ... },
];

db.model::<User>().batch_insert(&users).await?;
Source

pub fn upsert<'b>( &'b mut self, model: &'b T, conflict_columns: &'b [&'b str], update_columns: &'b [&'b str], ) -> BoxFuture<'b, Result<u64, Error>>

Inserts a record or updates it if a conflict occurs (UPSERT).

This method provides a cross-database way to perform “Insert or Update” operations. It uses ON CONFLICT for PostgreSQL and SQLite, and ON DUPLICATE KEY UPDATE for MySQL.

§Arguments
  • model - The model instance to insert or update
  • conflict_columns - Columns that trigger the conflict (e.g., primary key or unique columns)
  • update_columns - Columns to update when a conflict occurs
§Returns
  • Ok(u64) - The number of rows affected
  • Err(sqlx::Error) - Database error
§Example
let user = User { id: 1, username: "alice".to_string(), age: 25 };

// If id 1 exists, update username and age
db.model::<User>().upsert(&user, &["id"], &["username", "age"]).await?;
Source

pub fn to_sql(&self) -> String

Returns the generated SQL string for debugging purposes.

This method constructs the SQL query string without executing it. Useful for debugging and logging query construction. Note that this shows placeholders (?, $1, etc.) rather than actual bound values.

§Returns

A String containing the SQL query that would be executed

§Example
let query = db.model::<User>()
    .filter("age", ">=", 18)
    .order("created_at DESC")
    .limit(10);

println!("SQL: {}", query.to_sql());
// Output: SELECT * FROM "user" WHERE 1=1 AND "age" >= $1 ORDER BY created_at DESC
Source

pub async fn scan<R>(self) -> Result<Vec<R>, Error>
where R: FromAnyRow + AnyImpl + Send + Unpin,

Executes the query and returns a list of results.

This method builds and executes a SELECT query with all accumulated filters, ordering, and pagination settings. It returns all matching rows as a vector.

§Type Parameters
  • R - The result type. Must implement FromAnyRow and AnyImpl.
§Returns
  • Ok(Vec<R>) - Vector of results (empty if no matches)
  • Err(sqlx::Error) - Database error during query execution
§Example
let users: Vec<User> = db.model::<User>()
    .filter("age", Op::Gte, 18)
    .scan()
    .await?;
// SQL: SELECT * FROM "user" WHERE "age" >= 18
Source

pub async fn scan_as<R>(self) -> Result<Vec<R>, Error>
where R: FromAnyRow + AnyImpl + Send + Unpin,

Executes the query and maps the result to a custom DTO.

Useful for queries that return only a subset of columns or join multiple tables.

§Type Parameters
  • R - The DTO type. Must implement FromAnyRow and AnyImpl.
§Returns
  • Ok(Vec<R>) - Vector of results
  • Err(sqlx::Error) - Database error
§Example
let dtos: Vec<UserStats> = db.model::<User>()
    .select("username, age")
    .scan_as::<UserStats>()
    .await?;
// SQL: SELECT "username", "age" FROM "user"
Source

pub async fn first<R>(self) -> Result<R, Error>
where R: FromAnyRow + AnyImpl + Send + Unpin,

Executes the query and returns only the first result.

Automatically applies LIMIT 1 if no limit is set.

§Type Parameters
  • R - The result type. Must implement FromAnyRow and AnyImpl.
§Returns
  • Ok(R) - The first matching record
  • Err(sqlx::Error::RowNotFound) - If no records match
§Example
let user: User = db.model::<User>()
    .filter("id", Op::Eq, 1)
    .first()
    .await?;
// SQL: SELECT * FROM "user" WHERE "id" = 1 LIMIT 1
Source

pub async fn scalar<O>(self) -> Result<O, Error>
where O: FromAnyRow + AnyImpl + Send + Unpin,

Executes the query and returns a single scalar value.

This method is useful for fetching single values like counts, max/min values, or specific columns without mapping to a struct or tuple.

§Type Parameters
  • O - The output type. Must implement FromAnyRow, AnyImpl, Send and Unpin.
§Example
// Get count of users
let count: i64 = db.model::<User>()
    .select("count(*)")
    .scalar()
    .await?;

// Get specific field
let username: String = db.model::<User>()
    .filter("id", "=", 1)
    .select("username")
    .scalar()
    .await?;
Source

pub fn update<'b, V>( &'b mut self, col: &str, value: V, ) -> BoxFuture<'b, Result<u64, Error>>
where V: ToUpdateValue + Send + Sync,

Updates a single column in the database.

§Arguments
  • col - The column name to update
  • value - The new value
§Returns
  • Ok(u64) - The number of rows affected
Source

pub fn updates<'b>(&'b mut self, model: &T) -> BoxFuture<'b, Result<u64, Error>>

Updates all columns based on the model instance.

This method updates all active columns of the table with values from the provided model.

§Arguments
  • model - The model instance containing new values
§Returns
  • Ok(u64) - The number of rows affected
Source

pub fn update_partial<'b, P: AnyImpl>( &'b mut self, partial: &P, ) -> BoxFuture<'b, Result<u64, Error>>

Updates columns based on a partial model (struct implementing AnyImpl).

This allows updating a subset of columns using a custom struct. The struct must implement AnyImpl (usually via #[derive(FromAnyRow)]).

§Arguments
  • partial - The partial model containing new values
§Returns
  • Ok(u64) - The number of rows affected
Source

pub fn update_raw<'b, V>( &'b mut self, col: &str, expr: &str, value: V, ) -> BoxFuture<'b, Result<u64, Error>>
where V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,

Updates a column using a raw SQL expression.

This allows for complex updates like incrementing values or using database functions. You can use a ? placeholder in the expression and provide a value to bind.

§Arguments
  • col - The column name to update
  • expr - The raw SQL expression (e.g., “age + 1” or “age + ?”)
  • value - The value to bind for the placeholder
§Example
// Increment age by 1
db.model::<User>()
    .filter("id", "=", 1)
    .update_raw("age", "age + 1", 0)
    .await?;

// Increment age by a variable
db.model::<User>()
    .filter("id", "=", 1)
    .update_raw("age", "age + ?", 5)
    .await?;
Source

pub async fn delete(self) -> Result<u64, Error>

Executes a DELETE query based on the current filters.

Performs a soft delete if the model has a soft delete column, otherwise performs a permanent hard delete.

§Returns
  • Ok(u64) - The number of rows deleted (or soft-deleted)
  • Err(sqlx::Error) - Database error
§Example
db.model::<User>()
    .filter("id", Op::Eq, 1)
    .delete()
    .await?;
// SQL (Soft): UPDATE "user" SET "deleted_at" = NOW() WHERE "id" = 1
// SQL (Hard): DELETE FROM "user" WHERE "id" = 1
Source

pub async fn hard_delete(self) -> Result<u64, Error>

Permanently removes records from the database.

§Returns
  • Ok(u64) - The number of rows deleted
  • Err(sqlx::Error) - Database error
§Example
db.model::<User>()
    .filter("id", Op::Eq, 1)
    .hard_delete()
    .await?;
// SQL: DELETE FROM "user" WHERE "id" = 1

Auto Trait Implementations§

§

impl<T, E> Freeze for QueryBuilder<T, E>
where E: Freeze,

§

impl<T, E> !RefUnwindSafe for QueryBuilder<T, E>

§

impl<T, E> Send for QueryBuilder<T, E>
where E: Send, T: Send,

§

impl<T, E> Sync for QueryBuilder<T, E>
where E: Sync, T: Sync,

§

impl<T, E> Unpin for QueryBuilder<T, E>
where E: Unpin, T: Unpin,

§

impl<T, E> UnsafeUnpin for QueryBuilder<T, E>
where E: UnsafeUnpin,

§

impl<T, E> !UnwindSafe for QueryBuilder<T, E>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more