sqlite3_parser/parser/ast/
check.rs

1//! Check for additional syntax error
2use crate::ast::*;
3use crate::custom_err;
4use std::fmt::{Display, Formatter};
5
6impl Cmd {
7    /// Statement accessor
8    pub fn stmt(&self) -> &Stmt {
9        match self {
10            Self::Explain(stmt) => stmt,
11            Self::ExplainQueryPlan(stmt) => stmt,
12            Self::Stmt(stmt) => stmt,
13        }
14    }
15    /// Like `sqlite3_column_count` but more limited
16    pub fn column_count(&self) -> ColumnCount {
17        match self {
18            Self::Explain(_) => ColumnCount::Fixed(8),
19            Self::ExplainQueryPlan(_) => ColumnCount::Fixed(4),
20            Self::Stmt(stmt) => stmt.column_count(),
21        }
22    }
23    /// Like `sqlite3_stmt_isexplain`
24    pub fn is_explain(&self) -> bool {
25        matches!(self, Self::Explain(_) | Self::ExplainQueryPlan(_))
26    }
27    /// Like `sqlite3_stmt_readonly`
28    pub fn readonly(&self) -> bool {
29        self.stmt().readonly()
30    }
31    /// check for extra rules
32    pub fn check(&self) -> Result<(), ParserError> {
33        self.stmt().check()
34    }
35}
36
37/// Column count
38pub enum ColumnCount {
39    /// With `SELECT *` / PRAGMA
40    Dynamic,
41    /// Constant count
42    Fixed(usize),
43    /// No column
44    None,
45}
46
47impl ColumnCount {
48    fn incr(&mut self) {
49        if let Self::Fixed(n) = self {
50            *n += 1;
51        }
52    }
53}
54
55impl Stmt {
56    /// Like `sqlite3_column_count` but more limited
57    pub fn column_count(&self) -> ColumnCount {
58        match self {
59            Self::Delete {
60                returning: Some(returning),
61                ..
62            } => column_count(returning),
63            Self::Insert {
64                returning: Some(returning),
65                ..
66            } => column_count(returning),
67            Self::Pragma(..) => ColumnCount::Dynamic,
68            Self::Select(s) => s.column_count(),
69            Self::Update {
70                returning: Some(returning),
71                ..
72            } => column_count(returning),
73            _ => ColumnCount::None,
74        }
75    }
76
77    /// Like `sqlite3_stmt_readonly`
78    pub fn readonly(&self) -> bool {
79        match self {
80            Self::Attach { .. } => true,
81            Self::Begin(..) => true,
82            Self::Commit(..) => true,
83            Self::Detach(..) => true,
84            Self::Pragma(..) => true, // TODO check all
85            Self::Reindex { .. } => true,
86            Self::Release(..) => true,
87            Self::Rollback { .. } => true,
88            Self::Savepoint(..) => true,
89            Self::Select(..) => true,
90            _ => false,
91        }
92    }
93
94    /// check for extra rules
95    pub fn check(&self) -> Result<(), ParserError> {
96        match self {
97            Self::AlterTable(old_name, AlterTableBody::RenameTo(new_name)) => {
98                if *new_name == old_name.name {
99                    return Err(custom_err!(
100                        "there is already another table or index with this name: {}",
101                        new_name
102                    ));
103                }
104                Ok(())
105            }
106            Self::AlterTable(.., AlterTableBody::AddColumn(cd)) => {
107                for c in cd {
108                    if let ColumnConstraint::PrimaryKey { .. } = c {
109                        return Err(custom_err!("Cannot add a PRIMARY KEY column"));
110                    } else if let ColumnConstraint::Unique(..) = c {
111                        return Err(custom_err!("Cannot add a UNIQUE column"));
112                    }
113                }
114                Ok(())
115            }
116            Self::CreateTable {
117                temporary,
118                tbl_name,
119                body,
120                ..
121            } => {
122                if *temporary {
123                    if let Some(ref db_name) = tbl_name.db_name {
124                        if db_name != "TEMP" {
125                            return Err(custom_err!("temporary table name must be unqualified"));
126                        }
127                    }
128                }
129                body.check(tbl_name)
130            }
131            Self::CreateView {
132                view_name,
133                columns: Some(columns),
134                select,
135                ..
136            } => {
137                // SQLite3 engine renames duplicates:
138                for (i, c) in columns.iter().enumerate() {
139                    for o in &columns[i + 1..] {
140                        if c.col_name == o.col_name {
141                            return Err(custom_err!("duplicate column name: {}", c.col_name,));
142                        }
143                    }
144                }
145                // SQLite3 engine raises this error later (not while parsing):
146                match select.column_count() {
147                    ColumnCount::Fixed(n) if n != columns.len() => Err(custom_err!(
148                        "expected {} columns for {} but got {}",
149                        columns.len(),
150                        view_name,
151                        n
152                    )),
153                    _ => Ok(()),
154                }
155            }
156            Self::Delete {
157                order_by: Some(_),
158                limit: None,
159                ..
160            } => Err(custom_err!("ORDER BY without LIMIT on DELETE")),
161            Self::Insert {
162                columns: Some(columns),
163                body: InsertBody::Select(select, ..),
164                ..
165            } => match select.body.select.column_count() {
166                ColumnCount::Fixed(n) if n != columns.len() => {
167                    Err(custom_err!("{} values for {} columns", n, columns.len()))
168                }
169                _ => Ok(()),
170            },
171            Self::Insert {
172                columns: Some(columns),
173                body: InsertBody::DefaultValues,
174                ..
175            } => Err(custom_err!("0 values for {} columns", columns.len())),
176            Self::Update {
177                order_by: Some(_),
178                limit: None,
179                ..
180            } => Err(custom_err!("ORDER BY without LIMIT on UPDATE")),
181            _ => Ok(()),
182        }
183    }
184}
185
186impl CreateTableBody {
187    /// check for extra rules
188    pub fn check(&self, tbl_name: &QualifiedName) -> Result<(), ParserError> {
189        if let Self::ColumnsAndConstraints {
190            columns,
191            constraints: _,
192            options,
193        } = self
194        {
195            let mut generated_count = 0;
196            for c in columns.values() {
197                for cs in &c.constraints {
198                    if let ColumnConstraint::Generated { .. } = cs.constraint {
199                        generated_count += 1;
200                    }
201                }
202            }
203            if generated_count == columns.len() {
204                return Err(custom_err!("must have at least one non-generated column"));
205            }
206
207            if options.contains(TableOptions::STRICT) {
208                for c in columns.values() {
209                    match &c.col_type {
210                        Some(Type { name, .. }) => {
211                            // The datatype must be one of following: INT INTEGER REAL TEXT BLOB ANY
212                            if !(name.eq_ignore_ascii_case("INT")
213                                || name.eq_ignore_ascii_case("INTEGER")
214                                || name.eq_ignore_ascii_case("REAL")
215                                || name.eq_ignore_ascii_case("TEXT")
216                                || name.eq_ignore_ascii_case("BLOB")
217                                || name.eq_ignore_ascii_case("ANY"))
218                            {
219                                return Err(custom_err!(
220                                    "unknown datatype for {}.{}: \"{}\"",
221                                    tbl_name,
222                                    c.col_name,
223                                    name
224                                ));
225                            }
226                        }
227                        _ => {
228                            // Every column definition must specify a datatype for that column. The freedom to specify a column without a datatype is removed.
229                            return Err(custom_err!(
230                                "missing datatype for {}.{}",
231                                tbl_name,
232                                c.col_name
233                            ));
234                        }
235                    }
236                }
237            }
238            if options.contains(TableOptions::WITHOUT_ROWID) && !self.has_primary_key() {
239                return Err(custom_err!("PRIMARY KEY missing on table {}", tbl_name,));
240            }
241        }
242        Ok(())
243    }
244
245    /// explicit primary key constraint ?
246    pub fn has_primary_key(&self) -> bool {
247        if let Self::ColumnsAndConstraints {
248            columns,
249            constraints,
250            ..
251        } = self
252        {
253            for col in columns.values() {
254                for c in col {
255                    if let ColumnConstraint::PrimaryKey { .. } = c {
256                        return true;
257                    }
258                }
259            }
260            if let Some(constraints) = constraints {
261                for c in constraints {
262                    if let TableConstraint::PrimaryKey { .. } = c.constraint {
263                        return true;
264                    }
265                }
266            }
267        }
268        false
269    }
270}
271
272impl<'a> IntoIterator for &'a ColumnDefinition {
273    type Item = &'a ColumnConstraint;
274    type IntoIter = std::iter::Map<
275        std::slice::Iter<'a, NamedColumnConstraint>,
276        fn(&'a NamedColumnConstraint) -> &'a ColumnConstraint,
277    >;
278
279    fn into_iter(self) -> Self::IntoIter {
280        self.constraints.iter().map(|nc| &nc.constraint)
281    }
282}
283
284impl Select {
285    /// Like `sqlite3_column_count` but more limited
286    pub fn column_count(&self) -> ColumnCount {
287        self.body.select.column_count()
288    }
289}
290
291impl OneSelect {
292    /// Like `sqlite3_column_count` but more limited
293    pub fn column_count(&self) -> ColumnCount {
294        match self {
295            Self::Select { columns, .. } => column_count(columns),
296            Self::Values(values) => {
297                assert!(!values.is_empty()); // TODO Validate
298                ColumnCount::Fixed(values[0].len())
299            }
300        }
301    }
302    /// Check all VALUES have the same number of terms
303    pub fn push(values: &mut Vec<Vec<Expr>>, v: Vec<Expr>) -> Result<(), ParserError> {
304        if values[0].len() != v.len() {
305            return Err(custom_err!("all VALUES must have the same number of terms"));
306        }
307        values.push(v);
308        Ok(())
309    }
310}
311
312impl Display for QualifiedName {
313    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
314        self.to_fmt(f)
315    }
316}
317
318impl ResultColumn {
319    fn column_count(&self) -> ColumnCount {
320        match self {
321            Self::Expr(..) => ColumnCount::Fixed(1),
322            _ => ColumnCount::Dynamic,
323        }
324    }
325}
326fn column_count(cols: &[ResultColumn]) -> ColumnCount {
327    assert!(!cols.is_empty());
328    let mut count = ColumnCount::Fixed(0);
329    for col in cols {
330        match col.column_count() {
331            ColumnCount::Fixed(_) => count.incr(),
332            _ => return ColumnCount::Dynamic,
333        }
334    }
335    count
336}