Skip to main content

siderite_orm/
write.rs

1//! [`WritePlan`]: backend-neutral INSERT / UPDATE / DELETE.
2//!
3//! Like [`QueryPlan`](crate::QueryPlan), a write plan is pure data. Backends
4//! check it against their capabilities and compile it; values are always
5//! bound parameters.
6
7use crate::capabilities::{BackendCapabilities, Feature};
8use crate::error::BackendCapabilityError;
9use crate::expr::{Expr, Ident};
10use crate::value::Value;
11
12/// Multi-row insert.
13#[derive(Debug, Clone, PartialEq)]
14pub struct InsertPlan {
15    /// Target table.
16    pub table: Ident,
17    /// Inserted columns (auto columns are omitted by the caller).
18    pub columns: Vec<Ident>,
19    /// One `Vec` per row, each with `columns.len()` values.
20    pub rows: Vec<Vec<Value>>,
21    /// Columns to return (typically the generated primary key).
22    pub returning: Vec<Ident>,
23}
24
25/// `UPDATE table SET .. WHERE ..`.
26#[derive(Debug, Clone, PartialEq)]
27pub struct UpdatePlan {
28    /// Target table.
29    pub table: Ident,
30    /// `column = expression` pairs (expressions allow `F`-style updates).
31    pub assignments: Vec<(Ident, Expr)>,
32    /// Row filter; `None` updates every row.
33    pub filter: Option<Expr>,
34    /// Columns to return.
35    pub returning: Vec<Ident>,
36}
37
38/// `DELETE FROM table WHERE ..`.
39#[derive(Debug, Clone, PartialEq)]
40pub struct DeletePlan {
41    /// Target table.
42    pub table: Ident,
43    /// Row filter; `None` deletes every row.
44    pub filter: Option<Expr>,
45    /// Columns to return.
46    pub returning: Vec<Ident>,
47}
48
49/// A data-modifying statement.
50#[derive(Debug, Clone, PartialEq)]
51pub enum WritePlan {
52    /// INSERT.
53    Insert(InsertPlan),
54    /// UPDATE.
55    Update(UpdatePlan),
56    /// DELETE.
57    Delete(DeletePlan),
58}
59
60impl WritePlan {
61    /// Columns requested back from the statement.
62    pub fn returning(&self) -> &[Ident] {
63        match self {
64            WritePlan::Insert(p) => &p.returning,
65            WritePlan::Update(p) => &p.returning,
66            WritePlan::Delete(p) => &p.returning,
67        }
68    }
69
70    /// Features this plan needs from a backend (deduplicated).
71    pub fn required_features(&self) -> Vec<Feature> {
72        let mut out = Vec::new();
73        if !self.returning().is_empty() {
74            out.push(Feature::Returning);
75        }
76        let exprs: Vec<&Expr> = match self {
77            WritePlan::Insert(_) => Vec::new(),
78            WritePlan::Update(p) => p
79                .assignments
80                .iter()
81                .map(|(_, e)| e)
82                .chain(p.filter.iter())
83                .collect(),
84            WritePlan::Delete(p) => p.filter.iter().collect(),
85        };
86        for feature in exprs.into_iter().flat_map(Expr::required_features) {
87            if !out.contains(&feature) {
88                out.push(feature);
89            }
90        }
91        out
92    }
93
94    /// Check the plan against backend capabilities.
95    ///
96    /// # Errors
97    /// The first unsupported feature.
98    pub fn check(&self, caps: &BackendCapabilities) -> Result<(), BackendCapabilityError> {
99        self.required_features()
100            .into_iter()
101            .try_for_each(|f| caps.require(f))
102    }
103}