1use super::*;
16
17impl Parser {
18 pub(super) fn parse_begin_statement(&mut self) -> Option<BeginStatement> {
20 let token = self.cur_token.clone();
21
22 if self.peek_token_is_keyword("TRANSACTION") {
24 self.next_token();
25 }
26
27 let isolation_level = if self.peek_token_is_keyword("ISOLATION") {
29 self.next_token();
30 if !self.expect_keyword("LEVEL") {
31 return None;
32 }
33 self.next_token();
34
35 let level = self.cur_token.literal.to_uppercase();
36 let isolation = match level.as_str() {
37 "SNAPSHOT" | "SERIALIZABLE" => level,
38 "REPEATABLE" => {
39 if !self.expect_keyword("READ") {
40 return None;
41 }
42 SmartString::const_new("REPEATABLE READ")
43 }
44 "READ" => {
45 if self.peek_token_is_keyword("UNCOMMITTED") {
46 self.next_token();
47 SmartString::const_new("READ UNCOMMITTED")
48 } else if self.peek_token_is_keyword("COMMITTED") {
49 self.next_token();
50 SmartString::const_new("READ COMMITTED")
51 } else {
52 self.add_error(format!(
53 "expected UNCOMMITTED or COMMITTED after READ at {}",
54 self.cur_token.position
55 ));
56 return None;
57 }
58 }
59 _ => {
60 self.add_error(format!(
61 "invalid isolation level: {} at {}",
62 level, self.cur_token.position
63 ));
64 return None;
65 }
66 };
67 Some(isolation)
68 } else {
69 None
70 };
71
72 Some(BeginStatement {
73 token,
74 isolation_level,
75 })
76 }
77
78 pub(super) fn parse_commit_statement(&mut self) -> Option<CommitStatement> {
80 let token = self.cur_token.clone();
81
82 if self.peek_token_is_keyword("TRANSACTION") {
84 self.next_token();
85 }
86
87 Some(CommitStatement { token })
88 }
89
90 pub(super) fn parse_rollback_statement(&mut self) -> Option<RollbackStatement> {
92 let token = self.cur_token.clone();
93
94 if self.peek_token_is_keyword("TRANSACTION") {
96 self.next_token();
97 }
98
99 let savepoint_name = if self.peek_token_is_keyword("TO") {
101 self.next_token();
102 if self.peek_token_is_keyword("SAVEPOINT") {
103 self.next_token();
104 }
105 if !self.expect_peek(TokenType::Identifier) {
106 return None;
107 }
108 Some(Identifier::new(
109 self.cur_token.clone(),
110 self.cur_token.literal.clone(),
111 ))
112 } else {
113 None
114 };
115
116 Some(RollbackStatement {
117 token,
118 savepoint_name,
119 })
120 }
121
122 pub(super) fn parse_savepoint_statement(&mut self) -> Option<SavepointStatement> {
124 let token = self.cur_token.clone();
125
126 if !self.expect_peek(TokenType::Identifier) {
127 return None;
128 }
129
130 let savepoint_name =
131 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
132
133 Some(SavepointStatement {
134 token,
135 savepoint_name,
136 })
137 }
138
139 pub(super) fn parse_release_savepoint_statement(
141 &mut self,
142 ) -> Option<ReleaseSavepointStatement> {
143 let token = self.cur_token.clone();
144
145 if self.peek_token_is_keyword("SAVEPOINT") {
147 self.next_token();
148 }
149
150 if !self.expect_peek(TokenType::Identifier) {
151 return None;
152 }
153
154 let savepoint_name =
155 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
156
157 Some(ReleaseSavepointStatement {
158 token,
159 savepoint_name,
160 })
161 }
162
163 pub(super) fn parse_set_statement(&mut self) -> Option<SetStatement> {
165 let token = self.cur_token.clone();
166
167 self.next_token();
168 if !self.cur_token_is(TokenType::Identifier) {
169 self.add_error(format!(
170 "expected variable name at {}",
171 self.cur_token.position
172 ));
173 return None;
174 }
175
176 let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
177
178 self.next_token();
179 let is_equals = self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=";
181 if !is_equals && !self.cur_token_is_keyword("TO") {
182 self.add_error(format!(
183 "expected '=' or 'TO' after variable name at {}",
184 self.cur_token.position
185 ));
186 return None;
187 }
188
189 self.next_token();
190 let value = self.parse_expression(Precedence::Lowest)?;
191
192 Some(SetStatement { token, name, value })
193 }
194
195 pub(super) fn parse_pragma_statement(&mut self) -> Option<PragmaStatement> {
197 let token = self.cur_token.clone();
198
199 self.next_token();
200 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
202 self.add_error(format!(
203 "expected pragma name at {}",
204 self.cur_token.position
205 ));
206 return None;
207 }
208
209 let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
210
211 self.next_token();
212
213 let value = if self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=" {
215 self.next_token();
216 Some(self.parse_expression(Precedence::Lowest)?)
217 } else {
218 None
219 };
220
221 Some(PragmaStatement { token, name, value })
222 }
223
224 pub(super) fn parse_show_statement(&mut self) -> Option<Statement> {
226 let token = self.cur_token.clone();
227
228 if self.peek_token_is_keyword("TABLES") {
229 self.next_token();
230 Some(Statement::ShowTables(ShowTablesStatement { token }))
231 } else if self.peek_token_is_keyword("VIEWS") {
232 self.next_token();
233 Some(Statement::ShowViews(ShowViewsStatement { token }))
234 } else if self.peek_token_is_keyword("CREATE") {
235 self.next_token();
236 if self.peek_token_is_keyword("TABLE") {
238 self.next_token();
239 if !self.expect_peek(TokenType::Identifier) {
240 return None;
241 }
242 let table_name =
243 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
244 Some(Statement::ShowCreateTable(ShowCreateTableStatement {
245 token,
246 table_name,
247 }))
248 } else if self.peek_token_is_keyword("VIEW") {
249 self.next_token();
250 if !self.expect_peek(TokenType::Identifier) {
251 return None;
252 }
253 let view_name =
254 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
255 Some(Statement::ShowCreateView(ShowCreateViewStatement {
256 token,
257 view_name,
258 }))
259 } else {
260 self.add_error(format!(
261 "expected TABLE or VIEW after SHOW CREATE at {}",
262 self.cur_token.position
263 ));
264 None
265 }
266 } else if self.peek_token_is_keyword("INDEXES") || self.peek_token_is_keyword("INDEX") {
267 self.next_token();
268 if !self.expect_keyword("FROM") {
269 return None;
270 }
271 if !self.expect_peek(TokenType::Identifier) {
272 return None;
273 }
274 let table_name =
275 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
276 Some(Statement::ShowIndexes(ShowIndexesStatement {
277 token,
278 table_name,
279 }))
280 } else {
281 self.add_error(format!(
282 "unsupported SHOW statement at {}",
283 self.cur_token.position
284 ));
285 None
286 }
287 }
288
289 pub(super) fn parse_describe_statement(&mut self) -> Option<DescribeStatement> {
291 let token = self.cur_token.clone();
292
293 self.next_token();
295
296 let target = if self.cur_token.literal.eq_ignore_ascii_case("DATABASE") {
297 DescribeTarget::Database
298 } else {
299 if self.cur_token_is_keyword("TABLE") {
301 self.next_token();
302 }
303 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
304 self.add_error(format!(
305 "expected TABLE name or DATABASE after DESCRIBE at {}",
306 self.cur_token.position
307 ));
308 return None;
309 }
310 DescribeTarget::Table(Identifier::new(
311 self.cur_token.clone(),
312 self.cur_token.literal.clone(),
313 ))
314 };
315
316 let format = if self.peek_token_is_keyword("FORMAT") {
317 self.next_token();
318 self.next_token();
319 if !self.cur_token_is_keyword("JSON") {
320 self.add_error(format!(
321 "expected JSON after DESCRIBE ... FORMAT at {}",
322 self.cur_token.position
323 ));
324 return None;
325 }
326 DescribeFormat::Json
327 } else {
328 DescribeFormat::Tabular
329 };
330
331 if matches!(target, DescribeTarget::Database) && format != DescribeFormat::Json {
332 self.add_error(
333 "DESCRIBE DATABASE requires FORMAT JSON; the legacy tabular format is table-only"
334 .to_string(),
335 );
336 return None;
337 }
338
339 Some(DescribeStatement {
340 token,
341 target,
342 format,
343 })
344 }
345
346 pub(super) fn parse_explain_statement(&mut self) -> Option<ExplainStatement> {
348 let token = self.cur_token.clone();
349
350 let analyze = if self.peek_token_is_keyword("ANALYZE") {
352 self.next_token();
353 true
354 } else {
355 false
356 };
357
358 self.next_token();
360
361 let statement = self.parse_statement()?;
363
364 Some(ExplainStatement {
365 token,
366 statement: Box::new(statement),
367 analyze,
368 })
369 }
370
371 pub(super) fn parse_analyze_statement(&mut self) -> Option<AnalyzeStatement> {
374 let token = self.cur_token.clone();
375
376 self.next_token();
378
379 let table_name = if self.cur_token_is(TokenType::Identifier)
381 || (self.cur_token_is(TokenType::Keyword)
382 && !self.cur_token.literal.eq_ignore_ascii_case("TABLE"))
383 {
384 let name = self.cur_token.literal.clone();
385 Some(name)
386 } else if self.cur_token_is(TokenType::Keyword)
387 && self.cur_token.literal.eq_ignore_ascii_case("TABLE")
388 {
389 self.next_token();
391 if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
392 let name = self.cur_token.literal.clone();
393 Some(name)
394 } else {
395 self.add_error(format!(
396 "expected table name after ANALYZE TABLE at {}",
397 self.cur_token.position
398 ));
399 return None;
400 }
401 } else {
402 None
403 };
404
405 Some(AnalyzeStatement { token, table_name })
406 }
407
408 pub(super) fn parse_expression_statement(&mut self) -> Option<ExpressionStatement> {
410 let token = self.cur_token.clone();
411 let expression = self.parse_expression(Precedence::Lowest)?;
412
413 Some(ExpressionStatement { token, expression })
414 }
415
416 pub fn parse_identifier_list(&mut self) -> Vec<Identifier> {
418 let mut list = Vec::new();
419
420 self.next_token();
421 if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
423 list.push(self.cur_token_as_column_identifier());
424 }
425
426 while self.peek_token_is_punctuator(",") {
427 self.next_token(); self.next_token(); if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
431 list.push(self.cur_token_as_column_identifier());
432 } else {
433 self.add_error(format!(
434 "expected Identifier, got {:?} at {}",
435 self.cur_token.token_type, self.cur_token.position
436 ));
437 return list;
438 }
439 }
440
441 list
442 }
443
444 pub(super) fn parse_copy_statement(&mut self) -> Option<CopyStatement> {
447 let token = self.cur_token.clone();
448
449 self.next_token();
451 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
452 self.add_error(format!(
453 "expected table name after COPY, got {:?} at {}",
454 self.cur_token.token_type, self.cur_token.position
455 ));
456 return None;
457 }
458 let table_name = self.parse_relation_identifier_current()?;
459
460 let mut columns = Vec::new();
462 if self.peek_token_is_punctuator("(") {
463 self.next_token(); columns = self.parse_identifier_list();
465 if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
466 self.add_error(format!("expected ')' at {}", self.cur_token.position));
467 return None;
468 }
469 }
470
471 if !self.expect_keyword("FROM") {
473 return None;
474 }
475
476 self.next_token();
478 if !self.cur_token_is(TokenType::String) {
479 self.add_error(format!(
480 "expected file path string after FROM, got {:?} at {}",
481 self.cur_token.token_type, self.cur_token.position
482 ));
483 return None;
484 }
485 let file_path = {
486 let lit = &self.cur_token.literal;
487 if lit.len() >= 2
488 && (lit.starts_with('\'') || lit.starts_with('"'))
489 && lit.ends_with(lit.chars().next().unwrap())
490 {
491 lit[1..lit.len() - 1].to_string()
492 } else {
493 lit.to_string()
494 }
495 };
496
497 let mut format = None;
499 let mut header = true;
500 let mut delimiter = b',';
501 let mut null_string = None;
502 let mut header_specified = false;
503 let mut delimiter_specified = false;
504
505 if self.peek_token_is_keyword("WITH") {
507 self.next_token(); if !self.peek_token_is_punctuator("(") {
510 self.add_error(format!(
511 "expected '(' after WITH at {}",
512 self.peek_token.position
513 ));
514 return None;
515 }
516 self.next_token(); loop {
520 self.next_token();
521 if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")" {
522 break;
523 }
524
525 let key = self.cur_token.literal.to_uppercase();
526 match key.as_str() {
527 "FORMAT" => {
528 self.next_token();
529 let fmt_str = self.cur_token.literal.to_uppercase();
530 match fmt_str.as_str() {
531 "CSV" => format = Some(CopyFormat::Csv),
532 "JSON" => format = Some(CopyFormat::Json),
533 _ => {
534 self.add_error(format!(
535 "unsupported COPY format '{}', expected CSV or JSON",
536 fmt_str
537 ));
538 return None;
539 }
540 }
541 }
542 "HEADER" => {
543 self.next_token();
544 let val = self.cur_token.literal.to_uppercase();
545 header = match val.as_str() {
546 "TRUE" | "ON" | "1" => true,
547 "FALSE" | "OFF" | "0" => false,
548 _ => {
549 self.add_error(format!(
550 "invalid HEADER value '{}', expected TRUE or FALSE",
551 self.cur_token.literal
552 ));
553 return None;
554 }
555 };
556 header_specified = true;
557 }
558 "DELIMITER" => {
559 self.next_token();
560 let delim_str = &self.cur_token.literal;
561 let raw = if delim_str.len() >= 2
563 && (delim_str.starts_with('\'') || delim_str.starts_with('"'))
564 {
565 &delim_str[1..delim_str.len() - 1]
566 } else {
567 delim_str.as_str()
568 };
569 if raw.len() != 1 {
570 self.add_error(format!(
571 "DELIMITER must be a single character, got '{}'",
572 raw
573 ));
574 return None;
575 }
576 delimiter = raw.as_bytes()[0];
577 delimiter_specified = true;
578 }
579 "NULL" => {
580 self.next_token();
581 let ns = &self.cur_token.literal;
582 null_string = Some(
583 if ns.len() >= 2
584 && (ns.starts_with('\'') || ns.starts_with('"'))
585 && ns.ends_with(ns.chars().next().unwrap())
586 {
587 ns[1..ns.len() - 1].to_string()
588 } else {
589 ns.to_string()
590 },
591 );
592 }
593 _ => {
594 self.add_error(format!("unknown COPY option '{}'", key));
595 return None;
596 }
597 }
598
599 if self.peek_token_is_punctuator(",") {
601 self.next_token(); } else if self.peek_token_is_punctuator(")") {
603 self.next_token(); break;
605 } else if !self.peek_token_is(TokenType::Eof) {
606 self.add_error(format!(
607 "expected ',' or ')' after COPY option, got {:?} at {}",
608 self.peek_token.token_type, self.peek_token.position
609 ));
610 return None;
611 }
612 }
613 }
614
615 let format = match format {
617 Some(f) => f,
618 None => {
619 CopyFormat::Csv
621 }
622 };
623
624 if format == CopyFormat::Json && (header_specified || delimiter_specified) {
625 self.add_error("COPY JSON does not support HEADER or DELIMITER options".to_string());
626 return None;
627 }
628
629 Some(CopyStatement {
630 token,
631 table_name,
632 columns,
633 file_path,
634 format,
635 header,
636 delimiter,
637 null_string,
638 })
639 }
640}