use std::borrow::Cow;
use keelson_core::clause::{
HasJoins, HasReturning, HasTableRef, HasWith, Join, Returning, Set, TableRef, With,
};
use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
use super::HasTargetTable;
use crate::Psql;
use crate::extras::Overriding;
#[derive(Debug, Clone, Default)]
pub struct MergeQuery {
pub with: With,
pub target: TableRef,
pub source: TableRef,
pub on: Vec<Expr>,
pub whens: Vec<MergeWhen>,
pub returning: Returning,
}
impl MergeQuery {
pub fn new() -> MergeQuery {
MergeQuery::default()
}
pub fn apply(&mut self, mods: impl Mod<MergeQuery>) {
mods.apply(self);
}
}
impl Expression for MergeQuery {
fn write_sql(&self, w: &mut SqlWriter<'_>) {
w.write_if(!self.with.is_empty(), "", &self.with, " ");
if self.target.is_empty() {
w.record_error(Error::Incomplete("the target table of a MERGE"));
return;
}
if self.source.is_empty() {
w.record_error(Error::Incomplete("the USING source of a MERGE"));
return;
}
if self.on.is_empty() {
w.record_error(Error::Incomplete("the ON condition of a MERGE"));
return;
}
if self.whens.is_empty() {
w.record_error(Error::Incomplete("the WHEN clauses of a MERGE"));
return;
}
w.push_str("MERGE INTO ");
w.write_expr(&self.target);
w.push_str(" USING ");
w.write_expr(&self.source);
w.write_slice(&self.on, " ON ", " AND ", "");
w.write_slice(&self.whens, " ", " ", "");
w.write_if(!self.returning.is_empty(), " ", &self.returning, "");
}
}
impl Query for MergeQuery {
fn query_type(&self) -> QueryType {
QueryType::Merge
}
fn dialect(&self) -> &dyn Dialect {
&Psql
}
}
impl<H, L, M> QueryExtensions<H, L, M> for MergeQuery {}
impl IntoExpr for MergeQuery {
fn into_expr(self) -> Expr {
crate::query(self)
}
}
impl IntoExprList for MergeQuery {
fn into_expr_list(self) -> Vec<Expr> {
vec![self.into_expr()]
}
}
impl HasWith for MergeQuery {
fn with_mut(&mut self) -> &mut With {
&mut self.with
}
}
impl HasTargetTable for MergeQuery {
fn target_table_mut(&mut self) -> &mut TableRef {
&mut self.target
}
}
impl HasTableRef for MergeQuery {
fn table_ref_mut(&mut self) -> &mut TableRef {
&mut self.source
}
}
impl HasJoins for MergeQuery {
fn joins_mut(&mut self) -> &mut Vec<Join> {
&mut self.source.joins
}
}
impl HasReturning for MergeQuery {
fn returning_mut(&mut self) -> &mut Returning {
&mut self.returning
}
}
#[derive(Debug, Clone)]
pub struct MergeWhen {
pub kind: MergeMatchKind,
pub condition: Vec<Expr>,
pub action: MergeAction,
}
impl Expression for MergeWhen {
fn write_sql(&self, w: &mut SqlWriter<'_>) {
w.push_str(self.kind.as_str());
w.write_slice(&self.condition, " AND ", " AND ", "");
w.push_str(" THEN ");
match &self.action {
MergeAction::Update(set) => {
if set.is_empty() {
w.record_error(Error::Incomplete("the assignments of a MERGE UPDATE"));
return;
}
w.push_str("UPDATE SET ");
w.write_expr(set);
}
MergeAction::Delete => w.push_str("DELETE"),
MergeAction::DoNothing => w.push_str("DO NOTHING"),
MergeAction::Insert(insert) => w.write_expr(insert),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeMatchKind {
Matched,
NotMatched {
by_target: bool,
},
NotMatchedBySource,
}
impl MergeMatchKind {
pub fn as_str(self) -> &'static str {
match self {
MergeMatchKind::Matched => "WHEN MATCHED",
MergeMatchKind::NotMatched { by_target: false } => "WHEN NOT MATCHED",
MergeMatchKind::NotMatched { by_target: true } => "WHEN NOT MATCHED BY TARGET",
MergeMatchKind::NotMatchedBySource => "WHEN NOT MATCHED BY SOURCE",
}
}
}
#[derive(Debug, Clone)]
pub enum MergeAction {
Update(Set),
Delete,
DoNothing,
Insert(MergeInsert),
}
#[derive(Debug, Clone, Default)]
pub struct MergeInsert {
pub columns: Vec<Cow<'static, str>>,
pub overriding: Option<Overriding>,
pub row: Vec<Expr>,
}
impl Expression for MergeInsert {
fn write_sql(&self, w: &mut SqlWriter<'_>) {
w.push_str("INSERT");
if !self.columns.is_empty() {
w.push_str(" (");
for (i, column) in self.columns.iter().enumerate() {
if i > 0 {
w.push_str(", ");
}
w.push_quoted(&[column]);
}
w.push_str(")");
}
if let Some(overriding) = &self.overriding {
w.push_str(" OVERRIDING ");
w.push_str(overriding.as_str());
w.push_str(" VALUE");
}
if self.row.is_empty() {
w.push_str(" DEFAULT VALUES");
} else {
w.write_slice(&self.row, " VALUES (", ", ", ")");
}
}
}