sqlrite/sql/parser/
select.rs1use sqlparser::ast::{
2 Expr, LimitClause, OrderByKind, Query, Select, SelectItem, SetExpr, Statement, TableFactor,
3 TableWithJoins,
4};
5
6use crate::error::{Result, SQLRiteError};
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum Projection {
11 All,
13 Columns(Vec<String>),
15}
16
17#[derive(Debug, Clone)]
19pub struct OrderByClause {
20 pub column: String,
21 pub ascending: bool,
22}
23
24#[derive(Debug, Clone)]
26pub struct SelectQuery {
27 pub table_name: String,
28 pub projection: Projection,
29 pub selection: Option<Expr>,
31 pub order_by: Option<OrderByClause>,
32 pub limit: Option<usize>,
33}
34
35impl SelectQuery {
36 pub fn new(statement: &Statement) -> Result<Self> {
37 let Statement::Query(query) = statement else {
38 return Err(SQLRiteError::Internal(
39 "Error parsing SELECT: expected a Query statement".to_string(),
40 ));
41 };
42
43 let Query {
44 body,
45 order_by,
46 limit_clause,
47 ..
48 } = query.as_ref();
49
50 let SetExpr::Select(select) = body.as_ref() else {
51 return Err(SQLRiteError::NotImplemented(
52 "Only simple SELECT queries are supported (no UNION / VALUES / CTEs yet)"
53 .to_string(),
54 ));
55 };
56 let Select {
57 projection,
58 from,
59 selection,
60 distinct,
61 group_by,
62 having,
63 ..
64 } = select.as_ref();
65
66 if distinct.is_some() {
67 return Err(SQLRiteError::NotImplemented(
68 "SELECT DISTINCT is not supported yet".to_string(),
69 ));
70 }
71 if having.is_some() {
72 return Err(SQLRiteError::NotImplemented(
73 "HAVING is not supported yet".to_string(),
74 ));
75 }
76 if let sqlparser::ast::GroupByExpr::Expressions(exprs, _) = group_by {
78 if !exprs.is_empty() {
79 return Err(SQLRiteError::NotImplemented(
80 "GROUP BY is not supported yet".to_string(),
81 ));
82 }
83 } else {
84 return Err(SQLRiteError::NotImplemented(
85 "GROUP BY ALL is not supported".to_string(),
86 ));
87 }
88
89 let table_name = extract_single_table_name(from)?;
90 let projection = parse_projection(projection)?;
91 let order_by = parse_order_by(order_by.as_ref())?;
92 let limit = parse_limit(limit_clause.as_ref())?;
93
94 Ok(SelectQuery {
95 table_name,
96 projection,
97 selection: selection.clone(),
98 order_by,
99 limit,
100 })
101 }
102}
103
104fn extract_single_table_name(from: &[TableWithJoins]) -> Result<String> {
105 if from.len() != 1 {
106 return Err(SQLRiteError::NotImplemented(
107 "SELECT from multiple tables (joins / comma-joins) is not supported yet".to_string(),
108 ));
109 }
110 let twj = &from[0];
111 if !twj.joins.is_empty() {
112 return Err(SQLRiteError::NotImplemented(
113 "JOIN is not supported yet".to_string(),
114 ));
115 }
116 match &twj.relation {
117 TableFactor::Table { name, .. } => Ok(name.to_string()),
118 _ => Err(SQLRiteError::NotImplemented(
119 "Only SELECT from a plain table is supported".to_string(),
120 )),
121 }
122}
123
124fn parse_projection(items: &[SelectItem]) -> Result<Projection> {
125 if items.len() == 1 {
127 if let SelectItem::Wildcard(_) = &items[0] {
128 return Ok(Projection::All);
129 }
130 }
131 let mut cols = Vec::with_capacity(items.len());
132 for item in items {
133 match item {
134 SelectItem::UnnamedExpr(Expr::Identifier(ident)) => cols.push(ident.value.clone()),
135 SelectItem::UnnamedExpr(Expr::CompoundIdentifier(parts)) => {
136 if let Some(last) = parts.last() {
137 cols.push(last.value.clone());
138 } else {
139 return Err(SQLRiteError::Internal(
140 "empty qualified column reference".to_string(),
141 ));
142 }
143 }
144 SelectItem::Wildcard(_) | SelectItem::QualifiedWildcard(_, _) => {
145 return Err(SQLRiteError::NotImplemented(
146 "Wildcard mixed with other columns is not supported".to_string(),
147 ));
148 }
149 SelectItem::ExprWithAlias { .. } | SelectItem::UnnamedExpr(_) => {
150 return Err(SQLRiteError::NotImplemented(
151 "Only bare column references are supported in the projection list".to_string(),
152 ));
153 }
154 }
155 }
156 Ok(Projection::Columns(cols))
157}
158
159fn parse_order_by(order_by: Option<&sqlparser::ast::OrderBy>) -> Result<Option<OrderByClause>> {
160 let Some(ob) = order_by else {
161 return Ok(None);
162 };
163 let exprs = match &ob.kind {
164 OrderByKind::Expressions(v) => v,
165 OrderByKind::All(_) => {
166 return Err(SQLRiteError::NotImplemented(
167 "ORDER BY ALL is not supported".to_string(),
168 ));
169 }
170 };
171 if exprs.len() != 1 {
172 return Err(SQLRiteError::NotImplemented(
173 "ORDER BY must have exactly one column for now".to_string(),
174 ));
175 }
176 let obe = &exprs[0];
177 let column = match &obe.expr {
178 Expr::Identifier(ident) => ident.value.clone(),
179 Expr::CompoundIdentifier(parts) => {
180 parts.last().map(|i| i.value.clone()).unwrap_or_default()
181 }
182 _ => {
183 return Err(SQLRiteError::NotImplemented(
184 "ORDER BY only supports a bare column name for now".to_string(),
185 ));
186 }
187 };
188 let ascending = obe.options.asc.unwrap_or(true);
190 Ok(Some(OrderByClause { column, ascending }))
191}
192
193fn parse_limit(limit: Option<&LimitClause>) -> Result<Option<usize>> {
194 let Some(lc) = limit else {
195 return Ok(None);
196 };
197 let limit_expr = match lc {
198 LimitClause::LimitOffset { limit, offset, .. } => {
199 if offset.is_some() {
200 return Err(SQLRiteError::NotImplemented(
201 "OFFSET is not supported yet".to_string(),
202 ));
203 }
204 limit.as_ref()
205 }
206 LimitClause::OffsetCommaLimit { .. } => {
207 return Err(SQLRiteError::NotImplemented(
208 "`LIMIT <offset>, <limit>` syntax is not supported yet".to_string(),
209 ));
210 }
211 };
212 let Some(expr) = limit_expr else {
213 return Ok(None);
214 };
215 let n = eval_const_usize(expr)?;
216 Ok(Some(n))
217}
218
219fn eval_const_usize(expr: &Expr) -> Result<usize> {
220 match expr {
221 Expr::Value(v) => match &v.value {
222 sqlparser::ast::Value::Number(n, _) => n.parse::<usize>().map_err(|e| {
223 SQLRiteError::Internal(format!("LIMIT must be a non-negative integer: {e}"))
224 }),
225 _ => Err(SQLRiteError::Internal(
226 "LIMIT must be an integer literal".to_string(),
227 )),
228 },
229 _ => Err(SQLRiteError::NotImplemented(
230 "LIMIT expression must be a literal number".to_string(),
231 )),
232 }
233}