use std::borrow::Cow;
use std::marker::PhantomData;
use keelson_core::clause::{
Combine, ConflictClause, ConflictTarget, Cte, CteCycle, CteSearch, Fetch, HasCombines,
HasConflict, HasFetch, HasGroupBy, HasHaving, HasJoins, HasLimit, HasLocks, HasOffset,
HasOrderBy, HasReturning, HasSelectList, HasSet, HasTableRef, HasValues, HasWhere, HasWindows,
HasWith, Join, JoinKind, Lock, LockStrength, LockWait, NamedWindow, NullsPosition, OrderBy,
OrderDef, OrderDirection, SearchOrder, SetOp, TableFunctions, TableRef, Values, Window,
};
use keelson_core::expr::{Expr, IntoExpr, IntoExprList, IntoIdent};
use keelson_core::{Mod, mod_fn};
use crate::extras::{Incomplete, LateralBareName, Sample, SampledTable};
use crate::function::TableFunction;
use crate::statement::{HasExtraTables, HasTargetTable};
#[derive(Debug, Clone)]
pub struct CteChain {
cte: Cte,
}
pub fn with(name: impl Into<Cow<'static, str>>, body: impl IntoExpr) -> CteChain {
CteChain {
cte: Cte::new(name, body),
}
}
impl CteChain {
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CteChain {
self.cte.columns = columns.into_iter().map(Into::into).collect();
self
}
#[must_use]
pub fn materialized(mut self) -> CteChain {
self.cte.materialized = Some(true);
self
}
#[must_use]
pub fn not_materialized(mut self) -> CteChain {
self.cte.materialized = Some(false);
self
}
#[must_use]
pub fn search_breadth(
mut self,
set: impl Into<Cow<'static, str>>,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CteChain {
self.cte.search = CteSearch::new(SearchOrder::Breadth, columns, set);
self
}
#[must_use]
pub fn search_depth(
mut self,
set: impl Into<Cow<'static, str>>,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CteChain {
self.cte.search = CteSearch::new(SearchOrder::Depth, columns, set);
self
}
#[must_use]
pub fn cycle(
mut self,
set: impl Into<Cow<'static, str>>,
using: impl Into<Cow<'static, str>>,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CteChain {
let cycle = CteCycle::new(columns, set, using);
self.cte.cycle = CteCycle {
to: self.cte.cycle.to,
default_val: self.cte.cycle.default_val,
..cycle
};
self
}
#[must_use]
pub fn cycle_value(mut self, to: impl IntoExpr, default: impl IntoExpr) -> CteChain {
self.cte.cycle.to = Some(to.into_expr());
self.cte.cycle.default_val = Some(default.into_expr());
self
}
}
impl<Q: HasWith> Mod<Q> for CteChain {
fn apply(self, q: &mut Q) {
q.with_mut().append_cte(self.cte);
}
}
pub fn recursive<Q: HasWith>(recursive: bool) -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.with_mut().set_recursive(recursive))
}
pub fn columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
let columns = columns.into_expr_list();
mod_fn(move |q: &mut Q| q.select_list_mut().append_select(columns))
}
pub fn preload_columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
let columns = columns.into_expr_list();
mod_fn(move |q: &mut Q| q.select_list_mut().append_preload_select(columns))
}
pub trait TableSlot<Q> {
fn place(q: &mut Q, table: TableRef);
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FromSlot;
#[derive(Debug, Clone, Copy, Default)]
pub struct TargetSlot;
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtraSlot;
impl<Q: HasTableRef> TableSlot<Q> for FromSlot {
fn place(q: &mut Q, mut table: TableRef) {
table.joins.append(&mut q.table_ref_mut().joins);
*q.table_ref_mut() = table;
}
}
impl<Q: HasTargetTable> TableSlot<Q> for TargetSlot {
fn place(q: &mut Q, table: TableRef) {
*q.target_table_mut() = table;
}
}
impl<Q: HasExtraTables> TableSlot<Q> for ExtraSlot {
fn place(q: &mut Q, table: TableRef) {
q.extra_tables_mut().push(table);
}
}
#[derive(Debug, Clone)]
pub struct TableChain<S> {
table: TableRef,
sample: Option<Sample>,
slot: PhantomData<S>,
}
fn table_chain<S>(table: impl IntoExpr) -> TableChain<S> {
TableChain {
table: TableRef::new(table),
sample: None,
slot: PhantomData,
}
}
pub fn from_item(table: impl IntoExpr) -> TableChain<FromSlot> {
table_chain(table)
}
pub fn extra_from_item(table: impl IntoExpr) -> TableChain<ExtraSlot> {
table_chain(table)
}
pub fn target_table(table: impl IntoExpr) -> TableChain<TargetSlot> {
table_chain(table)
}
pub fn from_functions<F>(functions: impl IntoIterator<Item = F>) -> TableChain<FromSlot>
where
F: Into<TableFunction>,
{
let list: Vec<Expr> = functions
.into_iter()
.map(|f| f.into().into_expr())
.collect();
if list.is_empty() {
return table_chain(Expr::custom(Incomplete("the functions of a from-item")));
}
table_chain(Expr::custom(TableFunctions::new(list)))
}
impl<S> TableChain<S> {
#[must_use]
pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> TableChain<S> {
self.table.set_alias(alias);
self
}
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.table.set_columns(columns);
self
}
#[must_use]
pub fn only(mut self) -> TableChain<S> {
self.table.only = true;
self
}
#[must_use]
pub fn lateral(mut self) -> TableChain<S> {
self.table = lateral_table(self.table);
self
}
#[must_use]
pub fn with_ordinality(mut self) -> TableChain<S> {
self.table.with_ordinality = true;
self
}
#[must_use]
pub fn tablesample(
mut self,
method: impl Into<Cow<'static, str>>,
args: impl IntoExprList,
) -> TableChain<S> {
self.sample = Some(Sample {
method: method.into(),
args: args.into_expr_list(),
repeatable: None,
});
self
}
#[must_use]
pub fn repeatable(mut self, seed: impl IntoExpr) -> TableChain<S> {
if let Some(sample) = &mut self.sample {
sample.repeatable = Some(seed.into_expr());
}
self
}
}
fn lateral_table(mut table: TableRef) -> TableRef {
table.lateral = true;
if matches!(table.expression, Some(Expr::Ident(_))) {
let name = table.expression.take().expect("just matched Some");
table.expression = Some(Expr::custom(LateralBareName(name)));
}
table
}
fn finish_table(mut table: TableRef, sample: Option<Sample>) -> TableRef {
let Some(sample) = sample else {
return table;
};
let Some(expression) = table.expression.take() else {
return table;
};
table.expression = Some(Expr::custom(SampledTable {
table: expression,
alias: table.alias.take(),
columns: std::mem::take(&mut table.columns),
sample,
}));
table
}
impl<Q, S: TableSlot<Q>> Mod<Q> for TableChain<S> {
fn apply(self, q: &mut Q) {
S::place(q, finish_table(self.table, self.sample));
}
}
#[derive(Debug, Clone)]
pub struct JoinChain {
join: Join,
sample: Option<Sample>,
}
fn join_chain(kind: JoinKind, to: impl IntoExpr) -> JoinChain {
JoinChain {
join: Join::new(kind, TableRef::new(to)),
sample: None,
}
}
pub fn inner_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Inner, table)
}
pub fn left_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Left, table)
}
pub fn right_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Right, table)
}
pub fn full_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Full, table)
}
pub fn cross_join(table: impl IntoExpr) -> CrossJoinChain {
CrossJoinChain(join_chain(JoinKind::Cross, table))
}
impl JoinChain {
#[must_use]
pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> JoinChain {
self.join.to.set_alias(alias);
self
}
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.join.to.set_columns(columns);
self
}
#[must_use]
pub fn only(mut self) -> JoinChain {
self.join.to.only = true;
self
}
#[must_use]
pub fn lateral(mut self) -> JoinChain {
self.join.to = lateral_table(self.join.to);
self
}
#[must_use]
pub fn with_ordinality(mut self) -> JoinChain {
self.join.to.with_ordinality = true;
self
}
#[must_use]
pub fn tablesample(
mut self,
method: impl Into<Cow<'static, str>>,
args: impl IntoExprList,
) -> JoinChain {
self.sample = Some(Sample {
method: method.into(),
args: args.into_expr_list(),
repeatable: None,
});
self
}
#[must_use]
pub fn repeatable(mut self, seed: impl IntoExpr) -> JoinChain {
if let Some(sample) = &mut self.sample {
sample.repeatable = Some(seed.into_expr());
}
self
}
#[must_use]
pub fn natural(mut self) -> JoinChain {
self.join.natural = true;
self
}
#[must_use]
pub fn on(mut self, condition: impl IntoExpr) -> JoinChain {
self.join.append_on(condition);
self
}
#[must_use]
pub fn on_eq(self, a: impl IntoExpr, b: impl IntoExpr) -> JoinChain {
self.on(Expr::binary(a, "=", b).grouped())
}
#[must_use]
pub fn using(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.join.append_using(columns);
self
}
#[must_use]
pub fn using_alias(mut self, alias: impl Into<Cow<'static, str>>) -> JoinChain {
self.join.using_alias = Some(alias.into());
self
}
}
impl From<JoinChain> for Join {
fn from(chain: JoinChain) -> Join {
let JoinChain { mut join, sample } = chain;
join.to = finish_table(join.to, sample);
join
}
}
impl<Q: HasJoins> Mod<Q> for JoinChain {
fn apply(self, q: &mut Q) {
q.joins_mut().push(self.into());
}
}
#[derive(Debug, Clone)]
pub struct CrossJoinChain(JoinChain);
impl CrossJoinChain {
#[must_use]
pub fn as_(self, alias: impl Into<Cow<'static, str>>) -> CrossJoinChain {
CrossJoinChain(self.0.as_(alias))
}
#[must_use]
pub fn columns(
self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CrossJoinChain {
CrossJoinChain(self.0.columns(columns))
}
#[must_use]
pub fn only(self) -> CrossJoinChain {
CrossJoinChain(self.0.only())
}
#[must_use]
pub fn lateral(self) -> CrossJoinChain {
CrossJoinChain(self.0.lateral())
}
#[must_use]
pub fn with_ordinality(self) -> CrossJoinChain {
CrossJoinChain(self.0.with_ordinality())
}
#[must_use]
pub fn tablesample(
self,
method: impl Into<Cow<'static, str>>,
args: impl IntoExprList,
) -> CrossJoinChain {
CrossJoinChain(self.0.tablesample(method, args))
}
#[must_use]
pub fn repeatable(self, seed: impl IntoExpr) -> CrossJoinChain {
CrossJoinChain(self.0.repeatable(seed))
}
}
impl From<CrossJoinChain> for Join {
fn from(chain: CrossJoinChain) -> Join {
chain.0.into()
}
}
impl<Q: HasJoins> Mod<Q> for CrossJoinChain {
fn apply(self, q: &mut Q) {
self.0.apply(q);
}
}
impl TableChain<ExtraSlot> {
#[must_use]
pub fn join(mut self, join: impl Into<Join>) -> TableChain<ExtraSlot> {
self.table.joins.push(join.into());
self
}
}
pub fn where_<Q: HasWhere>(condition: impl IntoExpr) -> impl Mod<Q> {
let condition = condition.into_expr();
mod_fn(move |q: &mut Q| q.where_mut().append_where(condition))
}
pub fn where_current_of<Q: HasWhere>(cursor: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
let cursor = Expr::join((Expr::raw("CURRENT OF"), Expr::ident(cursor.into())));
mod_fn(move |q: &mut Q| q.where_mut().append_where(cursor))
}
pub fn having<Q: HasHaving>(condition: impl IntoExpr) -> impl Mod<Q> {
let condition = condition.into_expr();
mod_fn(move |q: &mut Q| q.having_mut().append_having(condition))
}
pub fn group_by<Q: HasGroupBy>(group: impl IntoExpr) -> impl Mod<Q> {
let group = group.into_expr();
mod_fn(move |q: &mut Q| q.group_by_mut().append_group(group))
}
pub fn group_by_distinct<Q: HasGroupBy>(distinct: bool) -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.group_by_mut().distinct = distinct)
}
pub fn window<Q: HasWindows>(
name: impl Into<Cow<'static, str>>,
definition: impl Mod<Window>,
) -> impl Mod<Q> {
let mut w = Window::default();
definition.apply(&mut w);
let named = NamedWindow::new(name, w);
mod_fn(move |q: &mut Q| q.windows_mut().append_window(named))
}
pub trait OrderSlot<Q> {
fn slot(q: &mut Q) -> &mut OrderBy;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DirectOrder;
#[derive(Debug, Clone, Copy, Default)]
pub struct CombinedOrder;
impl<Q: HasOrderBy> OrderSlot<Q> for DirectOrder {
fn slot(q: &mut Q) -> &mut OrderBy {
q.order_by_mut()
}
}
impl<Q: HasCombines> OrderSlot<Q> for CombinedOrder {
fn slot(q: &mut Q) -> &mut OrderBy {
&mut q.combines_mut().order_by
}
}
#[derive(Debug, Clone)]
pub struct OrderChain<S> {
def: OrderDef,
slot: PhantomData<S>,
}
pub fn order_by(expression: impl IntoExpr) -> OrderChain<DirectOrder> {
OrderChain {
def: OrderDef::new(expression),
slot: PhantomData,
}
}
pub fn order_by_combined(expression: impl IntoExpr) -> OrderChain<CombinedOrder> {
OrderChain {
def: OrderDef::new(expression),
slot: PhantomData,
}
}
impl<S> OrderChain<S> {
#[must_use]
pub fn asc(mut self) -> OrderChain<S> {
self.def.direction = Some(OrderDirection::Asc);
self
}
#[must_use]
pub fn desc(mut self) -> OrderChain<S> {
self.def.direction = Some(OrderDirection::Desc);
self
}
#[must_use]
pub fn using(mut self, operator: impl Into<Cow<'static, str>>) -> OrderChain<S> {
self.def.direction = Some(OrderDirection::Using(operator.into()));
self
}
#[must_use]
pub fn nulls_first(mut self) -> OrderChain<S> {
self.def.nulls = Some(NullsPosition::First);
self
}
#[must_use]
pub fn nulls_last(mut self) -> OrderChain<S> {
self.def.nulls = Some(NullsPosition::Last);
self
}
#[must_use]
pub fn collate(mut self, name: impl Into<Cow<'static, str>>) -> OrderChain<S> {
self.def.collation = Some(name.into());
self
}
}
impl<Q, S: OrderSlot<Q>> Mod<Q> for OrderChain<S> {
fn apply(self, q: &mut Q) {
S::slot(q).append_order(Expr::custom(self.def));
}
}
pub fn limit<Q: HasLimit>(count: impl IntoExpr) -> impl Mod<Q> {
let count = count.into_expr();
mod_fn(move |q: &mut Q| q.limit_mut().set_limit(count))
}
pub fn limit_all<Q: HasLimit>() -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.limit_mut().set_limit(Expr::raw("ALL")))
}
pub fn offset<Q: HasOffset>(start: impl IntoExpr) -> impl Mod<Q> {
let start = start.into_expr();
mod_fn(move |q: &mut Q| q.offset_mut().set_offset(start))
}
pub fn limit_combined<Q: HasCombines>(count: impl IntoExpr) -> impl Mod<Q> {
let count = count.into_expr();
mod_fn(move |q: &mut Q| q.combines_mut().limit.set_limit(count))
}
pub fn offset_combined<Q: HasCombines>(start: impl IntoExpr) -> impl Mod<Q> {
let start = start.into_expr();
mod_fn(move |q: &mut Q| q.combines_mut().offset.set_offset(start))
}
pub trait FetchSlot<Q> {
fn slot(q: &mut Q) -> &mut Fetch;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DirectFetch;
#[derive(Debug, Clone, Copy, Default)]
pub struct CombinedFetch;
impl<Q: HasFetch> FetchSlot<Q> for DirectFetch {
fn slot(q: &mut Q) -> &mut Fetch {
q.fetch_mut()
}
}
impl<Q: HasCombines> FetchSlot<Q> for CombinedFetch {
fn slot(q: &mut Q) -> &mut Fetch {
&mut q.combines_mut().fetch
}
}
#[derive(Debug, Clone)]
pub struct FetchChain<S> {
fetch: Fetch,
slot: PhantomData<S>,
}
pub fn fetch(count: impl IntoExpr) -> FetchChain<DirectFetch> {
FetchChain {
fetch: Fetch::new(count),
slot: PhantomData,
}
}
pub fn fetch_combined(count: impl IntoExpr) -> FetchChain<CombinedFetch> {
FetchChain {
fetch: Fetch::new(count),
slot: PhantomData,
}
}
impl<S> FetchChain<S> {
#[must_use]
pub fn with_ties(mut self) -> FetchChain<S> {
self.fetch.with_ties = true;
self
}
}
impl<Q, S: FetchSlot<Q>> Mod<Q> for FetchChain<S> {
fn apply(self, q: &mut Q) {
*S::slot(q) = self.fetch;
}
}
#[derive(Debug, Clone)]
pub struct LockChain {
lock: Lock,
}
pub fn for_update() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::Update),
}
}
pub fn for_no_key_update() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::NoKeyUpdate),
}
}
pub fn for_share() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::Share),
}
}
pub fn for_key_share() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::KeyShare),
}
}
impl LockChain {
#[must_use]
pub fn of(
mut self,
tables: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> LockChain {
self.lock.append_table(tables);
self
}
#[must_use]
pub fn no_wait(mut self) -> LockChain {
self.lock.wait = Some(LockWait::NoWait);
self
}
#[must_use]
pub fn skip_locked(mut self) -> LockChain {
self.lock.wait = Some(LockWait::SkipLocked);
self
}
}
impl<Q: HasLocks> Mod<Q> for LockChain {
fn apply(self, q: &mut Q) {
q.locks_mut().append_lock(self.lock);
}
}
fn combine<Q: HasCombines>(op: SetOp, all: bool, query: impl IntoExpr) -> impl Mod<Q> {
let mut c = Combine::new(op, query);
c.all = all;
mod_fn(move |q: &mut Q| q.combines_mut().append_combine(c))
}
pub fn union<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Union, false, query)
}
pub fn union_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Union, true, query)
}
pub fn intersect<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Intersect, false, query)
}
pub fn intersect_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Intersect, true, query)
}
pub fn except<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Except, false, query)
}
pub fn except_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
combine(SetOp::Except, true, query)
}
pub fn returning<Q: HasReturning>(expressions: impl IntoExprList) -> impl Mod<Q> {
let expressions = expressions.into_expr_list();
mod_fn(move |q: &mut Q| q.returning_mut().append_returnings(expressions))
}
pub fn set<Q: HasSet>(assignment: impl IntoExpr) -> impl Mod<Q> {
let assignment = assignment.into_expr();
mod_fn(move |q: &mut Q| q.set_mut().append_set(assignment))
}
#[derive(Debug, Clone)]
pub struct SetChain {
column: Expr,
}
pub fn set_col(column: impl IntoIdent) -> SetChain {
SetChain {
column: Expr::ident(column),
}
}
impl SetChain {
pub fn to<Q: HasSet>(self, value: impl IntoExpr) -> impl Mod<Q> {
set(Expr::binary(self.column, "=", value))
}
pub fn to_arg<Q: HasSet>(self, value: impl keelson_core::ToValue) -> impl Mod<Q> {
set(Expr::binary(self.column, "=", Expr::arg(value)))
}
}
pub fn set_excluded<Q: HasSet>(
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> impl Mod<Q> {
let assignments: Vec<Expr> = columns
.into_iter()
.map(Into::into)
.filter(|c: &Cow<'static, str>| !c.is_empty())
.map(|c| {
Expr::join_with(
"",
(
Expr::ident(c.clone()),
Expr::raw(" = EXCLUDED."),
Expr::ident(c),
),
)
})
.collect();
mod_fn(move |q: &mut Q| q.set_mut().append_sets(assignments))
}
pub fn values<Q: HasValues>(row: impl IntoExprList) -> impl Mod<Q> {
let row = row.into_expr_list();
mod_fn(move |q: &mut Q| q.values_mut().append_values(row))
}
pub fn rows<Q: HasValues, R: IntoExprList>(rows: impl IntoIterator<Item = R>) -> impl Mod<Q> {
let rows: Vec<Vec<Expr>> = rows.into_iter().map(IntoExprList::into_expr_list).collect();
mod_fn(move |q: &mut Q| {
let values = q.values_mut();
for row in rows {
values.append_values(row);
}
})
}
pub fn values_from_query<Q: HasValues>(query: impl IntoExpr) -> impl Mod<Q> {
let query = query.into_expr();
mod_fn(move |q: &mut Q| *q.values_mut() = Values::from_query(query))
}
#[derive(Debug, Clone)]
pub struct ConflictChain {
target: ConflictTarget,
}
pub fn on_conflict(columns: impl IntoExprList) -> ConflictChain {
ConflictChain {
target: ConflictTarget::on_columns(columns),
}
}
pub fn on_conflict_on_constraint(name: impl Into<Cow<'static, str>>) -> ConflictChain {
ConflictChain {
target: ConflictTarget::on_constraint(name),
}
}
impl ConflictChain {
#[must_use]
pub fn where_(mut self, predicate: impl IntoExpr) -> ConflictChain {
self.target.where_mut().append_where(predicate);
self
}
pub fn do_nothing(self) -> ConflictMod {
let mut clause = ConflictClause::do_nothing();
clause.target = self.target;
ConflictMod { clause }
}
pub fn do_update(self, body: impl Mod<ConflictClause>) -> ConflictMod {
let mut clause = ConflictClause::do_update();
clause.target = self.target;
body.apply(&mut clause);
ConflictMod { clause }
}
}
#[derive(Debug, Clone)]
pub struct ConflictMod {
clause: ConflictClause,
}
impl<Q: HasConflict> Mod<Q> for ConflictMod {
fn apply(self, q: &mut Q) {
q.conflict_mut().set_conflict(Expr::custom(self.clause));
}
}