vibesql_parser/parser/
table_options.rs

1use super::{ParseError, Parser};
2use crate::{keywords::Keyword, token::Token};
3
4impl Parser {
5    /// Parse MySQL table options for CREATE TABLE
6    pub fn parse_table_options(&mut self) -> Result<Vec<vibesql_ast::TableOption>, ParseError> {
7        let mut options = Vec::new();
8
9        loop {
10            // Check for table option keywords
11            let option = if self.try_consume_keyword(Keyword::KeyBlockSize) {
12                self.parse_key_block_size_option()?
13            } else if self.try_consume_keyword(Keyword::Connection) {
14                self.parse_connection_option()?
15            } else if self.try_consume_keyword(Keyword::InsertMethod) {
16                self.parse_insert_method_option()?
17            } else if self.try_consume_keyword(Keyword::Union) {
18                self.parse_union_option()?
19            } else if self.try_consume_keyword(Keyword::RowFormat) {
20                self.parse_row_format_option()?
21            } else if self.try_consume_keyword(Keyword::DelayKeyWrite) {
22                self.parse_delay_key_write_option()?
23            } else if self.try_consume_keyword(Keyword::TableChecksum)
24                || self.try_consume_keyword(Keyword::Checksum)
25            {
26                self.parse_table_checksum_option()?
27            } else if self.try_consume_keyword(Keyword::StatsSamplePages) {
28                self.parse_stats_sample_pages_option()?
29            } else if self.try_consume_keyword(Keyword::Password) {
30                self.parse_password_option()?
31            } else if self.try_consume_keyword(Keyword::AvgRowLength) {
32                self.parse_avg_row_length_option()?
33            } else if self.try_consume_keyword(Keyword::MinRows) {
34                self.parse_min_rows_option()?
35            } else if self.try_consume_keyword(Keyword::MaxRows) {
36                self.parse_max_rows_option()?
37            } else if self.try_consume_keyword(Keyword::SecondaryEngine) {
38                self.parse_secondary_engine_option()?
39            } else if self.try_consume_keyword(Keyword::Collate) {
40                self.parse_collate_option()?
41            } else if self.try_consume_keyword(Keyword::Comment) {
42                self.parse_comment_option()?
43            } else if self.try_consume_keyword(Keyword::Storage) {
44                self.parse_storage_option()?
45            } else {
46                // No more table options
47                break;
48            };
49
50            options.push(option);
51
52            // Table options can be separated by commas (MySQL style) or just spaces
53            self.try_consume(&Token::Comma);
54        }
55
56        Ok(options)
57    }
58
59    fn parse_key_block_size_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
60        // Optional = sign
61        self.try_consume(&Token::Symbol('='));
62        // Parse value
63        let value = self.parse_numeric_value()?;
64        Ok(vibesql_ast::TableOption::KeyBlockSize(value))
65    }
66
67    fn parse_connection_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
68        // Optional = sign
69        self.try_consume(&Token::Symbol('='));
70        // Parse string value
71        match self.peek() {
72            Token::String(s) => {
73                let val = s.clone();
74                self.advance();
75                Ok(vibesql_ast::TableOption::Connection(Some(val)))
76            }
77            _ => Err(ParseError { message: "Expected string value for CONNECTION".to_string() }),
78        }
79    }
80
81    fn parse_insert_method_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
82        // Optional = sign
83        self.try_consume(&Token::Symbol('='));
84        let method = if self.try_consume_keyword(Keyword::First) {
85            vibesql_ast::InsertMethod::First
86        } else if self.try_consume_keyword(Keyword::Last) {
87            vibesql_ast::InsertMethod::Last
88        } else if self.try_consume_keyword(Keyword::No) {
89            vibesql_ast::InsertMethod::No
90        } else {
91            return Err(ParseError {
92                message: "Expected FIRST, LAST, or NO for INSERT_METHOD".to_string(),
93            });
94        };
95        Ok(vibesql_ast::TableOption::InsertMethod(method))
96    }
97
98    fn parse_union_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
99        // Optional = sign
100        self.try_consume(&Token::Symbol('='));
101        // Optional parentheses
102        self.try_consume(&Token::LParen);
103        let mut tables = Vec::new();
104        if !self.try_consume(&Token::RParen) {
105            loop {
106                match self.peek() {
107                    Token::Identifier(id) | Token::DelimitedIdentifier(id) => {
108                        tables.push(id.clone());
109                        self.advance();
110                    }
111                    _ => {
112                        return Err(ParseError {
113                            message: "Expected table name in UNION".to_string(),
114                        })
115                    }
116                }
117                if self.try_consume(&Token::Comma) {
118                    continue;
119                } else {
120                    break;
121                }
122            }
123            self.expect_token(Token::RParen)?;
124        }
125        Ok(vibesql_ast::TableOption::Union(Some(tables)))
126    }
127
128    fn parse_row_format_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
129        // Optional = sign
130        self.try_consume(&Token::Symbol('='));
131        let format = if self.try_consume_keyword(Keyword::Default) {
132            vibesql_ast::RowFormat::Default
133        } else if self.try_consume_keyword(Keyword::Dynamic) {
134            vibesql_ast::RowFormat::Dynamic
135        } else if self.try_consume_keyword(Keyword::Fixed) {
136            vibesql_ast::RowFormat::Fixed
137        } else if self.try_consume_keyword(Keyword::Compressed) {
138            vibesql_ast::RowFormat::Compressed
139        } else if self.try_consume_keyword(Keyword::Redundant) {
140            vibesql_ast::RowFormat::Redundant
141        } else if self.try_consume_keyword(Keyword::Compact) {
142            vibesql_ast::RowFormat::Compact
143        } else {
144            return Err(ParseError { message: "Expected row format value".to_string() });
145        };
146        Ok(vibesql_ast::TableOption::RowFormat(Some(format)))
147    }
148
149    fn parse_delay_key_write_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
150        // Optional = sign
151        self.try_consume(&Token::Symbol('='));
152        // Parse numeric value
153        let value = self.parse_numeric_value()?;
154        Ok(vibesql_ast::TableOption::DelayKeyWrite(value))
155    }
156
157    fn parse_table_checksum_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
158        // Optional = sign
159        self.try_consume(&Token::Symbol('='));
160        // Parse numeric value
161        let value = self.parse_numeric_value()?;
162        Ok(vibesql_ast::TableOption::TableChecksum(value))
163    }
164
165    fn parse_stats_sample_pages_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
166        // Optional = sign
167        self.try_consume(&Token::Symbol('='));
168        // Parse numeric value
169        let value = self.parse_numeric_value()?;
170        Ok(vibesql_ast::TableOption::StatsSamplePages(value))
171    }
172
173    fn parse_password_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
174        // Optional = sign
175        self.try_consume(&Token::Symbol('='));
176        // Parse string value
177        match self.peek() {
178            Token::String(s) => {
179                let val = s.clone();
180                self.advance();
181                Ok(vibesql_ast::TableOption::Password(Some(val)))
182            }
183            _ => Err(ParseError { message: "Expected string value for PASSWORD".to_string() }),
184        }
185    }
186
187    fn parse_avg_row_length_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
188        // Optional = sign
189        self.try_consume(&Token::Symbol('='));
190        // Parse numeric value
191        let value = self.parse_numeric_value()?;
192        Ok(vibesql_ast::TableOption::AvgRowLength(value))
193    }
194
195    fn parse_min_rows_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
196        // Optional = sign
197        self.try_consume(&Token::Symbol('='));
198        // Parse numeric value
199        let value = self.parse_numeric_value()?;
200        Ok(vibesql_ast::TableOption::MinRows(value))
201    }
202
203    fn parse_max_rows_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
204        // Optional = sign
205        self.try_consume(&Token::Symbol('='));
206        // Parse numeric value
207        let value = self.parse_numeric_value()?;
208        Ok(vibesql_ast::TableOption::MaxRows(value))
209    }
210
211    fn parse_secondary_engine_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
212        // Optional = sign
213        self.try_consume(&Token::Symbol('='));
214        // Parse identifier or NULL
215        let value = if self.try_consume_keyword(Keyword::Null) {
216            Some("NULL".to_string())
217        } else if let Token::Identifier(id) = self.peek() {
218            let val = id.clone();
219            self.advance();
220            Some(val)
221        } else {
222            None
223        };
224        Ok(vibesql_ast::TableOption::SecondaryEngine(value))
225    }
226
227    fn parse_collate_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
228        // Optional = sign
229        self.try_consume(&Token::Symbol('='));
230        // Parse collation name
231        match self.peek() {
232            Token::Identifier(id) => {
233                let val = id.clone();
234                self.advance();
235                Ok(vibesql_ast::TableOption::Collate(Some(val)))
236            }
237            _ => Err(ParseError { message: "Expected collation name".to_string() }),
238        }
239    }
240
241    fn parse_comment_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
242        // Optional = sign
243        self.try_consume(&Token::Symbol('='));
244        // Parse string value
245        match self.peek() {
246            Token::String(s) => {
247                let val = s.clone();
248                self.advance();
249                Ok(vibesql_ast::TableOption::Comment(Some(val)))
250            }
251            _ => Err(ParseError { message: "Expected string value for COMMENT".to_string() }),
252        }
253    }
254
255    /// Parse STORAGE = {ROW | COLUMNAR} option (VibeSQL extension)
256    fn parse_storage_option(&mut self) -> Result<vibesql_ast::TableOption, ParseError> {
257        // Optional = sign
258        self.try_consume(&Token::Symbol('='));
259        // Parse storage format value
260        let format = if self.try_consume_keyword(Keyword::Row) {
261            vibesql_ast::StorageFormat::Row
262        } else if self.try_consume_keyword(Keyword::Columnar) {
263            vibesql_ast::StorageFormat::Columnar
264        } else {
265            return Err(ParseError { message: "Expected ROW or COLUMNAR for STORAGE".to_string() });
266        };
267        Ok(vibesql_ast::TableOption::Storage(format))
268    }
269
270    /// Parse a numeric value that can be integer or float (converts float to int by truncation)
271    pub(super) fn parse_numeric_value(&mut self) -> Result<Option<i64>, ParseError> {
272        if let Token::Number(n) = self.peek() {
273            // Try parsing as i64 first, then as f64 and truncate
274            let val = if let Ok(int_val) = n.parse::<i64>() {
275                int_val
276            } else if let Ok(float_val) = n.parse::<f64>() {
277                float_val as i64
278            } else {
279                return Err(ParseError { message: "Invalid numeric value".to_string() });
280            };
281            self.advance();
282            Ok(Some(val))
283        } else {
284            Ok(None)
285        }
286    }
287}