Skip to main content

QueryBuilder

Struct QueryBuilder 

Source
pub struct QueryBuilder<M: Model> { /* private fields */ }
Expand description

用于构造 SQL 查询的查询构造器

Implementations§

Source§

impl<M: Model> QueryBuilder<M>

Source

pub fn new(dialect: Box<dyn Dialect>) -> Self

Source

pub fn table(self, table: impl Into<String>) -> Self

Source

pub fn without_soft_delete(self) -> Self

P0-1:临时禁用软删除过滤,用于查询已删除的记录。

等价于 SeaORM 的 Entity::find().filter(Column::DeletedAt.is_not_null()) 或 Laravel Eloquent 的 Model::withTrashed()

§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;

// 查询包含已软删除的用户
let sql = QueryBuilder::<User>::new(Box::new(MySqlDialect))
    .table("users")
    .without_soft_delete()
    .build_select();
// 不会自动追加 WHERE deleted_at IS NULL
Source

pub fn is_soft_delete_disabled(&self) -> bool

P0-1:返回软删除过滤是否被禁用

Source

pub fn with_tenant_id(self, tenant_id: i64) -> Self

P0-3:设置当前租户 ID,启用多租户自动过滤。

M::tenant_field() 返回 Some(field) 时,QueryBuilder 会在以下场景 自动追加 WHERE {field} = ?(参数化,值通过 params 绑定):

  • build_select / build_select_with_params
  • build_count / build_exists / build_max / build_min / build_sum / build_avg
  • build_update / build_update_with_params(防止跨租户更新)
  • build_delete / build_delete_with_params(防止跨租户删除)

使用 without_tenant() 可临时禁用租户过滤(用于跨租户管理查询)。

§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;

let (sql, params) = QueryBuilder::<Order>::new(Box::new(MySqlDialect))
    .table("orders")
    .with_tenant_id(42)
    .build_select_with_params();
// sql => "SELECT * FROM `orders` WHERE `tenant_id` = ?"
// params => [Value::I64(42)]
Source

pub fn without_tenant(self) -> Self

P0-3:临时禁用租户过滤,用于跨租户管理查询。

等价于 Laravel Eloquent 的全局作用域禁用。

Source

pub fn is_tenant_disabled(&self) -> bool

P0-3:返回租户过滤是否被禁用

Source

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

设置 SELECT 列。

M-3 安全警告:本方法直接拼接 columns 到 SQL,进行标识符校验或 quote。 调用方必须确保 columns 来自可信来源(硬编码或经 sql_safety::validate_identifier 校验)。若列名可能来自不可信输入,请使用 QueryBuilder::select_quoted

本方法保留原行为以兼容复杂表达式(如 COUNT(*)users.id AS uid)。

Source

pub fn select_quoted(self, columns: Vec<&str>) -> Result<Self, DbError>

M-3 修复:安全的 SELECT 列设置,自动校验每个列名并 quote。

每个 column 必须通过 sql_safety::validate_identifier 校验 (仅允许 ASCII 字母数字 + 下划线,不以数字开头,长度 1-63)。 校验失败时返回 DbError::InvalidInput

对于复杂表达式(如 COUNT(*)users.id AS uid),请使用 QueryBuilder::select 并自行确保安全。

Source

pub fn where_cond(self, condition: impl Into<String>) -> Self

👎Deprecated since 1.3.0:

P0-2: 字符串拼接存在 SQL 注入风险,请使用 where_eq/where_ne/where_gt/where_lt/where_like 等参数化方法

添加原始字符串 WHERE 条件(AND 关系)。

⚠️ P0-2 安全警告(v1.3.0+):本方法直接拼接 condition 到 SQL, 存在 SQL 注入风险。仅在以下场景使用:

  • 条件来自硬编码字符串(如 where_cond("age > 18")
  • 条件含复杂表达式(如 where_cond("status = 'active' AND role = 'admin'")

禁止将用户输入拼接到 condition 中。若值来自不可信来源, 必须使用参数化方法:where_eq / where_ne / where_gt / where_lt / where_like

§推荐迁移
// ❌ 危险:字符串拼接
builder.where_cond(format!("name = '{}'", user_input));

// ✅ 安全:参数化绑定
builder.where_eq("name", Value::String(user_input.to_string()));
Source

pub fn or_where(self, condition: impl Into<String>) -> Self

👎Deprecated since 1.3.0:

P0-2: 字符串拼接存在 SQL 注入风险,请使用参数化方法

添加原始字符串 WHERE 条件(OR 关系)。

⚠️ P0-2 安全警告:同 where_cond,存在注入风险。

Source

pub fn where_eq(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化等值条件 field = ?(AND 关系)。

值通过 ? 占位符绑定,杜绝 SQL 注入。

§示例
use sz_orm_core::Value;

builder
    .where_eq("status", Value::String("active".into()))
    .where_eq("tenant_id", Value::I64(42));
Source

pub fn where_ne(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化不等条件 field != ?(AND 关系)。

Source

pub fn where_gt(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化大于条件 field > ?(AND 关系)。

Source

pub fn where_ge(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化大于等于条件 field >= ?(AND 关系)。

Source

pub fn where_lt(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化小于条件 field < ?(AND 关系)。

Source

pub fn where_le(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化小于等于条件 field <= ?(AND 关系)。

Source

pub fn where_like(self, field: impl Into<String>, pattern: Value) -> Self

P0-2:参数化 LIKE 条件 field LIKE ?(AND 关系)。

调用方负责在 pattern 中包含 % 通配符。

§示例
use sz_orm_core::Value;

builder.where_like("name", Value::String("%alice%".into()));
Source

pub fn or_where_eq(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 等值条件 OR field = ?

值通过 ? 占位符绑定,杜绝 SQL 注入。OR 条件会与相邻的 OR 条件组合成 (cond1 OR cond2) 形式。

Source

pub fn or_where_ne(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 不等条件 OR field != ?

Source

pub fn or_where_gt(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 大于条件 OR field > ?

Source

pub fn or_where_ge(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 大于等于条件 OR field >= ?

Source

pub fn or_where_lt(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 小于条件 OR field < ?

Source

pub fn or_where_le(self, field: impl Into<String>, value: Value) -> Self

P0-2:参数化 OR 小于等于条件 OR field <= ?

Source

pub fn or_where_like(self, field: impl Into<String>, pattern: Value) -> Self

P0-2:参数化 OR LIKE 条件 OR field LIKE ?

Source

pub fn where_in(self, field: impl Into<String>, values: Vec<Value>) -> Self

Source

pub fn where_not_in(self, field: impl Into<String>, values: Vec<Value>) -> Self

Source

pub fn where_between( self, field: impl Into<String>, start: Value, end: Value, ) -> Self

Source

pub fn where_not_between( self, field: impl Into<String>, start: Value, end: Value, ) -> Self

Source

pub fn where_null(self, field: impl Into<String>) -> Self

Source

pub fn where_not_null(self, field: impl Into<String>) -> Self

Source

pub fn order_by(self, field: impl Into<String>) -> Self

Source

pub fn order_desc(self, field: impl Into<String>) -> Self

Source

pub fn group_by(self, field: impl Into<String>) -> Self

Source

pub fn having(self, condition: impl Into<String>) -> Self

Source

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

Source

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

Source

pub fn page(self, page: usize, page_size: usize) -> Self

Source

pub fn keyset_after( self, field: impl Into<String>, cursor_value: Value, page_size: usize, ) -> Self

P2-5:基于游标的 Keyset 分页 — 查询指定字段值之后的记录(下一页)

生成 WHERE {field} > ? ORDER BY {field} ASC LIMIT {page_size}。 适用于按主键或时间戳递增遍历大表的场景,性能不受数据插入/删除影响。

§参数
  • field:排序字段(通常是主键或索引列,如 idcreated_at
  • cursor_value:当前页最后一条记录的该字段值
  • page_size:每页大小
§示例
use sz_orm_core::{QueryBuilder, DbType, dialect::get_dialect, Value};
let dialect = get_dialect(DbType::MySQL).unwrap();
let builder = QueryBuilder::<User>::new(dialect)
    .keyset_after("id", Value::I64(100), 20);
let (sql, params) = builder.build_select_with_params();
// 方言会引用字段名(如 MySQL 的 `id`),去除引号后检查
let sql_clean = sql.replace('`', "").replace('"', "");
assert!(sql_clean.contains("id > ?"));
assert!(sql.to_uppercase().contains("ORDER BY"));
assert!(sql.to_uppercase().contains("ASC"));
assert!(sql.contains("LIMIT 20"));
assert_eq!(params, vec![Value::I64(100)]);
Source

pub fn keyset_before( self, field: impl Into<String>, cursor_value: Value, page_size: usize, ) -> Self

P2-5:基于游标的 Keyset 分页 — 查询指定字段值之前的记录(上一页)

生成 WHERE {field} < ? ORDER BY {field} DESC LIMIT {page_size}。 适用于反向遍历场景。

§参数
  • field:排序字段
  • cursor_value:当前页第一条记录的该字段值
  • page_size:每页大小
§示例
use sz_orm_core::{QueryBuilder, DbType, dialect::get_dialect, Value};
let dialect = get_dialect(DbType::MySQL).unwrap();
let builder = QueryBuilder::<User>::new(dialect)
    .keyset_before("id", Value::I64(100), 20);
let (sql, params) = builder.build_select_with_params();
// 方言会引用字段名(如 MySQL 的 `id`),去除引号后检查
let sql_clean = sql.replace('`', "").replace('"', "");
assert!(sql_clean.contains("id < ?"));
assert!(sql.to_uppercase().contains("ORDER BY"));
assert!(sql.to_uppercase().contains("DESC"));
assert!(sql.contains("LIMIT 20"));
assert_eq!(params, vec![Value::I64(100)]);
Source

pub fn join_inner( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self

Source

pub fn join_left( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self

Source

pub fn join_right( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self

Source

pub fn build_select(&self) -> String

构建 SELECT SQL 语句

L-5 修复:补充示例文档

根据 tableselect_columnswhere_conditionsjoinsorder_bygroup_byhavinglimitoffset 等条件拼装最终 SQL。 若未通过 table() 指定表名,则使用 M::table_name()

§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;
use sz_orm_core::model::Model;

#[derive(Default)]
struct User;
impl Model for User {
    type PrimaryKey = i64;
    fn table_name() -> &'static str { "users" }
    fn pk(&self) -> Self::PrimaryKey { 0 }
    fn set_pk(&mut self, _: Self::PrimaryKey) {}
}

let sql = QueryBuilder::<User>::new(Box::new(MySqlDialect))
    .select(vec!["id", "name"])
    .where_cond("age > 18")
    .order_by("id DESC")
    .limit(10)
    .build_select();
// sql => "SELECT id, name FROM `users` WHERE age > 18 ORDER BY id DESC LIMIT 10"
Source

pub fn build_insert(&self, data: &HashMap<String, Value>) -> String

Source

pub fn build_update(&self, data: &HashMap<String, Value>) -> String

Source

pub fn build_delete(&self) -> String

构建 DELETE SQL 语句。

P0-1 软删除集成(v1.3.0+):当 M: Model 实现了 soft_delete_field() 返回 Some(field) 且未调用 without_soft_delete() 时,本方法自动生成 UPDATE {table} SET {field} = NOW() WHERE ... 而非 DELETE FROM ...

这与 SeaORM 的 ActiveModelBehavior::after_delete + ActiveValue::Set 行为对齐:删除操作实际是软删除 UPDATE。

若需物理删除,请使用 build_force_delete

Source

pub fn build_force_delete(&self) -> String

构建物理 DELETE SQL 语句(绕过软删除)。

即使 Model 实现了 soft_delete_field(),也生成 DELETE FROM ..., 且不追加 WHERE deleted_at IS NULL 过滤(保留用户指定的 WHERE 条件)。 用于管理员强制清除场景。

§安全警告

物理删除不可恢复,请谨慎使用。

Source

pub fn build_select_with_params(&self) -> (String, Vec<Value>)

构建 SELECT SQL(参数绑定版本)。

WHERE 子句中的值使用 ? 占位符,值通过 params 返回。 适用于 Connection::query_with_params()

Source

pub fn build_insert_with_params( &self, data: &HashMap<String, Value>, ) -> (String, Vec<Value>)

构建 INSERT SQL(参数绑定版本)。

Source

pub fn build_batch_insert_with_params( &self, rows: &[HashMap<String, Value>], ) -> (String, Vec<Value>)

P2-6:构建批量 INSERT SQL(参数绑定版本)。

生成 INSERT INTO t (c1, c2) VALUES (?, ?), (?, ?), ... 形式的多行插入 SQL。 所有行的列必须一致(取第一行的列顺序);空行列表返回空 SQL。

L3 实现深度:使用参数化占位符 ?,所有值通过 params 绑定,杜绝 SQL 注入。

Source

pub fn build_batch_upsert_with_params( &self, rows: &[HashMap<String, Value>], conflict_columns: &[&str], update_columns: &[&str], ) -> Result<(String, Vec<Value>), DbError>

P2-6:构建批量 Upsert SQL(参数绑定版本)。

build_batch_insert_with_params 基础上追加冲突处理子句:

  • MySQL: ON DUPLICATE KEY UPDATE col=VALUES(col), ...
  • PostgreSQL/SQLite: ON CONFLICT (conflict_cols) DO UPDATE SET col=EXCLUDED.col, ...
  • Oracle/SQL Server/ClickHouse/Db2: 返回 Err(DbError::InvalidInput)(不支持)
§参数
  • rows: 批量数据行(所有行的列必须一致)
  • conflict_columns: 冲突检测列(主键/唯一键);MySQL 自动检测可传空
  • update_columns: 冲突时更新的列;空切片表示更新所有非冲突列
§返回
  • Ok((sql, params)): 生成的 SQL 和参数列表
  • Err(DbError::InvalidInput): 方言不支持 upsert 或 rows 为空

L3 实现深度

  1. SQL 下推:冲突处理由数据库执行,非内存判断
  2. 参数化:所有值通过 ? 占位符绑定,不拼接用户值
  3. 实际执行:生成标准 INSERT…ON CONFLICT/ON DUPLICATE KEY SQL
Source

pub fn build_update_with_params( &self, data: &HashMap<String, Value>, ) -> (String, Vec<Value>)

构建 UPDATE SQL(参数绑定版本)。 参数顺序:SET 参数在前,WHERE 参数在后。

Source

pub fn build_delete_with_params(&self) -> (String, Vec<Value>)

构建 DELETE SQL(参数绑定版本)。

P0-1 软删除集成(v1.3.0+):当 Model 启用软删除时,自动生成 UPDATE {table} SET {field} = NOW() WHERE ... 而非 DELETE FROM ...。 参数列表为空(NOW() 由数据库填充)。

Source

pub fn build_force_delete_with_params(&self) -> (String, Vec<Value>)

构建物理 DELETE SQL(参数绑定版本,绕过软删除)。

即使 Model 启用软删除,也生成 DELETE FROM ...,且不追加软删除过滤。

Source

pub fn build_count(&self) -> String

Source

pub fn build_exists(&self) -> String

Source

pub fn build_max(&self, field: &str) -> String

Source

pub fn build_min(&self, field: &str) -> String

Source

pub fn build_sum(&self, field: &str) -> String

Source

pub fn build_avg(&self, field: &str) -> String

Source

pub fn validate(&self) -> Result<(), Vec<SqlValidationError>>

校验生成的 SELECT SQL 语句 检查 SQL 语法、JOIN 列名、表名合法性

Source

pub fn validate_insert( &self, data: &HashMap<String, Value>, ) -> Result<(), Vec<SqlValidationError>>

校验生成的 INSERT SQL 语句 含空数据检测(EmptyInsertData 错误)

Source

pub fn validate_update( &self, data: &HashMap<String, Value>, ) -> Result<(), Vec<SqlValidationError>>

校验生成的 UPDATE SQL 语句 含空数据检测(EmptyUpdateData 错误)

Source

pub fn validate_delete(&self) -> Result<(), Vec<SqlValidationError>>

校验生成的 DELETE SQL 语句

Trait Implementations§

Source§

impl<M: Model> Debug for QueryBuilder<M>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<M> !RefUnwindSafe for QueryBuilder<M>

§

impl<M> !UnwindSafe for QueryBuilder<M>

§

impl<M> Freeze for QueryBuilder<M>

§

impl<M> Send for QueryBuilder<M>

§

impl<M> Sync for QueryBuilder<M>

§

impl<M> Unpin for QueryBuilder<M>
where M: Unpin,

§

impl<M> UnsafeUnpin for QueryBuilder<M>

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, 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<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