mik_sql/builder/
insert.rs

1//! INSERT query builder.
2
3use crate::dialect::{Dialect, Postgres, Sqlite};
4use crate::validate::assert_valid_sql_identifier;
5
6use super::types::{QueryResult, Value};
7
8/// Builder for INSERT queries.
9#[derive(Debug)]
10#[must_use = "builder does nothing until .build() is called"]
11pub struct InsertBuilder<D: Dialect> {
12    dialect: D,
13    table: String,
14    columns: Vec<String>,
15    values: Vec<Vec<Value>>,
16    returning: Vec<String>,
17}
18
19impl<D: Dialect> InsertBuilder<D> {
20    /// Create a new insert builder.
21    ///
22    /// # Panics
23    ///
24    /// Panics if the table name is not a valid SQL identifier.
25    pub fn new(dialect: D, table: impl Into<String>) -> Self {
26        let table = table.into();
27        assert_valid_sql_identifier(&table, "table");
28        Self {
29            dialect,
30            table,
31            columns: Vec::new(),
32            values: Vec::new(),
33            returning: Vec::new(),
34        }
35    }
36
37    /// Set the columns for insertion.
38    ///
39    /// # Panics
40    ///
41    /// Panics if any column name is not a valid SQL identifier.
42    pub fn columns(mut self, columns: &[&str]) -> Self {
43        for col in columns {
44            assert_valid_sql_identifier(col, "column");
45        }
46        self.columns = columns.iter().map(|s| (*s).to_string()).collect();
47        self
48    }
49
50    /// Add a row of values.
51    pub fn values(mut self, values: Vec<Value>) -> Self {
52        self.values.push(values);
53        self
54    }
55
56    /// Add multiple rows of values.
57    pub fn values_many(mut self, rows: Vec<Vec<Value>>) -> Self {
58        self.values.extend(rows);
59        self
60    }
61
62    /// Add RETURNING clause (Postgres).
63    ///
64    /// # Panics
65    ///
66    /// Panics if any column name is not a valid SQL identifier.
67    pub fn returning(mut self, columns: &[&str]) -> Self {
68        for col in columns {
69            assert_valid_sql_identifier(col, "returning column");
70        }
71        self.returning = columns.iter().map(|s| (*s).to_string()).collect();
72        self
73    }
74
75    /// Build the INSERT query.
76    pub fn build(self) -> QueryResult {
77        let mut sql = String::new();
78        let mut params = Vec::new();
79        let mut param_idx = 1usize;
80
81        // INSERT INTO table (columns)
82        sql.push_str(&format!(
83            "INSERT INTO {} ({})",
84            self.table,
85            self.columns.join(", ")
86        ));
87
88        // VALUES (...)
89        let mut value_groups = Vec::new();
90        for row in &self.values {
91            let placeholders: Vec<String> = row
92                .iter()
93                .map(|v| {
94                    let p = self.dialect.param(param_idx);
95                    params.push(v.clone());
96                    param_idx += 1;
97                    p
98                })
99                .collect();
100            value_groups.push(format!("({})", placeholders.join(", ")));
101        }
102        sql.push_str(&format!(" VALUES {}", value_groups.join(", ")));
103
104        // RETURNING clause
105        if !self.returning.is_empty() {
106            sql.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
107        }
108
109        QueryResult { sql, params }
110    }
111}
112
113/// Create an INSERT builder for Postgres.
114pub fn insert(table: impl Into<String>) -> InsertBuilder<Postgres> {
115    InsertBuilder::new(Postgres, table)
116}
117
118/// Create an INSERT builder for `SQLite`.
119pub fn insert_sqlite(table: impl Into<String>) -> InsertBuilder<Sqlite> {
120    InsertBuilder::new(Sqlite, table)
121}