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) && !self.cur_token_is(TokenType::Keyword) {
169 self.add_error(format!(
170 "expected variable name at {}",
171 self.cur_token.position
172 ));
173 return None;
174 }
175
176 let name_token = self.cur_token.clone();
177 let time_zone = self.cur_token.literal.eq_ignore_ascii_case("TIME")
178 && self.peek_token.literal.eq_ignore_ascii_case("ZONE");
179
180 let name = if time_zone {
181 self.next_token();
182 Identifier::new(name_token, "TIME ZONE")
183 } else {
184 Identifier::new(name_token, self.cur_token.literal.clone())
185 };
186
187 self.next_token();
188 let is_equals = self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=";
192 if is_equals || self.cur_token_is_keyword("TO") {
193 self.next_token();
194 } else if !time_zone {
195 self.add_error(format!(
196 "expected '=' or 'TO' after variable name at {}",
197 self.cur_token.position
198 ));
199 return None;
200 }
201
202 let value = self.parse_expression(Precedence::Lowest)?;
203
204 Some(SetStatement { token, name, value })
205 }
206
207 pub(super) fn parse_pragma_statement(&mut self) -> Option<PragmaStatement> {
209 let token = self.cur_token.clone();
210
211 self.next_token();
212 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
214 self.add_error(format!(
215 "expected pragma name at {}",
216 self.cur_token.position
217 ));
218 return None;
219 }
220
221 let name = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
222
223 self.next_token();
224
225 let value = if self.cur_token_is(TokenType::Operator) && self.cur_token.literal == "=" {
227 self.next_token();
228 Some(self.parse_expression(Precedence::Lowest)?)
229 } else {
230 None
231 };
232
233 Some(PragmaStatement { token, name, value })
234 }
235
236 pub(super) fn parse_show_statement(&mut self) -> Option<Statement> {
238 let token = self.cur_token.clone();
239
240 if self.peek_token.literal.eq_ignore_ascii_case("TIMEZONE")
241 || self.peek_token.literal.eq_ignore_ascii_case("TIME_ZONE")
242 {
243 self.next_token();
244 let name = Identifier::new(self.cur_token.clone(), "TIME ZONE");
245 Some(Statement::ShowVariable(ShowVariableStatement {
246 token,
247 name,
248 }))
249 } else if self.peek_token_is_keyword("TIME") {
250 self.next_token();
251 if !self.peek_token.literal.eq_ignore_ascii_case("ZONE") {
252 self.add_error(format!(
253 "expected ZONE after SHOW TIME at {}",
254 self.cur_token.position
255 ));
256 return None;
257 }
258 self.next_token();
259 let name = Identifier::new(self.cur_token.clone(), "TIME ZONE");
260 Some(Statement::ShowVariable(ShowVariableStatement {
261 token,
262 name,
263 }))
264 } else if self.peek_token_is_keyword("TABLES") {
265 self.next_token();
266 Some(Statement::ShowTables(ShowTablesStatement { token }))
267 } else if self.peek_token_is_keyword("VIEWS") {
268 self.next_token();
269 Some(Statement::ShowViews(ShowViewsStatement { token }))
270 } else if self.peek_token_is_keyword("CREATE") {
271 self.next_token();
272 if self.peek_token_is_keyword("TABLE") {
274 self.next_token();
275 if !self.expect_peek(TokenType::Identifier) {
276 return None;
277 }
278 let table_name =
279 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
280 Some(Statement::ShowCreateTable(ShowCreateTableStatement {
281 token,
282 table_name,
283 }))
284 } else if self.peek_token_is_keyword("VIEW") {
285 self.next_token();
286 if !self.expect_peek(TokenType::Identifier) {
287 return None;
288 }
289 let view_name =
290 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
291 Some(Statement::ShowCreateView(ShowCreateViewStatement {
292 token,
293 view_name,
294 }))
295 } else {
296 self.add_error(format!(
297 "expected TABLE or VIEW after SHOW CREATE at {}",
298 self.cur_token.position
299 ));
300 None
301 }
302 } else if self.peek_token_is_keyword("INDEXES") || self.peek_token_is_keyword("INDEX") {
303 self.next_token();
304 if !self.expect_keyword("FROM") {
305 return None;
306 }
307 if !self.expect_peek(TokenType::Identifier) {
308 return None;
309 }
310 let table_name =
311 Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
312 Some(Statement::ShowIndexes(ShowIndexesStatement {
313 token,
314 table_name,
315 }))
316 } else {
317 self.add_error(format!(
318 "unsupported SHOW statement at {}",
319 self.cur_token.position
320 ));
321 None
322 }
323 }
324
325 pub(super) fn parse_describe_statement(&mut self) -> Option<DescribeStatement> {
327 let token = self.cur_token.clone();
328
329 self.next_token();
331
332 let target = if self.cur_token.literal.eq_ignore_ascii_case("DATABASE") {
333 DescribeTarget::Database
334 } else {
335 if self.cur_token_is_keyword("TABLE") {
337 self.next_token();
338 }
339 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
340 self.add_error(format!(
341 "expected TABLE name or DATABASE after DESCRIBE at {}",
342 self.cur_token.position
343 ));
344 return None;
345 }
346 DescribeTarget::Table(Identifier::new(
347 self.cur_token.clone(),
348 self.cur_token.literal.clone(),
349 ))
350 };
351
352 let format = if self.peek_token_is_keyword("FORMAT") {
353 self.next_token();
354 self.next_token();
355 if !self.cur_token_is_keyword("JSON") {
356 self.add_error(format!(
357 "expected JSON after DESCRIBE ... FORMAT at {}",
358 self.cur_token.position
359 ));
360 return None;
361 }
362 DescribeFormat::Json
363 } else {
364 DescribeFormat::Tabular
365 };
366
367 if matches!(target, DescribeTarget::Database) && format != DescribeFormat::Json {
368 self.add_error(
369 "DESCRIBE DATABASE requires FORMAT JSON; the legacy tabular format is table-only"
370 .to_string(),
371 );
372 return None;
373 }
374
375 Some(DescribeStatement {
376 token,
377 target,
378 format,
379 })
380 }
381
382 pub(super) fn parse_explain_statement(&mut self) -> Option<ExplainStatement> {
384 let token = self.cur_token.clone();
385
386 let analyze = if self.peek_token_is_keyword("ANALYZE") {
388 self.next_token();
389 true
390 } else {
391 false
392 };
393
394 self.next_token();
396
397 let statement = self.parse_statement()?;
399
400 Some(ExplainStatement {
401 token,
402 statement: Box::new(statement),
403 analyze,
404 })
405 }
406
407 pub(super) fn parse_analyze_statement(&mut self) -> Option<AnalyzeStatement> {
410 let token = self.cur_token.clone();
411
412 self.next_token();
414
415 let table_name = if self.cur_token_is(TokenType::Identifier)
417 || (self.cur_token_is(TokenType::Keyword)
418 && !self.cur_token.literal.eq_ignore_ascii_case("TABLE"))
419 {
420 let name = self.cur_token.literal.clone();
421 Some(name)
422 } else if self.cur_token_is(TokenType::Keyword)
423 && self.cur_token.literal.eq_ignore_ascii_case("TABLE")
424 {
425 self.next_token();
427 if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
428 let name = self.cur_token.literal.clone();
429 Some(name)
430 } else {
431 self.add_error(format!(
432 "expected table name after ANALYZE TABLE at {}",
433 self.cur_token.position
434 ));
435 return None;
436 }
437 } else {
438 None
439 };
440
441 Some(AnalyzeStatement { token, table_name })
442 }
443
444 pub(super) fn parse_expression_statement(&mut self) -> Option<ExpressionStatement> {
446 let token = self.cur_token.clone();
447 let expression = self.parse_expression(Precedence::Lowest)?;
448
449 Some(ExpressionStatement { token, expression })
450 }
451
452 pub fn parse_identifier_list(&mut self) -> Vec<Identifier> {
454 let mut list = Vec::new();
455
456 self.next_token();
457 if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
459 list.push(self.cur_token_as_column_identifier());
460 }
461
462 while self.peek_token_is_punctuator(",") {
463 self.next_token(); self.next_token(); if self.cur_token_is(TokenType::Identifier) || self.cur_token_is(TokenType::Keyword) {
467 list.push(self.cur_token_as_column_identifier());
468 } else {
469 self.add_error(format!(
470 "expected Identifier, got {:?} at {}",
471 self.cur_token.token_type, self.cur_token.position
472 ));
473 return list;
474 }
475 }
476
477 list
478 }
479
480 pub(super) fn parse_copy_statement(&mut self) -> Option<CopyStatement> {
483 let token = self.cur_token.clone();
484
485 self.next_token();
487 if !self.cur_token_is(TokenType::Identifier) && !self.cur_token_is(TokenType::Keyword) {
488 self.add_error(format!(
489 "expected table name after COPY, got {:?} at {}",
490 self.cur_token.token_type, self.cur_token.position
491 ));
492 return None;
493 }
494 let table_name = self.parse_relation_identifier_current()?;
495
496 let mut columns = Vec::new();
498 if self.peek_token_is_punctuator("(") {
499 self.next_token(); columns = self.parse_identifier_list();
501 if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
502 self.add_error(format!("expected ')' at {}", self.cur_token.position));
503 return None;
504 }
505 }
506
507 if !self.expect_keyword("FROM") {
509 return None;
510 }
511
512 self.next_token();
514 if !self.cur_token_is(TokenType::String) {
515 self.add_error(format!(
516 "expected file path string after FROM, got {:?} at {}",
517 self.cur_token.token_type, self.cur_token.position
518 ));
519 return None;
520 }
521 let file_path = {
522 let lit = &self.cur_token.literal;
523 if lit.len() >= 2
524 && (lit.starts_with('\'') || lit.starts_with('"'))
525 && lit.ends_with(lit.chars().next().unwrap())
526 {
527 lit[1..lit.len() - 1].to_string()
528 } else {
529 lit.to_string()
530 }
531 };
532
533 let mut format = None;
535 let mut header = true;
536 let mut delimiter = b',';
537 let mut null_string = None;
538 let mut header_specified = false;
539 let mut delimiter_specified = false;
540
541 if self.peek_token_is_keyword("WITH") {
543 self.next_token(); if !self.peek_token_is_punctuator("(") {
546 self.add_error(format!(
547 "expected '(' after WITH at {}",
548 self.peek_token.position
549 ));
550 return None;
551 }
552 self.next_token(); loop {
556 self.next_token();
557 if self.cur_token_is(TokenType::Punctuator) && self.cur_token.literal == ")" {
558 break;
559 }
560
561 let key = self.cur_token.literal.to_uppercase();
562 match key.as_str() {
563 "FORMAT" => {
564 self.next_token();
565 let fmt_str = self.cur_token.literal.to_uppercase();
566 match fmt_str.as_str() {
567 "CSV" => format = Some(CopyFormat::Csv),
568 "JSON" => format = Some(CopyFormat::Json),
569 _ => {
570 self.add_error(format!(
571 "unsupported COPY format '{}', expected CSV or JSON",
572 fmt_str
573 ));
574 return None;
575 }
576 }
577 }
578 "HEADER" => {
579 self.next_token();
580 let val = self.cur_token.literal.to_uppercase();
581 header = match val.as_str() {
582 "TRUE" | "ON" | "1" => true,
583 "FALSE" | "OFF" | "0" => false,
584 _ => {
585 self.add_error(format!(
586 "invalid HEADER value '{}', expected TRUE or FALSE",
587 self.cur_token.literal
588 ));
589 return None;
590 }
591 };
592 header_specified = true;
593 }
594 "DELIMITER" => {
595 self.next_token();
596 let delim_str = &self.cur_token.literal;
597 let raw = if delim_str.len() >= 2
599 && (delim_str.starts_with('\'') || delim_str.starts_with('"'))
600 {
601 &delim_str[1..delim_str.len() - 1]
602 } else {
603 delim_str.as_str()
604 };
605 if raw.len() != 1 {
606 self.add_error(format!(
607 "DELIMITER must be a single character, got '{}'",
608 raw
609 ));
610 return None;
611 }
612 delimiter = raw.as_bytes()[0];
613 delimiter_specified = true;
614 }
615 "NULL" => {
616 self.next_token();
617 let ns = &self.cur_token.literal;
618 null_string = Some(
619 if ns.len() >= 2
620 && (ns.starts_with('\'') || ns.starts_with('"'))
621 && ns.ends_with(ns.chars().next().unwrap())
622 {
623 ns[1..ns.len() - 1].to_string()
624 } else {
625 ns.to_string()
626 },
627 );
628 }
629 _ => {
630 self.add_error(format!("unknown COPY option '{}'", key));
631 return None;
632 }
633 }
634
635 if self.peek_token_is_punctuator(",") {
637 self.next_token(); } else if self.peek_token_is_punctuator(")") {
639 self.next_token(); break;
641 } else if !self.peek_token_is(TokenType::Eof) {
642 self.add_error(format!(
643 "expected ',' or ')' after COPY option, got {:?} at {}",
644 self.peek_token.token_type, self.peek_token.position
645 ));
646 return None;
647 }
648 }
649 }
650
651 let format = match format {
653 Some(f) => f,
654 None => {
655 CopyFormat::Csv
657 }
658 };
659
660 if format == CopyFormat::Json && (header_specified || delimiter_specified) {
661 self.add_error("COPY JSON does not support HEADER or DELIMITER options".to_string());
662 return None;
663 }
664
665 Some(CopyStatement {
666 token,
667 table_name,
668 columns,
669 file_path,
670 format,
671 header,
672 delimiter,
673 null_string,
674 })
675 }
676}