Skip to main content

limbo_sqlite3_parser/to_sql_string/stmt/
insert.rs

1use crate::{ast, to_sql_string::ToSqlString};
2
3impl ToSqlString for ast::Insert {
4    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
5        format!(
6            "{}INSERT {}INTO {} {}{}{}",
7            self.with.as_ref().map_or("".to_string(), |with| format!(
8                "{} ",
9                with.to_sql_string(context)
10            )),
11            self.or_conflict
12                .map_or("".to_string(), |conflict| format!("OR {} ", conflict)),
13            self.tbl_name.to_sql_string(context),
14            self.columns
15                .as_ref()
16                .map_or("".to_string(), |col_names| format!(
17                    "({}) ",
18                    col_names
19                        .iter()
20                        .map(|name| name.0.clone())
21                        .collect::<Vec<_>>()
22                        .join(", ")
23                )),
24            self.body.to_sql_string(context),
25            self.returning
26                .as_ref()
27                .map_or("".to_string(), |returning| format!(
28                    " RETURNING {}",
29                    returning
30                        .iter()
31                        .map(|col| col.to_sql_string(context))
32                        .collect::<Vec<_>>()
33                        .join(", ")
34                ))
35        )
36    }
37}
38
39impl ToSqlString for ast::InsertBody {
40    fn to_sql_string<C: crate::to_sql_string::ToSqlContext>(&self, context: &C) -> String {
41        match self {
42            Self::DefaultValues => "DEFAULT VALUES".to_string(),
43            Self::Select(select, upsert) => format!(
44                "{}{}",
45                select.to_sql_string(context),
46                upsert.as_ref().map_or("".to_string(), |upsert| format!(
47                    " {}",
48                    upsert.to_sql_string(context)
49                )),
50            ),
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::to_sql_string_test;
58
59    // Basic INSERT with all columns
60    to_sql_string_test!(
61        test_insert_basic,
62        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000);"
63    );
64
65    // INSERT with multiple rows
66    to_sql_string_test!(
67        test_insert_multiple_rows,
68        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000), (2, 'Jane Smith', 60000);"
69    );
70
71    // INSERT with specific columns
72    to_sql_string_test!(
73        test_insert_specific_columns,
74        "INSERT INTO employees (name, salary) VALUES ('Alice Brown', 55000);"
75    );
76
77    // INSERT with DEFAULT VALUES
78    to_sql_string_test!(
79        test_insert_default_values,
80        "INSERT INTO employees DEFAULT VALUES;"
81    );
82
83    // INSERT with SELECT subquery
84    to_sql_string_test!(
85        test_insert_select_subquery,
86        "INSERT INTO employees (id, name, salary) SELECT id, name, salary FROM temp_employees WHERE salary > 40000;"
87    );
88
89    // INSERT with ON CONFLICT IGNORE
90    to_sql_string_test!(
91        test_insert_on_conflict_ignore,
92        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO NOTHING;"
93    );
94
95    // INSERT with ON CONFLICT REPLACE
96    to_sql_string_test!(
97        test_insert_on_conflict_replace,
98        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET name = excluded.name, salary = excluded.salary;"
99    );
100
101    // INSERT with RETURNING clause
102    to_sql_string_test!(
103        test_insert_with_returning,
104        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) RETURNING id, name;"
105    );
106
107    // INSERT with NULL values
108    to_sql_string_test!(
109        test_insert_with_null,
110        "INSERT INTO employees (id, name, salary, department_id) VALUES (1, 'John Doe', NULL, NULL);"
111    );
112
113    // INSERT with expression in VALUES
114    to_sql_string_test!(
115        test_insert_with_expression,
116        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000 * 1.1);"
117    );
118
119    // INSERT into schema-qualified table
120    to_sql_string_test!(
121        test_insert_schema_qualified,
122        "INSERT INTO main.employees (id, name, salary) VALUES (1, 'John Doe', 50000);"
123    );
124
125    // INSERT with subquery and JOIN
126    to_sql_string_test!(
127        test_insert_subquery_join,
128        "INSERT INTO employees (id, name, department_id) SELECT e.id, e.name, d.id FROM temp_employees e JOIN departments d ON e.dept_name = d.name;"
129    );
130
131    // INSERT with all columns from SELECT
132    to_sql_string_test!(
133        test_insert_all_columns_select,
134        "INSERT INTO employees SELECT * FROM temp_employees;"
135    );
136
137    // INSERT with ON CONFLICT and WHERE clause
138    to_sql_string_test!(
139        test_insert_on_conflict_where,
140        "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET salary = excluded.salary WHERE excluded.salary > employees.salary;"
141    );
142
143    // INSERT with quoted column names (reserved words)
144    to_sql_string_test!(
145        test_insert_quoted_columns,
146        "INSERT INTO employees (\"select\", \"from\") VALUES (1, 'data');"
147    );
148}