Skip to main content

sqlrite/sql/parser/
insert.rs

1use sqlparser::ast::{Expr, Insert, SetExpr, Statement, Value as AstValue, Values};
2
3use crate::error::{Result, SQLRiteError};
4use crate::sql::db::table::{Value, parse_vector_literal};
5
6/// Parsed INSERT statement: target table, declared column list, and one
7/// or more rows of typed values.
8///
9/// `rows` is `Vec<Vec<Option<Value>>>` rather than `Vec<Vec<String>>` so
10/// SQL `NULL` can be represented faithfully as `None` instead of leaking
11/// out as the string sentinel `"Null"` (which used to break INTEGER /
12/// REAL / BOOL inserts and silently round-trip as the literal text
13/// `"Null"` in TEXT columns).
14#[derive(Debug)]
15pub struct InsertQuery {
16    pub table_name: String,
17    pub columns: Vec<String>,
18    pub rows: Vec<Vec<Option<Value>>>,
19}
20
21impl InsertQuery {
22    pub fn new(statement: &Statement) -> Result<InsertQuery> {
23        let tname: Option<String>;
24        let mut columns: Vec<String> = vec![];
25        let mut all_values: Vec<Vec<Option<Value>>> = vec![];
26
27        match statement {
28            Statement::Insert(Insert {
29                table,
30                columns: cols,
31                source,
32                ..
33            }) => {
34                tname = Some(table.to_string());
35                for col in cols {
36                    columns.push(col.to_string());
37                }
38
39                let source = source.as_ref().ok_or_else(|| {
40                    SQLRiteError::Internal(
41                        "INSERT statement is missing a source expression".to_string(),
42                    )
43                })?;
44
45                if let SetExpr::Values(Values { rows, .. }) = source.body.as_ref() {
46                    for row in rows {
47                        let mut value_set: Vec<Option<Value>> = vec![];
48                        for e in row {
49                            match e {
50                                Expr::Value(v) => match &v.value {
51                                    AstValue::Number(n, _) => {
52                                        // Try integer first; if the literal has
53                                        // a decimal point or exponent, i64 fails
54                                        // and we fall through to f64.
55                                        if let Ok(i) = n.parse::<i64>() {
56                                            value_set.push(Some(Value::Integer(i)));
57                                        } else if let Ok(f) = n.parse::<f64>() {
58                                            value_set.push(Some(Value::Real(f)));
59                                        } else {
60                                            return Err(SQLRiteError::General(format!(
61                                                "Could not parse numeric literal '{n}'"
62                                            )));
63                                        }
64                                    }
65                                    AstValue::Boolean(b) => {
66                                        value_set.push(Some(Value::Bool(*b)));
67                                    }
68                                    AstValue::SingleQuotedString(sqs) => {
69                                        value_set.push(Some(Value::Text(sqs.to_string())));
70                                    }
71                                    AstValue::Null => {
72                                        value_set.push(None);
73                                    }
74                                    _ => {}
75                                },
76                                Expr::Identifier(i) => {
77                                    // Phase 7a — sqlparser parses bracket-array
78                                    // literals like `[0.1, 0.2, 0.3]` as
79                                    // bracket-quoted identifiers (it inherits
80                                    // MSSQL-style `[name]` quoting). Detect
81                                    // that by `quote_style == Some('[')` and
82                                    // parse it eagerly into a typed
83                                    // `Value::Vector` so the rest of the
84                                    // pipeline sees a real vector. Dimension
85                                    // checking against the column declaration
86                                    // happens at insert_row time.
87                                    if i.quote_style == Some('[') {
88                                        let raw = format!("[{}]", i.value);
89                                        let parsed = parse_vector_literal(&raw).map_err(|e| {
90                                            SQLRiteError::General(format!(
91                                                "Could not parse vector literal '{raw}': {e}"
92                                            ))
93                                        })?;
94                                        value_set.push(Some(Value::Vector(parsed)));
95                                    } else {
96                                        value_set.push(Some(Value::Text(i.to_string())));
97                                    }
98                                }
99                                _ => {}
100                            }
101                        }
102                        all_values.push(value_set);
103                    }
104                }
105            }
106            _ => {
107                return Err(SQLRiteError::Internal(
108                    "Error parsing insert query".to_string(),
109                ));
110            }
111        }
112
113        match tname {
114            Some(t) => Ok(InsertQuery {
115                table_name: t,
116                columns,
117                rows: all_values,
118            }),
119            None => Err(SQLRiteError::Internal(
120                "Error parsing insert query".to_string(),
121            )),
122        }
123    }
124}