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/// Builder for INSERT queries.
33#[derive(Debug, Clone)]
34pub struct InsertBuilder {
35    table: String,
36    columns: Vec<ColumnMarker>,
37    values: Vec<Vec<Value>>,
38    returning: Vec<ColumnMarker>,
39}
40
41impl InsertBuilder {
42    /// Sets the columns to insert into.
43    #[must_use]
44    pub fn columns(mut self, columns: Vec<ColumnMarker>) -> Self {
45        self.columns = columns;
46        self
47    }
48
49    /// Adds a column to insert into.
50    #[must_use]
51    pub fn column(mut self, column: ColumnMarker) -> Self {
52        self.columns.push(column);
53        self
54    }
55
56    /// Adds a row of values to insert.
57    ///
58    /// Each call adds one row. The number of values must match
59    /// the number of columns when `build()` is called.
60    #[must_use]
61    pub fn values(mut self, row: Vec<Value>) -> Self {
62        self.values.push(row);
63        self
64    }
65
66    /// Sets the RETURNING clause columns.
67    #[must_use]
68    pub fn returning(mut self, columns: Vec<ColumnMarker>) -> Self {
69        self.returning = columns;
70        self
71    }
72
73    /// Builds the final INSERT query.
74    ///
75    /// # Errors
76    ///
77    /// Returns an error if:
78    /// - The table name is empty
79    /// - No columns are specified
80    /// - No rows of values are specified
81    /// - Any row has a different number of values than columns
82    pub fn build(self) -> Result<Insert> {
83        if self.table.is_empty() {
84            return Err(Error::MissingField("table".to_string()));
85        }
86
87        if self.columns.is_empty() {
88            return Err(Error::MissingField("columns".to_string()));
89        }
90
91        if self.values.is_empty() {
92            return Err(Error::MissingField("values".to_string()));
93        }
94
95        let col_count = self.columns.len();
96        for (i, row) in self.values.iter().enumerate() {
97            if row.len() != col_count {
98                return Err(Error::InvalidQuery(format!(
99                    "row {} has {} values but {} columns were specified",
100                    i,
101                    row.len(),
102                    col_count
103                )));
104            }
105        }
106
107        Ok(Insert {
108            table: self.table,
109            columns: self.columns,
110            values: self.values,
111            returning: self.returning,
112        })
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_simple_insert() {
122        let insert = Insert::into_table("users")
123            .column(ColumnMarker::new("users", "email"))
124            .values(vec![Value::String("alice@example.com".to_string())])
125            .build()
126            .unwrap();
127
128        assert_eq!(insert.table, "users");
129        assert_eq!(insert.columns.len(), 1);
130        assert_eq!(insert.columns[0].name, "email");
131        assert_eq!(insert.values.len(), 1);
132        assert!(insert.returning.is_empty());
133    }
134
135    #[test]
136    fn test_multi_column_insert() {
137        let insert = Insert::into_table("users")
138            .columns(vec![
139                ColumnMarker::new("users", "email"),
140                ColumnMarker::new("users", "name"),
141            ])
142            .values(vec![
143                Value::String("alice@example.com".to_string()),
144                Value::String("Alice".to_string()),
145            ])
146            .build()
147            .unwrap();
148
149        assert_eq!(insert.columns.len(), 2);
150        assert_eq!(insert.values.len(), 1);
151        assert_eq!(insert.values[0].len(), 2);
152    }
153
154    #[test]
155    fn test_batch_insert() {
156        let insert = Insert::into_table("users")
157            .column(ColumnMarker::new("users", "email"))
158            .values(vec![Value::String("alice@example.com".to_string())])
159            .values(vec![Value::String("bob@example.com".to_string())])
160            .values(vec![Value::String("charlie@example.com".to_string())])
161            .build()
162            .unwrap();
163
164        assert_eq!(insert.values.len(), 3);
165    }
166
167    #[test]
168    fn test_insert_with_returning() {
169        let insert = Insert::into_table("users")
170            .column(ColumnMarker::new("users", "email"))
171            .values(vec![Value::String("alice@example.com".to_string())])
172            .returning(vec![
173                ColumnMarker::new("users", "id"),
174                ColumnMarker::new("users", "email"),
175            ])
176            .build()
177            .unwrap();
178
179        assert_eq!(insert.returning.len(), 2);
180        assert_eq!(insert.returning[0].name, "id");
181        assert_eq!(insert.returning[1].name, "email");
182    }
183
184    #[test]
185    fn test_missing_table() {
186        let result = Insert::into_table("")
187            .column(ColumnMarker::new("users", "email"))
188            .values(vec![Value::String("test".to_string())])
189            .build();
190
191        assert!(result.is_err());
192    }
193
194    #[test]
195    fn test_missing_columns() {
196        let result = Insert::into_table("users")
197            .values(vec![Value::String("test".to_string())])
198            .build();
199
200        assert!(result.is_err());
201    }
202
203    #[test]
204    fn test_missing_values() {
205        let result = Insert::into_table("users")
206            .column(ColumnMarker::new("users", "email"))
207            .build();
208
209        assert!(result.is_err());
210    }
211
212    #[test]
213    fn test_mismatched_values_count() {
214        let result = Insert::into_table("users")
215            .columns(vec![
216                ColumnMarker::new("users", "email"),
217                ColumnMarker::new("users", "name"),
218            ])
219            .values(vec![Value::String("alice@example.com".to_string())])
220            .build();
221
222        assert!(result.is_err());
223        let err = result.unwrap_err();
224        assert!(matches!(err, Error::InvalidQuery(_)));
225    }
226}