Skip to main content

nautilus_core/
insert.rs

1//! INSERT query AST and builder.
2
3use crate::column::ColumnMarker;
4use crate::error::{Error, Result};
5use crate::value::Value;
6
7/// INSERT query AST node.
8#[derive(Debug, Clone, PartialEq)]
9pub struct Insert {
10    /// Table name.
11    pub table: String,
12    /// Columns to insert into.
13    pub columns: Vec<ColumnMarker>,
14    /// Rows of values to insert (each inner Vec is one row).
15    pub values: Vec<Vec<Value>>,
16    /// Columns to return (RETURNING clause). Empty = no RETURNING.
17    pub returning: Vec<ColumnMarker>,
18}
19
20impl Insert {
21    /// Creates a new INSERT query builder for the given table.
22    pub fn into_table(table: impl Into<String>) -> InsertBuilder {
23        InsertBuilder {
24            table: table.into(),
25            columns: Vec::new(),
26            values: Vec::new(),
27            returning: Vec::new(),
28        }
29    }
30}
31
32/// Reserved capacities for the `Vec`s maintained by an [`InsertBuilder`].
33#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
34pub struct InsertCapacity {
35    /// Expected number of insert columns.
36    pub columns: usize,
37    /// Expected number of rows to insert.
38    pub rows: usize,
39    /// Expected number of `RETURNING` columns.
40    pub returning: usize,
41}
42
43/// Builder for INSERT queries.
44#[derive(Debug, Clone)]
45pub struct InsertBuilder {
46    table: String,
47    columns: Vec<ColumnMarker>,
48    values: Vec<Vec<Value>>,
49    returning: Vec<ColumnMarker>,
50}
51
52impl InsertBuilder {
53    /// Reserve capacity for the builder's internal vectors.
54    #[must_use]
55    pub fn with_capacity(mut self, capacity: InsertCapacity) -> Self {
56        self.columns.reserve(capacity.columns);
57        self.values.reserve(capacity.rows);
58        self.returning.reserve(capacity.returning);
59        self
60    }
61
62    /// Sets the columns to insert into.
63    #[must_use]
64    pub fn columns(mut self, columns: Vec<ColumnMarker>) -> Self {
65        self.columns = columns;
66        self
67    }
68
69    /// Adds a column to insert into.
70    #[must_use]
71    pub fn column(mut self, column: ColumnMarker) -> Self {
72        self.columns.push(column);
73        self
74    }
75
76    /// Adds a row of values to insert.
77    ///
78    /// Each call adds one row. The number of values must match
79    /// the number of columns when `build()` is called.
80    #[must_use]
81    pub fn values(mut self, row: Vec<Value>) -> Self {
82        self.values.push(row);
83        self
84    }
85
86    /// Sets all rows to insert in one call.
87    #[must_use]
88    pub fn rows(mut self, rows: Vec<Vec<Value>>) -> Self {
89        self.values = rows;
90        self
91    }
92
93    /// Sets the RETURNING clause columns.
94    #[must_use]
95    pub fn returning(mut self, columns: Vec<ColumnMarker>) -> Self {
96        self.returning = columns;
97        self
98    }
99
100    /// Builds the final INSERT query.
101    ///
102    /// # Errors
103    ///
104    /// Returns an error if:
105    /// - The table name is empty
106    /// - No columns are specified
107    /// - No rows of values are specified
108    /// - Any row has a different number of values than columns
109    pub fn build(self) -> Result<Insert> {
110        if self.table.is_empty() {
111            return Err(Error::MissingField("table".to_string()));
112        }
113
114        if self.columns.is_empty() {
115            return Err(Error::MissingField("columns".to_string()));
116        }
117
118        if self.values.is_empty() {
119            return Err(Error::MissingField("values".to_string()));
120        }
121
122        let col_count = self.columns.len();
123        for (i, row) in self.values.iter().enumerate() {
124            if row.len() != col_count {
125                return Err(Error::InvalidQuery(format!(
126                    "row {} has {} values but {} columns were specified",
127                    i,
128                    row.len(),
129                    col_count
130                )));
131            }
132        }
133
134        Ok(Insert {
135            table: self.table,
136            columns: self.columns,
137            values: self.values,
138            returning: self.returning,
139        })
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_simple_insert() {
149        let insert = Insert::into_table("users")
150            .column(ColumnMarker::new("users", "email"))
151            .values(vec![Value::String("alice@example.com".to_string())])
152            .build()
153            .unwrap();
154
155        assert_eq!(insert.table, "users");
156        assert_eq!(insert.columns.len(), 1);
157        assert_eq!(insert.columns[0].name, "email");
158        assert_eq!(insert.values.len(), 1);
159        assert!(insert.returning.is_empty());
160    }
161
162    #[test]
163    fn test_multi_column_insert() {
164        let insert = Insert::into_table("users")
165            .columns(vec![
166                ColumnMarker::new("users", "email"),
167                ColumnMarker::new("users", "name"),
168            ])
169            .values(vec![
170                Value::String("alice@example.com".to_string()),
171                Value::String("Alice".to_string()),
172            ])
173            .build()
174            .unwrap();
175
176        assert_eq!(insert.columns.len(), 2);
177        assert_eq!(insert.values.len(), 1);
178        assert_eq!(insert.values[0].len(), 2);
179    }
180
181    #[test]
182    fn test_batch_insert() {
183        let insert = Insert::into_table("users")
184            .column(ColumnMarker::new("users", "email"))
185            .values(vec![Value::String("alice@example.com".to_string())])
186            .values(vec![Value::String("bob@example.com".to_string())])
187            .values(vec![Value::String("charlie@example.com".to_string())])
188            .build()
189            .unwrap();
190
191        assert_eq!(insert.values.len(), 3);
192    }
193
194    #[test]
195    fn test_insert_with_returning() {
196        let insert = Insert::into_table("users")
197            .column(ColumnMarker::new("users", "email"))
198            .values(vec![Value::String("alice@example.com".to_string())])
199            .returning(vec![
200                ColumnMarker::new("users", "id"),
201                ColumnMarker::new("users", "email"),
202            ])
203            .build()
204            .unwrap();
205
206        assert_eq!(insert.returning.len(), 2);
207        assert_eq!(insert.returning[0].name, "id");
208        assert_eq!(insert.returning[1].name, "email");
209    }
210
211    #[test]
212    fn test_missing_table() {
213        let result = Insert::into_table("")
214            .column(ColumnMarker::new("users", "email"))
215            .values(vec![Value::String("test".to_string())])
216            .build();
217
218        assert!(result.is_err());
219    }
220
221    #[test]
222    fn test_missing_columns() {
223        let result = Insert::into_table("users")
224            .values(vec![Value::String("test".to_string())])
225            .build();
226
227        assert!(result.is_err());
228    }
229
230    #[test]
231    fn test_missing_values() {
232        let result = Insert::into_table("users")
233            .column(ColumnMarker::new("users", "email"))
234            .build();
235
236        assert!(result.is_err());
237    }
238
239    #[test]
240    fn test_mismatched_values_count() {
241        let result = Insert::into_table("users")
242            .columns(vec![
243                ColumnMarker::new("users", "email"),
244                ColumnMarker::new("users", "name"),
245            ])
246            .values(vec![Value::String("alice@example.com".to_string())])
247            .build();
248
249        assert!(result.is_err());
250        let err = result.unwrap_err();
251        assert!(matches!(err, Error::InvalidQuery(_)));
252    }
253}