use crate::ast::{
Action, Cage, Condition, Distance, Expr, GroupByMode, IndexDef, Join, LockMode, OverridingKind,
SampleMethod, SetOp, TableConstraint,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Qail {
pub action: Action,
pub table: String,
pub from_source: Option<FromSource>,
pub columns: Vec<Expr>,
pub joins: Vec<Join>,
pub cages: Vec<Cage>,
pub distinct: bool,
pub index_def: Option<Box<IndexDef>>,
pub table_constraints: Vec<TableConstraint>,
pub set_ops: Vec<(SetOp, Box<Qail>)>,
pub having: Vec<Condition>,
pub group_by_mode: GroupByMode,
pub ctes: Vec<CTEDef>,
pub distinct_on: Vec<Expr>,
pub returning: Option<Vec<Expr>>,
pub returning_aliases: Option<ReturningAliases>,
pub on_conflict: Option<OnConflict>,
pub conflict_update_scope: Vec<Condition>,
pub merge: Option<Merge>,
pub source_query: Option<Box<Qail>>,
pub channel: Option<String>,
pub payload: Option<String>,
pub savepoint_name: Option<String>,
pub from_tables: Vec<String>,
pub using_tables: Vec<String>,
pub lock_mode: Option<LockMode>,
pub skip_locked: bool,
pub lock_nowait: bool,
pub lock_of: Vec<String>,
pub fetch: Option<(u64, bool)>,
pub default_values: bool,
pub overriding: Option<OverridingKind>,
pub sample: Option<(SampleMethod, f64, Option<u64>)>,
pub only_table: bool,
pub vector: Option<Vec<f32>>,
pub score_threshold: Option<f32>,
pub vector_name: Option<String>,
pub with_vector: bool,
pub vector_size: Option<u64>,
pub distance: Option<Distance>,
pub on_disk: Option<bool>,
pub function_def: Option<Box<crate::ast::FunctionDef>>,
pub trigger_def: Option<Box<crate::ast::TriggerDef>>,
pub policy_def: Option<Box<crate::migrate::policy::RlsPolicy>>,
pub view_security_invoker: bool,
pub view_security_barrier: bool,
pub view_check_option: Option<crate::ast::ViewCheckOption>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CTEDef {
pub name: String,
pub recursive: bool,
pub columns: Vec<String>,
pub base_query: Box<Qail>,
pub recursive_query: Option<Box<Qail>>,
pub source_table: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub materialization: Option<CteMaterialization>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub search: Option<CteSearch>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cycle: Option<CteCycle>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum CteMaterialization {
Materialized,
NotMaterialized,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum CteSearchOrder {
DepthFirst,
BreadthFirst,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CteSearch {
pub order: CteSearchOrder,
pub by: Vec<String>,
pub set_column: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CteCycle {
pub columns: Vec<String>,
pub set_column: String,
pub using_column: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReturningAliases {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub before: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
}
impl ReturningAliases {
pub fn sql_parts(&self) -> Option<Vec<(&'static str, &str)>> {
let mut parts = Vec::new();
if let Some(before) = &self.before {
parts.push(("OLD", before.as_str()));
}
if let Some(after) = &self.after {
parts.push(("NEW", after.as_str()));
}
if parts.is_empty() { None } else { Some(parts) }
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum FromSource {
Subquery {
query: Box<Qail>,
alias: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
column_aliases: Vec<String>,
},
Function {
name: String,
args: Vec<Expr>,
#[serde(default)]
with_ordinality: bool,
alias: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
column_aliases: Vec<String>,
},
}
impl FromSource {
pub fn subquery(query: Qail, alias: impl Into<String>) -> Self {
FromSource::Subquery {
query: Box::new(query),
alias: alias.into(),
column_aliases: Vec::new(),
}
}
pub fn function<I, E>(name: impl Into<String>, args: I, alias: impl Into<String>) -> Self
where
I: IntoIterator<Item = E>,
E: Into<Expr>,
{
FromSource::Function {
name: name.into(),
args: args.into_iter().map(Into::into).collect(),
with_ordinality: false,
alias: alias.into(),
column_aliases: Vec::new(),
}
}
pub fn column_aliases<I, S>(mut self, columns: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
let columns: Vec<String> = columns.into_iter().map(Into::into).collect();
match &mut self {
FromSource::Subquery { column_aliases, .. }
| FromSource::Function { column_aliases, .. } => *column_aliases = columns,
}
self
}
pub fn with_ordinality(mut self) -> Self {
if let FromSource::Function {
with_ordinality, ..
} = &mut self
{
*with_ordinality = true;
}
self
}
pub fn alias(&self) -> &str {
match self {
FromSource::Subquery { alias, .. } | FromSource::Function { alias, .. } => alias,
}
}
pub fn column_alias_list(&self) -> &[String] {
match self {
FromSource::Subquery { column_aliases, .. }
| FromSource::Function { column_aliases, .. } => column_aliases,
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct OnConflict {
pub columns: Vec<String>,
#[serde(default)]
pub constraint: Option<String>,
pub action: ConflictAction,
#[serde(default)]
pub where_conditions: Vec<Condition>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ConflictAction {
DoNothing,
DoUpdate {
assignments: Vec<(String, Expr)>,
},
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Merge {
pub target_alias: Option<String>,
pub source: MergeSource,
pub on: Vec<Condition>,
pub clauses: Vec<MergeClause>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum MergeSource {
Table {
name: String,
alias: Option<String>,
#[serde(default)]
only: bool,
},
Query {
query: Box<Qail>,
alias: Option<String>,
},
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MergeClause {
pub match_kind: MergeMatchKind,
pub condition: Vec<Condition>,
pub action: MergeAction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MergeMatchKind {
Matched,
NotMatchedByTarget,
NotMatchedBySource,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum MergeAction {
Update {
assignments: Vec<(String, Expr)>,
},
Insert {
columns: Vec<String>,
values: Vec<Expr>,
#[serde(default)]
overriding: Option<OverridingKind>,
#[serde(default)]
default_values: bool,
},
Delete,
DoNothing,
}
impl Default for OnConflict {
fn default() -> Self {
Self {
columns: vec![],
constraint: None,
action: ConflictAction::DoNothing,
where_conditions: Vec::new(),
}
}
}
impl ConflictAction {
pub(crate) fn update_assignments(&self) -> Option<&[(String, Expr)]> {
match self {
Self::DoNothing => None,
Self::DoUpdate { assignments } => Some(assignments),
}
}
}
impl Default for Qail {
fn default() -> Self {
Self {
action: Action::Get,
table: String::new(),
from_source: None,
columns: vec![],
joins: vec![],
cages: vec![],
distinct: false,
index_def: None,
table_constraints: vec![],
set_ops: vec![],
having: vec![],
group_by_mode: GroupByMode::Simple,
ctes: vec![],
distinct_on: vec![],
returning: None,
returning_aliases: None,
on_conflict: None,
conflict_update_scope: Vec::new(),
merge: None,
source_query: None,
channel: None,
payload: None,
savepoint_name: None,
from_tables: vec![],
using_tables: vec![],
lock_mode: None,
skip_locked: false,
lock_nowait: false,
lock_of: vec![],
fetch: None,
default_values: false,
overriding: None,
sample: None,
only_table: false,
vector: None,
score_threshold: None,
vector_name: None,
with_vector: false,
vector_size: None,
distance: None,
on_disk: None,
function_def: None,
trigger_def: None,
policy_def: None,
view_security_invoker: false,
view_security_barrier: false,
view_check_option: None,
}
}
}
mod advanced;
mod constructors;
mod cte;
mod grouping;
mod merge;
mod query;
pub use grouping::GroupByClause;
mod rls;
mod serialization;
mod vector;
impl std::fmt::Display for Qail {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::fmt::Formatter;
match Formatter::new().format(self) {
Ok(s) => write!(f, "{}", s),
Err(_) => write!(f, "{:?}", self), }
}
}