use std::borrow::Cow;
use std::marker::PhantomData;
use keelson_core::clause::{
Combine, Cte, GroupByWith, HasCombines, HasGroupBy, HasHaving, HasJoins, HasLimit, HasLocks,
HasOffset, HasOrderBy, HasSelectList, HasSet, HasTableRef, HasValues, HasWhere, HasWindows,
HasWith, IndexHint, IndexHintKind, IndexHintScope, Join, JoinKind, Lock, LockStrength,
LockWait, NamedWindow, OrderBy, OrderDef, OrderDirection, Set, SetOp, TableRef, Values, Window,
};
use keelson_core::expr::{Expr, IntoExpr, IntoExprList, IntoIdent};
use keelson_core::{Expression, Mod, SqlWriter, mod_fn};
use crate::extras::{
HasDuplicateKeyUpdate, HasHints, HasModifiers, HasRowAlias, Modifier, RowAlias, row_value,
values_of,
};
use crate::statement::{HasDeleteTables, 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
}
}
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 optimizer_hint<Q: HasHints>(hint: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
let hint = hint.into();
mod_fn(move |q: &mut Q| q.hints_mut().append_hint(hint))
}
pub fn max_execution_time<Q: HasHints>(millis: u64) -> impl Mod<Q> {
optimizer_hint(format!("MAX_EXECUTION_TIME({millis})"))
}
pub fn set_var<Q: HasHints>(assignment: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
optimizer_hint(format!("SET_VAR({})", assignment.into()))
}
pub fn qb_name<Q: HasHints>(name: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
optimizer_hint(format!("QB_NAME({})", name.into()))
}
pub fn resource_group<Q: HasHints>(name: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
optimizer_hint(format!("RESOURCE_GROUP({})", name.into()))
}
fn modifier<Q: HasModifiers>(modifier: Modifier) -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.modifiers_mut().append_modifier(modifier))
}
pub fn distinct<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::Distinct)
}
pub fn distinct_row<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::DistinctRow)
}
pub fn low_priority<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::LowPriority)
}
pub fn high_priority<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::HighPriority)
}
pub fn delayed<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::Delayed)
}
pub fn quick<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::Quick)
}
pub fn ignore<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::Ignore)
}
pub fn straight<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::StraightJoin)
}
pub fn sql_small_result<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::SmallResult)
}
pub fn sql_big_result<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::BigResult)
}
pub fn sql_buffer_result<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::BufferResult)
}
pub fn sql_no_cache<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::NoCache)
}
pub fn sql_calc_found_rows<Q: HasModifiers>() -> impl Mod<Q> {
modifier(Modifier::CalcFoundRows)
}
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;
#[derive(Debug, Clone, Copy, Default)]
pub struct DeleteSlot;
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, mut table: TableRef) {
table.joins.append(&mut q.target_table_mut().joins);
*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);
}
}
impl<Q: HasDeleteTables> TableSlot<Q> for DeleteSlot {
fn place(q: &mut Q, mut table: TableRef) {
let partitions = std::mem::take(&mut table.partitions);
q.delete_partitions_mut().extend(partitions);
q.delete_tables_mut().push(table);
}
}
#[derive(Debug, Clone)]
pub struct TableChain<S> {
table: TableRef,
slot: PhantomData<S>,
}
fn table_chain<S>(table: impl IntoExpr) -> TableChain<S> {
TableChain {
table: TableRef::new(table),
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 delete_table(table: impl IntoExpr) -> TableChain<DeleteSlot> {
table_chain(table)
}
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 lateral(mut self) -> TableChain<S> {
self.table = lateral_table(self.table);
self
}
#[must_use]
pub fn partition(
mut self,
partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.table.append_partition(partitions);
self
}
#[must_use]
pub fn use_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.index_hint(IndexHintKind::Use, indexes)
}
#[must_use]
pub fn ignore_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.index_hint(IndexHintKind::Ignore, indexes)
}
#[must_use]
pub fn force_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.index_hint(IndexHintKind::Force, indexes)
}
#[must_use]
pub fn for_join(self) -> TableChain<S> {
self.hint_scope(IndexHintScope::Join)
}
#[must_use]
pub fn for_order_by(self) -> TableChain<S> {
self.hint_scope(IndexHintScope::OrderBy)
}
#[must_use]
pub fn for_group_by(self) -> TableChain<S> {
self.hint_scope(IndexHintScope::GroupBy)
}
fn index_hint(
mut self,
kind: IndexHintKind,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> TableChain<S> {
self.table.append_index_hint(IndexHint::new(kind, indexes));
self
}
fn hint_scope(mut self, scope: IndexHintScope) -> TableChain<S> {
if let Some(hint) = self.table.index_hints.last_mut() {
hint.for_ = Some(scope);
}
self
}
}
impl<Q, S: TableSlot<Q>> Mod<Q> for TableChain<S> {
fn apply(self, q: &mut Q) {
S::place(q, self.table);
}
}
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
}
#[derive(Debug)]
struct LateralBareName(Expr);
impl Expression for LateralBareName {
fn write_sql(&self, w: &mut SqlWriter<'_>) {
w.record_error(keelson_core::Error::other(
"LATERAL is set on a bare table or CTE name, but LATERAL can precede only a derived table",
));
w.write_expr(&self.0);
}
}
#[derive(Debug, Clone)]
pub struct JoinChain {
join: Join,
}
fn join_chain(kind: JoinKind, to: impl IntoExpr) -> JoinChain {
JoinChain {
join: Join::new(kind, TableRef::new(to)),
}
}
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 cross_join(table: impl IntoExpr) -> PlainJoinChain {
PlainJoinChain(join_chain(JoinKind::Cross, table))
}
pub fn straight_join(table: impl IntoExpr) -> PlainJoinChain {
PlainJoinChain(join_chain(JoinKind::Custom("STRAIGHT_JOIN".into()), 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 lateral(mut self) -> JoinChain {
self.join.to = lateral_table(self.join.to);
self
}
#[must_use]
pub fn partition(
mut self,
partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.join.to.append_partition(partitions);
self
}
#[must_use]
pub fn use_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.index_hint(IndexHintKind::Use, indexes)
}
#[must_use]
pub fn ignore_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.index_hint(IndexHintKind::Ignore, indexes)
}
#[must_use]
pub fn force_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.index_hint(IndexHintKind::Force, indexes)
}
#[must_use]
pub fn for_join(self) -> JoinChain {
self.hint_scope(IndexHintScope::Join)
}
#[must_use]
pub fn for_order_by(self) -> JoinChain {
self.hint_scope(IndexHintScope::OrderBy)
}
#[must_use]
pub fn for_group_by(self) -> JoinChain {
self.hint_scope(IndexHintScope::GroupBy)
}
#[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
}
fn index_hint(
mut self,
kind: IndexHintKind,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.join
.to
.append_index_hint(IndexHint::new(kind, indexes));
self
}
fn hint_scope(mut self, scope: IndexHintScope) -> JoinChain {
if let Some(hint) = self.join.to.index_hints.last_mut() {
hint.for_ = Some(scope);
}
self
}
}
impl From<JoinChain> for Join {
fn from(chain: JoinChain) -> Join {
chain.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 PlainJoinChain(JoinChain);
impl PlainJoinChain {
#[must_use]
pub fn as_(self, alias: impl Into<Cow<'static, str>>) -> PlainJoinChain {
PlainJoinChain(self.0.as_(alias))
}
#[must_use]
pub fn columns(
self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.columns(columns))
}
#[must_use]
pub fn lateral(self) -> PlainJoinChain {
PlainJoinChain(self.0.lateral())
}
#[must_use]
pub fn partition(
self,
partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.partition(partitions))
}
#[must_use]
pub fn use_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.use_index(indexes))
}
#[must_use]
pub fn ignore_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.ignore_index(indexes))
}
#[must_use]
pub fn force_index(
self,
indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.force_index(indexes))
}
#[must_use]
pub fn on(self, condition: impl IntoExpr) -> PlainJoinChain {
PlainJoinChain(self.0.on(condition))
}
#[must_use]
pub fn on_eq(self, a: impl IntoExpr, b: impl IntoExpr) -> PlainJoinChain {
PlainJoinChain(self.0.on_eq(a, b))
}
#[must_use]
pub fn using(
self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> PlainJoinChain {
PlainJoinChain(self.0.using(columns))
}
}
impl From<PlainJoinChain> for Join {
fn from(chain: PlainJoinChain) -> Join {
chain.0.join
}
}
impl<Q: HasJoins> Mod<Q> for PlainJoinChain {
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 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 with_rollup<Q: HasGroupBy>() -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.group_by_mut().with = Some(GroupByWith::Rollup))
}
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 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 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))
}
#[derive(Debug, Clone)]
pub struct LockChain {
lock: Lock,
}
pub fn for_update() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::Update),
}
}
pub fn for_share() -> LockChain {
LockChain {
lock: Lock::new(LockStrength::Share),
}
}
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 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_values<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::binary(Expr::ident(c.clone()), "=", values_of(c)))
.collect();
mod_fn(move |q: &mut Q| q.set_mut().append_sets(assignments))
}
pub fn set_row<Q: HasSet>(
alias: impl Into<Cow<'static, str>>,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> impl Mod<Q> {
let alias = alias.into();
let assignments: Vec<Expr> = columns
.into_iter()
.map(Into::into)
.filter(|c: &Cow<'static, str>| !c.is_empty())
.map(|c| Expr::binary(Expr::ident(c.clone()), "=", row_value(alias.clone(), 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 RowAliasChain {
alias: RowAlias,
}
pub fn as_(alias: impl Into<Cow<'static, str>>) -> RowAliasChain {
RowAliasChain {
alias: RowAlias::new(alias),
}
}
impl RowAliasChain {
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> RowAliasChain {
self.alias.columns = columns.into_iter().map(Into::into).collect();
self
}
}
impl<Q: HasRowAlias> Mod<Q> for RowAliasChain {
fn apply(self, q: &mut Q) {
*q.row_alias_mut() = self.alias;
}
}
pub fn on_duplicate_key_update<Q: HasDuplicateKeyUpdate>(body: impl Mod<Set>) -> impl Mod<Q> {
let mut set = Set::default();
body.apply(&mut set);
mod_fn(move |q: &mut Q| q.duplicate_key_update_mut().append_sets(set.exprs))
}