sqlite_fsr/command/sql/parser/
sql_statement.rs

1
2use std::iter::Peekable;
3
4use crate::{command::sql::parser::{sql_token::{Symbol, Tokenize}, SQLToken}, models::error::SQLSyntaxError};
5
6pub enum SQLStatement {
7    Select(SelectStatement),
8    CreateTable(CreateTableStatement),
9}
10
11#[derive(Debug)]
12pub struct CreateTableStatement {
13    pub table_name: String,
14    pub columns: Vec<String>,
15    pub integer_primary_key_column: Option<usize>
16}
17
18impl CreateTableStatement {
19    
20    pub fn from_tokens(tokens: Vec<SQLToken>) -> Self {
21        
22        let mut tokens_cursor = tokens.into_iter().peekable();
23
24        if let Some(SQLToken::Identifier(second_word)) = tokens_cursor.nth(1) { assert!(second_word == "TABLE"); } 
25        else { panic!(); }
26
27        let mut table_name = String::new();
28        if let Some(SQLToken::Identifier(third_word)) = tokens_cursor.next() { table_name = third_word }
29        else { panic!(); }
30
31        let columns_defintions = Self::extract_column_definitions(&mut tokens_cursor);
32        let integer_primary_key_column = columns_defintions.iter().position(|column_definition| {column_definition.len() >= 4 && (column_definition[1].to_uppercase() == "INTEGER" && column_definition[2].to_uppercase() == "PRIMARY" && column_definition[3].to_uppercase() == "KEY") } );
33        let mut columns: Vec<String> = columns_defintions.iter()
34                                        .map(|column_defintion| column_defintion[0].clone())
35                                        .collect();
36        
37
38        Self { table_name, columns, integer_primary_key_column }
39    }
40
41    fn extract_column_definitions(tokens_iterator: &mut Peekable<std::vec::IntoIter<SQLToken>>) -> Vec<Vec<String>> {
42        let mut column_definitions: Vec<Vec<String>> = Vec::new();
43        if let Some(SQLToken::Symbol(Symbol::LeftParenthesis)) = tokens_iterator.next() {
44
45            while !matches!(tokens_iterator.peek(), Some(&SQLToken::Symbol(Symbol::RightParenthesis))) & !matches!(tokens_iterator.peek(), None){
46                let mut column_defintion_components: Vec<String> = Vec::new();
47
48                loop {
49                    let token = tokens_iterator.next_if(|t| !matches!(t, SQLToken::Symbol(Symbol::RightParenthesis)));
50                    match token {
51                        Some(SQLToken::Identifier(column_defintion_component)) => column_defintion_components.push(column_defintion_component.to_string()),
52                        Some(SQLToken::Symbol(Symbol::Comma)) => { break; }
53                        _ => break
54                    }
55                }
56                column_definitions.push(column_defintion_components);
57            }
58
59        } else { panic!() }
60
61        return column_definitions;
62    }
63
64}
65
66pub struct SelectStatement {
67    pub table_name: String,
68    pub columns: Option<Vec<String>>,
69    pub where_clause: Option<Condition>,
70    pub aggregator_function: Option<AggregatorFunction>
71}
72pub struct Condition {
73    pub left: String,
74    pub operator: String,
75    pub right: String
76}
77
78#[derive(Debug, PartialEq)]
79pub enum AggregatorFunction {
80    COUNT,
81    SUM
82}
83
84impl SelectStatement {
85    pub fn from_tokens(tokens: Vec<SQLToken>) -> Self {
86        let mut tokens_cursor = tokens.into_iter().peekable();
87
88        if let Some(SQLToken::Keyword(first_word)) = tokens_cursor.nth(0) { assert!(first_word == "SELECT") }
89        else { panic!() }
90                
91        tokens_cursor.next_if(|t| matches!(t, SQLToken::Symbol(Symbol::LeftParenthesis)));
92
93        let aggregator_function = if let Some(SQLToken::Identifier(token)) = tokens_cursor.peek() {
94                                        match token.as_str() {
95                                            "COUNT" => {
96                                                tokens_cursor.next();
97                                                Some(AggregatorFunction::COUNT)
98                                            },
99                                            "SUM" => {
100                                                tokens_cursor.next();
101                                                Some(AggregatorFunction::SUM)
102                                            },
103                                            _ => None
104                                        }                
105                                  } else { panic!("Was expecting Indetifier token.") };
106
107        tokens_cursor.next_if(|t| matches!(t, SQLToken::Symbol(Symbol::LeftParenthesis)));
108
109        let columns: Option<Vec<String>> = if let Some(SQLToken::Identifier(token)) = tokens_cursor.peek() {
110                                                match token.as_str() {
111                                                    "*" => {
112                                                        tokens_cursor.next();
113                                                        None
114                                                    },
115                                                    _ => Self::extract_columns(&mut tokens_cursor)
116                                                }
117                                            } else { panic!() };
118
119        tokens_cursor.next_if(|t| matches!(t, SQLToken::Symbol(Symbol::RightParenthesis)));
120
121        if let Some(SQLToken::Keyword(first_word_after_columns)) = tokens_cursor.next() { assert!(first_word_after_columns == "FROM") }
122        else { panic!() }
123
124        let table_name = match tokens_cursor.next() {
125                            Some(SQLToken::Identifier(tablename)) => tablename,
126                            _ => panic!()
127                         };
128
129        Self { columns, table_name, where_clause: None, aggregator_function }
130    }
131
132    fn extract_columns(tokens_iterator: &mut Peekable<std::vec::IntoIter<SQLToken>>) -> Option<Vec<String>> {
133        let mut columns: Vec<String> = Vec::new();
134
135        while !matches!(tokens_iterator.peek(), Some(&SQLToken::Symbol(Symbol::RightParenthesis)))
136        & !matches!(tokens_iterator.peek(), Some(&SQLToken::Keyword(_))) 
137        & !matches!(tokens_iterator.peek(), None) {
138
139            let token = tokens_iterator.next();
140            match token {
141                Some(SQLToken::Identifier(column)) => columns.push(column),
142                Some(SQLToken::Symbol(Symbol::Comma)) => continue,
143                Some(SQLToken::Symbol(Symbol::RightParenthesis)) => break,
144                _ => panic!()
145            }
146            
147        }
148
149        match columns.len() {
150            0 => return None,
151            _ => return Some(columns)
152        }
153    }
154}
155
156
157
158pub trait ToSQLStatement {
159    fn to_sql_statment(&self) -> Result<SQLStatement, SQLSyntaxError>;
160}
161
162impl ToSQLStatement for &str {
163    fn to_sql_statment(&self) -> Result<SQLStatement, SQLSyntaxError> {
164        let tokens: Vec<SQLToken> = self.tokenize();
165                            
166        match &tokens[0] {
167            SQLToken::Keyword(s) if s == "CREATE" => Ok(SQLStatement::CreateTable(CreateTableStatement::from_tokens(tokens))),
168            SQLToken::Keyword(s) if s == "SELECT" => Ok(SQLStatement::Select(SelectStatement::from_tokens(tokens))),            
169            _ => Err(SQLSyntaxError::UnexpectedToken(String::new()))
170        }
171    }
172}
173
174impl ToSQLStatement for Vec<&str> {
175    fn to_sql_statment(&self) -> Result<SQLStatement, SQLSyntaxError> {
176        let joined = self.join(" ");
177        joined.as_str().to_sql_statment()
178    }
179}
180
181
182
183pub struct CreateIndexStatement {
184    tablename: String,
185    columns: Vec<String>
186}
187
188impl CreateIndexStatement {
189    pub fn from_tokens(tokens: Vec<SQLToken>) -> Self {
190        let tablename = String::new();
191        let columns = Vec::new();
192
193        CreateIndexStatement { tablename, columns }
194    }
195}