1#[cfg(not(feature = "std"))]
25use alloc::{
26 borrow::ToOwned,
27 format,
28 string::{String, ToString},
29 vec,
30 vec::Vec,
31};
32use core::num::NonZeroU8;
33use core::str::Chars;
34use core::{cmp, fmt};
35use core::{iter::Peekable, str};
36
37#[cfg(feature = "serde")]
38use serde::{Deserialize, Serialize};
39
40#[cfg(feature = "visitor")]
41use sqlparser_derive::{Visit, VisitMut};
42
43use crate::dialect::Dialect;
44use crate::dialect::{
45 BigQueryDialect, DuckDbDialect, GenericDialect, MySqlDialect, PostgreSqlDialect,
46 SnowflakeDialect,
47};
48use crate::keywords::{Keyword, ALL_KEYWORDS, ALL_KEYWORDS_INDEX};
49use crate::{
50 ast::{DollarQuotedString, QuoteDelimitedString},
51 dialect::HiveDialect,
52};
53
54#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
58pub enum Token {
59 EOF,
61 Word(Word),
63 Number(String, bool),
65 Char(char),
67 SingleQuotedString(String),
69 DoubleQuotedString(String),
71 TripleSingleQuotedString(String),
74 TripleDoubleQuotedString(String),
77 DollarQuotedString(DollarQuotedString),
79 SingleQuotedByteStringLiteral(String),
82 DoubleQuotedByteStringLiteral(String),
84 TripleSingleQuotedByteStringLiteral(String),
87 TripleDoubleQuotedByteStringLiteral(String),
90 SingleQuotedRawStringLiteral(String),
93 DoubleQuotedRawStringLiteral(String),
96 TripleSingleQuotedRawStringLiteral(String),
99 TripleDoubleQuotedRawStringLiteral(String),
102 NationalStringLiteral(String),
104 QuoteDelimitedStringLiteral(QuoteDelimitedString),
107 NationalQuoteDelimitedStringLiteral(QuoteDelimitedString),
110 EscapedStringLiteral(String),
112 UnicodeStringLiteral(String),
114 HexStringLiteral(String),
116 Comma,
118 Whitespace(Whitespace),
120 DoubleEq,
122 Eq,
124 Neq,
126 Lt,
128 Gt,
130 LtEq,
132 GtEq,
134 Spaceship,
136 Plus,
138 Minus,
140 Mul,
142 Div,
144 DuckIntDiv,
146 Mod,
148 StringConcat,
150 LParen,
152 RParen,
154 Period,
156 Colon,
158 DoubleColon,
160 Assignment,
162 SemiColon,
164 Backslash,
166 LBracket,
168 RBracket,
170 Ampersand,
172 Pipe,
174 Caret,
176 LBrace,
178 RBrace,
180 RArrow,
182 Sharp,
184 DoubleSharp,
186 Tilde,
188 TildeAsterisk,
190 ExclamationMarkTilde,
192 ExclamationMarkTildeAsterisk,
194 DoubleTilde,
196 DoubleTildeAsterisk,
198 ExclamationMarkDoubleTilde,
200 ExclamationMarkDoubleTildeAsterisk,
202 ShiftLeft,
204 ShiftRight,
206 Overlap,
208 ExclamationMark,
210 DoubleExclamationMark,
212 AtSign,
214 CaretAt,
216 PGSquareRoot,
218 PGCubeRoot,
220 Placeholder(String),
222 Arrow,
224 LongArrow,
226 HashArrow,
228 AtDashAt,
230 QuestionMarkDash,
232 AmpersandLeftAngleBracket,
234 AmpersandRightAngleBracket,
236 AmpersandLeftAngleBracketVerticalBar,
238 VerticalBarAmpersandRightAngleBracket,
240 TwoWayArrow,
242 LeftAngleBracketCaret,
244 RightAngleBracketCaret,
246 QuestionMarkSharp,
248 QuestionMarkDashVerticalBar,
250 QuestionMarkDoubleVerticalBar,
252 TildeEqual,
254 ShiftLeftVerticalBar,
256 VerticalBarShiftRight,
258 VerticalBarRightAngleBracket,
260 HashLongArrow,
262 AtArrow,
264 ArrowAt,
266 HashMinus,
269 AtQuestion,
272 AtAt,
276 Question,
279 QuestionAnd,
282 QuestionPipe,
285 CustomBinaryOperator(String),
289}
290
291impl fmt::Display for Token {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 match self {
294 Token::EOF => f.write_str("EOF"),
295 Token::Word(ref w) => write!(f, "{w}"),
296 Token::Number(ref n, l) => write!(f, "{}{long}", n, long = if *l { "L" } else { "" }),
297 Token::Char(ref c) => write!(f, "{c}"),
298 Token::SingleQuotedString(ref s) => write!(f, "'{s}'"),
299 Token::TripleSingleQuotedString(ref s) => write!(f, "'''{s}'''"),
300 Token::DoubleQuotedString(ref s) => write!(f, "\"{s}\""),
301 Token::TripleDoubleQuotedString(ref s) => write!(f, "\"\"\"{s}\"\"\""),
302 Token::DollarQuotedString(ref s) => write!(f, "{s}"),
303 Token::NationalStringLiteral(ref s) => write!(f, "N'{s}'"),
304 Token::QuoteDelimitedStringLiteral(ref s) => s.fmt(f),
305 Token::NationalQuoteDelimitedStringLiteral(ref s) => write!(f, "N{s}"),
306 Token::EscapedStringLiteral(ref s) => write!(f, "E'{s}'"),
307 Token::UnicodeStringLiteral(ref s) => write!(f, "U&'{s}'"),
308 Token::HexStringLiteral(ref s) => write!(f, "X'{s}'"),
309 Token::SingleQuotedByteStringLiteral(ref s) => write!(f, "B'{s}'"),
310 Token::TripleSingleQuotedByteStringLiteral(ref s) => write!(f, "B'''{s}'''"),
311 Token::DoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"{s}\""),
312 Token::TripleDoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"\"\"{s}\"\"\""),
313 Token::SingleQuotedRawStringLiteral(ref s) => write!(f, "R'{s}'"),
314 Token::DoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"{s}\""),
315 Token::TripleSingleQuotedRawStringLiteral(ref s) => write!(f, "R'''{s}'''"),
316 Token::TripleDoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"\"\"{s}\"\"\""),
317 Token::Comma => f.write_str(","),
318 Token::Whitespace(ws) => write!(f, "{ws}"),
319 Token::DoubleEq => f.write_str("=="),
320 Token::Spaceship => f.write_str("<=>"),
321 Token::Eq => f.write_str("="),
322 Token::Neq => f.write_str("<>"),
323 Token::Lt => f.write_str("<"),
324 Token::Gt => f.write_str(">"),
325 Token::LtEq => f.write_str("<="),
326 Token::GtEq => f.write_str(">="),
327 Token::Plus => f.write_str("+"),
328 Token::Minus => f.write_str("-"),
329 Token::Mul => f.write_str("*"),
330 Token::Div => f.write_str("/"),
331 Token::DuckIntDiv => f.write_str("//"),
332 Token::StringConcat => f.write_str("||"),
333 Token::Mod => f.write_str("%"),
334 Token::LParen => f.write_str("("),
335 Token::RParen => f.write_str(")"),
336 Token::Period => f.write_str("."),
337 Token::Colon => f.write_str(":"),
338 Token::DoubleColon => f.write_str("::"),
339 Token::Assignment => f.write_str(":="),
340 Token::SemiColon => f.write_str(";"),
341 Token::Backslash => f.write_str("\\"),
342 Token::LBracket => f.write_str("["),
343 Token::RBracket => f.write_str("]"),
344 Token::Ampersand => f.write_str("&"),
345 Token::Caret => f.write_str("^"),
346 Token::Pipe => f.write_str("|"),
347 Token::LBrace => f.write_str("{"),
348 Token::RBrace => f.write_str("}"),
349 Token::RArrow => f.write_str("=>"),
350 Token::Sharp => f.write_str("#"),
351 Token::DoubleSharp => f.write_str("##"),
352 Token::ExclamationMark => f.write_str("!"),
353 Token::DoubleExclamationMark => f.write_str("!!"),
354 Token::Tilde => f.write_str("~"),
355 Token::TildeAsterisk => f.write_str("~*"),
356 Token::ExclamationMarkTilde => f.write_str("!~"),
357 Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
358 Token::DoubleTilde => f.write_str("~~"),
359 Token::DoubleTildeAsterisk => f.write_str("~~*"),
360 Token::ExclamationMarkDoubleTilde => f.write_str("!~~"),
361 Token::ExclamationMarkDoubleTildeAsterisk => f.write_str("!~~*"),
362 Token::AtSign => f.write_str("@"),
363 Token::CaretAt => f.write_str("^@"),
364 Token::ShiftLeft => f.write_str("<<"),
365 Token::ShiftRight => f.write_str(">>"),
366 Token::Overlap => f.write_str("&&"),
367 Token::PGSquareRoot => f.write_str("|/"),
368 Token::PGCubeRoot => f.write_str("||/"),
369 Token::AtDashAt => f.write_str("@-@"),
370 Token::QuestionMarkDash => f.write_str("?-"),
371 Token::AmpersandLeftAngleBracket => f.write_str("&<"),
372 Token::AmpersandRightAngleBracket => f.write_str("&>"),
373 Token::AmpersandLeftAngleBracketVerticalBar => f.write_str("&<|"),
374 Token::VerticalBarAmpersandRightAngleBracket => f.write_str("|&>"),
375 Token::VerticalBarRightAngleBracket => f.write_str("|>"),
376 Token::TwoWayArrow => f.write_str("<->"),
377 Token::LeftAngleBracketCaret => f.write_str("<^"),
378 Token::RightAngleBracketCaret => f.write_str(">^"),
379 Token::QuestionMarkSharp => f.write_str("?#"),
380 Token::QuestionMarkDashVerticalBar => f.write_str("?-|"),
381 Token::QuestionMarkDoubleVerticalBar => f.write_str("?||"),
382 Token::TildeEqual => f.write_str("~="),
383 Token::ShiftLeftVerticalBar => f.write_str("<<|"),
384 Token::VerticalBarShiftRight => f.write_str("|>>"),
385 Token::Placeholder(ref s) => write!(f, "{s}"),
386 Token::Arrow => write!(f, "->"),
387 Token::LongArrow => write!(f, "->>"),
388 Token::HashArrow => write!(f, "#>"),
389 Token::HashLongArrow => write!(f, "#>>"),
390 Token::AtArrow => write!(f, "@>"),
391 Token::ArrowAt => write!(f, "<@"),
392 Token::HashMinus => write!(f, "#-"),
393 Token::AtQuestion => write!(f, "@?"),
394 Token::AtAt => write!(f, "@@"),
395 Token::Question => write!(f, "?"),
396 Token::QuestionAnd => write!(f, "?&"),
397 Token::QuestionPipe => write!(f, "?|"),
398 Token::CustomBinaryOperator(s) => f.write_str(s),
399 }
400 }
401}
402
403impl Token {
404 pub fn make_keyword(keyword: &str) -> Self {
408 Token::make_word(keyword, None)
409 }
410
411 pub fn make_word(word: &str, quote_style: Option<char>) -> Self {
416 Token::Word(Word {
417 keyword: keyword_lookup(word, quote_style),
418 value: word.to_string(),
419 quote_style,
420 })
421 }
422
423 fn make_word_owned(word: String, quote_style: Option<char>) -> Self {
426 Token::Word(Word {
427 keyword: keyword_lookup(&word, quote_style),
428 value: word,
429 quote_style,
430 })
431 }
432}
433
434fn keyword_lookup(word: &str, quote_style: Option<char>) -> Keyword {
436 if quote_style.is_some() {
437 return Keyword::NoKeyword;
438 }
439 ALL_KEYWORDS
440 .binary_search_by(|probe| {
441 let probe = probe.as_bytes();
442 let word = word.as_bytes();
443 for (p, w) in probe.iter().zip(word.iter()) {
444 let cmp = p.cmp(&w.to_ascii_uppercase());
445 if cmp != core::cmp::Ordering::Equal {
446 return cmp;
447 }
448 }
449 probe.len().cmp(&word.len())
450 })
451 .map_or(Keyword::NoKeyword, |x| ALL_KEYWORDS_INDEX[x])
452}
453
454#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
456#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
457#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
458pub struct Word {
459 pub value: String,
462 pub quote_style: Option<char>,
466 pub keyword: Keyword,
469}
470
471impl fmt::Display for Word {
472 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473 match self.quote_style {
474 Some(s) if s == '"' || s == '[' || s == '`' => {
475 write!(f, "{}{}{}", s, self.value, Word::matching_end_quote(s))
476 }
477 None => f.write_str(&self.value),
478 _ => panic!("Unexpected quote_style!"),
479 }
480 }
481}
482
483impl Word {
484 fn matching_end_quote(ch: char) -> char {
485 match ch {
486 '"' => '"', '[' => ']', '`' => '`', _ => panic!("unexpected quoting style!"),
490 }
491 }
492}
493
494#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
496#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
497#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
498pub enum Whitespace {
499 Space,
501 Newline,
503 Tab,
505 SingleLineComment {
508 comment: String,
510 prefix: String,
512 },
513
514 MultiLineComment(String),
516}
517
518impl fmt::Display for Whitespace {
519 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520 match self {
521 Whitespace::Space => f.write_str(" "),
522 Whitespace::Newline => f.write_str("\n"),
523 Whitespace::Tab => f.write_str("\t"),
524 Whitespace::SingleLineComment { prefix, comment } => writeln!(f, "{prefix}{comment}"),
525 Whitespace::MultiLineComment(s) => write!(f, "/*{s}*/"),
526 }
527 }
528}
529
530#[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)]
550#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
551#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
552pub struct Location {
553 pub line: u64,
557 pub column: u64,
561}
562
563impl fmt::Display for Location {
564 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
565 if self.line == 0 {
566 return Ok(());
567 }
568 write!(f, " at Line: {}, Column: {}", self.line, self.column)
569 }
570}
571
572impl fmt::Debug for Location {
573 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
574 write!(f, "Location({},{})", self.line, self.column)
575 }
576}
577
578impl Location {
579 pub fn empty() -> Self {
581 Self { line: 0, column: 0 }
582 }
583
584 pub fn new(line: u64, column: u64) -> Self {
586 Self { line, column }
587 }
588
589 pub fn of(line: u64, column: u64) -> Self {
594 Self::new(line, column)
595 }
596
597 pub fn span_to(self, end: Self) -> Span {
599 Span { start: self, end }
600 }
601}
602
603impl From<(u64, u64)> for Location {
604 fn from((line, column): (u64, u64)) -> Self {
605 Self { line, column }
606 }
607}
608
609#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)]
613#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
614#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
615pub struct Span {
616 pub start: Location,
618 pub end: Location,
620}
621
622impl fmt::Debug for Span {
623 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
624 write!(f, "Span({:?}..{:?})", self.start, self.end)
625 }
626}
627
628impl Span {
629 const EMPTY: Span = Self::empty();
632
633 pub fn new(start: Location, end: Location) -> Span {
635 Span { start, end }
636 }
637
638 pub const fn empty() -> Span {
643 Span {
644 start: Location { line: 0, column: 0 },
645 end: Location { line: 0, column: 0 },
646 }
647 }
648
649 pub fn union(&self, other: &Span) -> Span {
665 match (self, other) {
668 (&Span::EMPTY, _) => *other,
669 (_, &Span::EMPTY) => *self,
670 _ => Span {
671 start: cmp::min(self.start, other.start),
672 end: cmp::max(self.end, other.end),
673 },
674 }
675 }
676
677 pub fn union_opt(&self, other: &Option<Span>) -> Span {
681 match other {
682 Some(other) => self.union(other),
683 None => *self,
684 }
685 }
686
687 pub fn union_iter<I: IntoIterator<Item = Span>>(iter: I) -> Span {
705 iter.into_iter()
706 .reduce(|acc, item| acc.union(&item))
707 .unwrap_or(Span::empty())
708 }
709}
710
711#[deprecated(since = "0.53.0", note = "please use `TokenWithSpan` instead")]
713pub type TokenWithLocation = TokenWithSpan;
714
715#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
738#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
739#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
740pub struct TokenWithSpan {
742 pub token: Token,
744 pub span: Span,
746}
747
748impl TokenWithSpan {
749 pub fn new(token: Token, span: Span) -> Self {
751 Self { token, span }
752 }
753
754 pub fn wrap(token: Token) -> Self {
756 Self::new(token, Span::empty())
757 }
758
759 pub fn at(token: Token, start: Location, end: Location) -> Self {
761 Self::new(token, Span::new(start, end))
762 }
763
764 pub fn new_eof() -> Self {
766 Self::wrap(Token::EOF)
767 }
768}
769
770impl PartialEq<Token> for TokenWithSpan {
771 fn eq(&self, other: &Token) -> bool {
772 &self.token == other
773 }
774}
775
776impl PartialEq<TokenWithSpan> for Token {
777 fn eq(&self, other: &TokenWithSpan) -> bool {
778 self == &other.token
779 }
780}
781
782impl fmt::Display for TokenWithSpan {
783 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
784 self.token.fmt(f)
785 }
786}
787
788#[derive(Debug, PartialEq, Eq)]
790pub struct TokenizerError {
791 pub message: String,
793 pub location: Location,
795}
796
797impl fmt::Display for TokenizerError {
798 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799 write!(f, "{}{}", self.message, self.location,)
800 }
801}
802
803impl core::error::Error for TokenizerError {}
804
805struct State<'a> {
806 peekable: Peekable<Chars<'a>>,
807 line: u64,
808 col: u64,
809}
810
811impl State<'_> {
812 pub fn next(&mut self) -> Option<char> {
814 match self.peekable.next() {
815 None => None,
816 Some(s) => {
817 if s == '\n' {
818 self.line += 1;
819 self.col = 1;
820 } else {
821 self.col += 1;
822 }
823 Some(s)
824 }
825 }
826 }
827
828 pub fn peek(&mut self) -> Option<&char> {
830 self.peekable.peek()
831 }
832
833 pub fn location(&self) -> Location {
835 Location {
836 line: self.line,
837 column: self.col,
838 }
839 }
840}
841
842#[derive(Copy, Clone)]
844enum NumStringQuoteChars {
845 One,
847 Many(NonZeroU8),
849}
850
851struct TokenizeQuotedStringSettings {
853 quote_style: char,
855 num_quote_chars: NumStringQuoteChars,
857 num_opening_quotes_to_consume: u8,
863 backslash_escape: bool,
866}
867
868pub struct Tokenizer<'a> {
870 dialect: &'a dyn Dialect,
871 query: &'a str,
872 unescape: bool,
875}
876
877impl<'a> Tokenizer<'a> {
878 pub fn new(dialect: &'a dyn Dialect, query: &'a str) -> Self {
895 Self {
896 dialect,
897 query,
898 unescape: true,
899 }
900 }
901
902 pub fn with_unescape(mut self, unescape: bool) -> Self {
933 self.unescape = unescape;
934 self
935 }
936
937 pub fn tokenize(&mut self) -> Result<Vec<Token>, TokenizerError> {
939 let twl = self.tokenize_with_location()?;
940 Ok(twl.into_iter().map(|t| t.token).collect())
941 }
942
943 pub fn tokenize_with_location(&mut self) -> Result<Vec<TokenWithSpan>, TokenizerError> {
945 let mut tokens: Vec<TokenWithSpan> = vec![];
946 self.tokenize_with_location_into_buf(&mut tokens)
947 .map(|_| tokens)
948 }
949
950 pub fn tokenize_with_location_into_buf(
953 &mut self,
954 buf: &mut Vec<TokenWithSpan>,
955 ) -> Result<(), TokenizerError> {
956 self.tokenize_with_location_into_buf_with_mapper(buf, |token| token)
957 }
958
959 pub fn tokenize_with_location_into_buf_with_mapper(
962 &mut self,
963 buf: &mut Vec<TokenWithSpan>,
964 mut mapper: impl FnMut(TokenWithSpan) -> TokenWithSpan,
965 ) -> Result<(), TokenizerError> {
966 let mut state = State {
967 peekable: self.query.chars().peekable(),
968 line: 1,
969 col: 1,
970 };
971
972 let mut location = state.location();
973 while let Some(token) = self.next_token(&mut state, buf.last().map(|t| &t.token))? {
974 let span = location.span_to(state.location());
975
976 match &token {
978 Token::Whitespace(Whitespace::MultiLineComment(comment))
979 if self.dialect.supports_multiline_comment_hints()
980 && comment.starts_with('!') =>
981 {
982 self.tokenize_comment_hints(comment, span, buf, &mut mapper)?;
984 }
985 _ => {
986 buf.push(mapper(TokenWithSpan { token, span }));
987 }
988 }
989
990 location = state.location();
991 }
992 Ok(())
993 }
994
995 fn tokenize_comment_hints(
998 &self,
999 comment: &str,
1000 span: Span,
1001 buf: &mut Vec<TokenWithSpan>,
1002 mut mapper: impl FnMut(TokenWithSpan) -> TokenWithSpan,
1003 ) -> Result<(), TokenizerError> {
1004 let hint_content = comment
1006 .strip_prefix('!')
1007 .unwrap_or(comment)
1008 .trim_start_matches(|c: char| c.is_ascii_digit());
1009
1010 if hint_content.is_empty() {
1012 return Ok(());
1013 }
1014
1015 let inner = Tokenizer::new(self.dialect, hint_content).with_unescape(self.unescape);
1017
1018 let mut state = State {
1020 peekable: hint_content.chars().peekable(),
1021 line: span.start.line,
1022 col: span.start.column,
1023 };
1024
1025 let mut location = state.location();
1027 while let Some(token) = inner.next_token(&mut state, buf.last().map(|t| &t.token))? {
1028 let token_span = location.span_to(state.location());
1029 buf.push(mapper(TokenWithSpan {
1030 token,
1031 span: token_span,
1032 }));
1033 location = state.location();
1034 }
1035
1036 Ok(())
1037 }
1038
1039 fn tokenize_identifier_or_keyword(
1041 &self,
1042 ch: impl IntoIterator<Item = char>,
1043 chars: &mut State,
1044 ) -> Result<Option<Token>, TokenizerError> {
1045 chars.next(); let ch: String = ch.into_iter().collect();
1047 let word = self.tokenize_word(ch, chars);
1048
1049 if word.chars().all(|x| x.is_ascii_digit() || x == '.') {
1051 let mut inner_state = State {
1052 peekable: word.chars().peekable(),
1053 line: 0,
1054 col: 0,
1055 };
1056 let mut s = peeking_take_while(&mut inner_state, |ch| matches!(ch, '0'..='9' | '.'));
1057 let s2 = peeking_take_while(chars, |ch| matches!(ch, '0'..='9' | '.'));
1058 s += s2.as_str();
1059 return Ok(Some(Token::Number(s, false)));
1060 }
1061
1062 Ok(Some(Token::make_word_owned(word, None)))
1063 }
1064
1065 fn next_token(
1067 &self,
1068 chars: &mut State,
1069 prev_token: Option<&Token>,
1070 ) -> Result<Option<Token>, TokenizerError> {
1071 match chars.peek() {
1072 Some(&ch) => match ch {
1073 ' ' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Space)),
1074 '\t' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Tab)),
1075 '\n' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Newline)),
1076 '\r' => {
1077 chars.next();
1079 if let Some('\n') = chars.peek() {
1080 chars.next();
1081 }
1082 Ok(Some(Token::Whitespace(Whitespace::Newline)))
1083 }
1084 b @ 'B' | b @ 'b' if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | MySqlDialect | GenericDialect) =>
1086 {
1087 chars.next(); match chars.peek() {
1089 Some('\'') => {
1090 if self.dialect.supports_triple_quoted_string() {
1091 return self
1092 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1093 chars,
1094 '\'',
1095 false,
1096 Token::SingleQuotedByteStringLiteral,
1097 Token::TripleSingleQuotedByteStringLiteral,
1098 );
1099 }
1100 let s = self.tokenize_single_quoted_string(chars, '\'', false)?;
1101 Ok(Some(Token::SingleQuotedByteStringLiteral(s)))
1102 }
1103 Some('\"') => {
1104 if self.dialect.supports_triple_quoted_string() {
1105 return self
1106 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1107 chars,
1108 '"',
1109 false,
1110 Token::DoubleQuotedByteStringLiteral,
1111 Token::TripleDoubleQuotedByteStringLiteral,
1112 );
1113 }
1114 let s = self.tokenize_single_quoted_string(chars, '\"', false)?;
1115 Ok(Some(Token::DoubleQuotedByteStringLiteral(s)))
1116 }
1117 _ => {
1118 let s = self.tokenize_word(b, chars);
1120 Ok(Some(Token::make_word_owned(s, None)))
1121 }
1122 }
1123 }
1124 b @ 'R' | b @ 'r' if dialect_of!(self is BigQueryDialect | GenericDialect) => {
1126 chars.next(); match chars.peek() {
1128 Some('\'') => self
1129 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1130 chars,
1131 '\'',
1132 false,
1133 Token::SingleQuotedRawStringLiteral,
1134 Token::TripleSingleQuotedRawStringLiteral,
1135 ),
1136 Some('\"') => self
1137 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1138 chars,
1139 '"',
1140 false,
1141 Token::DoubleQuotedRawStringLiteral,
1142 Token::TripleDoubleQuotedRawStringLiteral,
1143 ),
1144 _ => {
1145 let s = self.tokenize_word(b, chars);
1147 Ok(Some(Token::make_word_owned(s, None)))
1148 }
1149 }
1150 }
1151 n @ 'N' | n @ 'n' => {
1153 chars.next(); match chars.peek() {
1155 Some('\'') => {
1156 let backslash_escape =
1158 self.dialect.supports_string_literal_backslash_escape();
1159 let s =
1160 self.tokenize_single_quoted_string(chars, '\'', backslash_escape)?;
1161 Ok(Some(Token::NationalStringLiteral(s)))
1162 }
1163 Some(&q @ 'q') | Some(&q @ 'Q')
1164 if self.dialect.supports_quote_delimited_string() =>
1165 {
1166 chars.next(); if let Some('\'') = chars.peek() {
1168 self.tokenize_quote_delimited_string(chars, &[n, q])
1169 .map(|s| Some(Token::NationalQuoteDelimitedStringLiteral(s)))
1170 } else {
1171 let s = self.tokenize_word(String::from_iter([n, q]), chars);
1172 Ok(Some(Token::make_word_owned(s, None)))
1173 }
1174 }
1175 _ => {
1176 let s = self.tokenize_word(n, chars);
1178 Ok(Some(Token::make_word_owned(s, None)))
1179 }
1180 }
1181 }
1182 q @ 'Q' | q @ 'q' if self.dialect.supports_quote_delimited_string() => {
1183 chars.next(); if let Some('\'') = chars.peek() {
1185 self.tokenize_quote_delimited_string(chars, &[q])
1186 .map(|s| Some(Token::QuoteDelimitedStringLiteral(s)))
1187 } else {
1188 let s = self.tokenize_word(q, chars);
1189 Ok(Some(Token::make_word_owned(s, None)))
1190 }
1191 }
1192 x @ 'e' | x @ 'E' if self.dialect.supports_string_escape_constant() => {
1194 let starting_loc = chars.location();
1195 chars.next(); match chars.peek() {
1197 Some('\'') => {
1198 let s =
1199 self.tokenize_escaped_single_quoted_string(starting_loc, chars)?;
1200 Ok(Some(Token::EscapedStringLiteral(s)))
1201 }
1202 _ => {
1203 let s = self.tokenize_word(x, chars);
1205 Ok(Some(Token::make_word_owned(s, None)))
1206 }
1207 }
1208 }
1209 x @ 'u' | x @ 'U' if self.dialect.supports_unicode_string_literal() => {
1211 chars.next(); if chars.peek() == Some(&'&') {
1213 let mut chars_clone = chars.peekable.clone();
1215 chars_clone.next(); if chars_clone.peek() == Some(&'\'') {
1217 chars.next(); let s = unescape_unicode_single_quoted_string(chars)?;
1219 return Ok(Some(Token::UnicodeStringLiteral(s)));
1220 }
1221 }
1222 let s = self.tokenize_word(x, chars);
1224 Ok(Some(Token::make_word_owned(s, None)))
1225 }
1226 x @ 'x' | x @ 'X' => {
1229 chars.next(); match chars.peek() {
1231 Some('\'') => {
1232 let s = self.tokenize_single_quoted_string(chars, '\'', true)?;
1234 Ok(Some(Token::HexStringLiteral(s)))
1235 }
1236 _ => {
1237 let s = self.tokenize_word(x, chars);
1239 Ok(Some(Token::make_word_owned(s, None)))
1240 }
1241 }
1242 }
1243 '\'' => {
1245 if self.dialect.supports_triple_quoted_string() {
1246 return self
1247 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1248 chars,
1249 '\'',
1250 self.dialect.supports_string_literal_backslash_escape(),
1251 Token::SingleQuotedString,
1252 Token::TripleSingleQuotedString,
1253 );
1254 }
1255 let s = self.tokenize_single_quoted_string(
1256 chars,
1257 '\'',
1258 self.dialect.supports_string_literal_backslash_escape(),
1259 )?;
1260
1261 Ok(Some(Token::SingleQuotedString(s)))
1262 }
1263 '\"' if !self.dialect.is_delimited_identifier_start(ch)
1265 && !self.dialect.is_identifier_start(ch) =>
1266 {
1267 if self.dialect.supports_triple_quoted_string() {
1268 return self
1269 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1270 chars,
1271 '"',
1272 self.dialect.supports_string_literal_backslash_escape(),
1273 Token::DoubleQuotedString,
1274 Token::TripleDoubleQuotedString,
1275 );
1276 }
1277 let s = self.tokenize_single_quoted_string(
1278 chars,
1279 '"',
1280 self.dialect.supports_string_literal_backslash_escape(),
1281 )?;
1282
1283 Ok(Some(Token::DoubleQuotedString(s)))
1284 }
1285 quote_start if self.dialect.is_delimited_identifier_start(ch) => {
1287 let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1288 Ok(Some(Token::make_word_owned(word, Some(quote_start))))
1289 }
1290 quote_start
1292 if self
1293 .dialect
1294 .is_nested_delimited_identifier_start(quote_start)
1295 && self
1296 .dialect
1297 .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1298 .is_some() =>
1299 {
1300 let Some((quote_start, nested_quote_start)) = self
1301 .dialect
1302 .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1303 else {
1304 return self.tokenizer_error(
1305 chars.location(),
1306 format!("Expected nested delimiter '{quote_start}' before EOF."),
1307 );
1308 };
1309
1310 let Some(nested_quote_start) = nested_quote_start else {
1311 let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1312 return Ok(Some(Token::make_word_owned(word, Some(quote_start))));
1313 };
1314
1315 let mut word = vec![];
1316 let quote_end = Word::matching_end_quote(quote_start);
1317 let nested_quote_end = Word::matching_end_quote(nested_quote_start);
1318 let error_loc = chars.location();
1319
1320 chars.next(); peeking_take_while(chars, |ch| ch.is_whitespace());
1322 if chars.peek() != Some(&nested_quote_start) {
1323 return self.tokenizer_error(
1324 error_loc,
1325 format!("Expected nested delimiter '{nested_quote_start}' before EOF."),
1326 );
1327 }
1328 word.push(nested_quote_start.into());
1329 word.push(self.tokenize_quoted_identifier(nested_quote_end, chars)?);
1330 word.push(nested_quote_end.into());
1331 peeking_take_while(chars, |ch| ch.is_whitespace());
1332 if chars.peek() != Some("e_end) {
1333 return self.tokenizer_error(
1334 error_loc,
1335 format!("Expected close delimiter '{quote_end}' before EOF."),
1336 );
1337 }
1338 chars.next(); Ok(Some(Token::make_word_owned(
1341 word.concat(),
1342 Some(quote_start),
1343 )))
1344 }
1345 '0'..='9' | '.' => {
1347 if ch == '.' && chars.peekable.clone().nth(1) == Some('_') {
1352 if let Some(Token::Word(_)) = prev_token {
1353 chars.next();
1354 return Ok(Some(Token::Period));
1355 }
1356
1357 return self.tokenizer_error(
1358 chars.location(),
1359 "Unexpected character '_'".to_string(),
1360 );
1361 }
1362
1363 let is_number_separator = |ch: char, next_char: Option<char>| {
1366 self.dialect.supports_numeric_literal_underscores()
1367 && ch == '_'
1368 && next_char.is_some_and(|next_ch| next_ch.is_ascii_hexdigit())
1369 };
1370
1371 let mut s = peeking_next_take_while(chars, |ch, next_ch| {
1372 ch.is_ascii_digit() || is_number_separator(ch, next_ch)
1373 });
1374
1375 if s == "0" && chars.peek() == Some(&'x') {
1377 chars.next();
1378 let s2 = peeking_next_take_while(chars, |ch, next_ch| {
1379 ch.is_ascii_hexdigit() || is_number_separator(ch, next_ch)
1380 });
1381 return Ok(Some(Token::HexStringLiteral(s2)));
1382 }
1383
1384 if let Some('.') = chars.peek() {
1386 s.push('.');
1387 chars.next();
1388 }
1389
1390 if s == "." && self.dialect.supports_numeric_prefix() {
1396 if let Some(Token::Word(_)) = prev_token {
1397 return Ok(Some(Token::Period));
1398 }
1399 }
1400
1401 s += &peeking_next_take_while(chars, |ch, next_ch| {
1403 ch.is_ascii_digit() || is_number_separator(ch, next_ch)
1404 });
1405
1406 if s == "." {
1408 return Ok(Some(Token::Period));
1409 }
1410
1411 let mut exponent_part = String::new();
1413 if chars.peek() == Some(&'e') || chars.peek() == Some(&'E') {
1414 let mut char_clone = chars.peekable.clone();
1415 exponent_part.push(char_clone.next().unwrap());
1416
1417 match char_clone.peek() {
1419 Some(&c) if matches!(c, '+' | '-') => {
1420 exponent_part.push(c);
1421 char_clone.next();
1422 }
1423 _ => (),
1424 }
1425
1426 match char_clone.peek() {
1427 Some(&c) if c.is_ascii_digit() => {
1429 for _ in 0..exponent_part.len() {
1430 chars.next();
1431 }
1432 exponent_part +=
1433 &peeking_take_while(chars, |ch| ch.is_ascii_digit());
1434 s += exponent_part.as_str();
1435 }
1436 _ => (),
1438 }
1439 }
1440
1441 if self.dialect.supports_numeric_prefix() {
1445 if exponent_part.is_empty() {
1446 let word =
1449 peeking_take_while(chars, |ch| self.dialect.is_identifier_part(ch));
1450
1451 if !word.is_empty() {
1452 s += word.as_str();
1453 return Ok(Some(Token::make_word_owned(s, None)));
1454 }
1455 } else if prev_token == Some(&Token::Period) {
1456 return Ok(Some(Token::make_word_owned(s, None)));
1459 }
1460 }
1461
1462 let long = if chars.peek() == Some(&'L') {
1463 chars.next();
1464 true
1465 } else {
1466 false
1467 };
1468 Ok(Some(Token::Number(s, long)))
1469 }
1470 '(' => self.consume_and_return(chars, Token::LParen),
1472 ')' => self.consume_and_return(chars, Token::RParen),
1473 ',' => self.consume_and_return(chars, Token::Comma),
1474 '-' => {
1476 chars.next(); match chars.peek() {
1479 Some('-') => {
1480 let mut is_comment = true;
1481 if self.dialect.requires_single_line_comment_whitespace() {
1482 is_comment = chars
1483 .peekable
1484 .clone()
1485 .nth(1)
1486 .is_some_and(char::is_whitespace);
1487 }
1488
1489 if is_comment {
1490 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1492 return Ok(Some(Token::Whitespace(
1493 Whitespace::SingleLineComment {
1494 prefix: "--".to_owned(),
1495 comment,
1496 },
1497 )));
1498 }
1499
1500 self.start_binop(chars, "-", Token::Minus)
1501 }
1502 Some('>') => {
1503 chars.next();
1504 match chars.peek() {
1505 Some('>') => self.consume_for_binop(chars, "->>", Token::LongArrow),
1506 _ => self.start_binop(chars, "->", Token::Arrow),
1507 }
1508 }
1509 _ => self.start_binop(chars, "-", Token::Minus),
1511 }
1512 }
1513 '/' => {
1514 chars.next(); match chars.peek() {
1516 Some('*') => {
1517 chars.next(); self.tokenize_multiline_comment(chars)
1519 }
1520 Some('/') if dialect_of!(self is SnowflakeDialect) => {
1521 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1523 Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1524 prefix: "//".to_owned(),
1525 comment,
1526 })))
1527 }
1528 Some('/') if dialect_of!(self is DuckDbDialect | GenericDialect) => {
1529 self.consume_and_return(chars, Token::DuckIntDiv)
1530 }
1531 _ => Ok(Some(Token::Div)),
1533 }
1534 }
1535 '+' => self.consume_and_return(chars, Token::Plus),
1536 '*' => self.consume_and_return(chars, Token::Mul),
1537 '%' => {
1538 chars.next(); match chars.peek() {
1540 Some(s) if s.is_whitespace() => Ok(Some(Token::Mod)),
1541 Some(sch) if self.dialect.is_identifier_start('%') => {
1542 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1543 }
1544 _ => self.start_binop(chars, "%", Token::Mod),
1545 }
1546 }
1547 '|' => {
1548 chars.next(); match chars.peek() {
1550 Some('/') => self.consume_for_binop(chars, "|/", Token::PGSquareRoot),
1551 Some('|') => {
1552 chars.next(); match chars.peek() {
1554 Some('/') => {
1555 self.consume_for_binop(chars, "||/", Token::PGCubeRoot)
1556 }
1557 _ => self.start_binop(chars, "||", Token::StringConcat),
1558 }
1559 }
1560 Some('&') if self.dialect.supports_geometric_types() => {
1561 chars.next(); match chars.peek() {
1563 Some('>') => self.consume_for_binop(
1564 chars,
1565 "|&>",
1566 Token::VerticalBarAmpersandRightAngleBracket,
1567 ),
1568 _ => self.start_binop_opt(chars, "|&", None),
1569 }
1570 }
1571 Some('>') if self.dialect.supports_geometric_types() => {
1572 chars.next(); match chars.peek() {
1574 Some('>') => self.consume_for_binop(
1575 chars,
1576 "|>>",
1577 Token::VerticalBarShiftRight,
1578 ),
1579 _ => self.start_binop_opt(chars, "|>", None),
1580 }
1581 }
1582 Some('>') if self.dialect.supports_pipe_operator() => {
1583 self.consume_for_binop(chars, "|>", Token::VerticalBarRightAngleBracket)
1584 }
1585 _ => self.start_binop(chars, "|", Token::Pipe),
1587 }
1588 }
1589 '=' => {
1590 chars.next(); match chars.peek() {
1592 Some('>') => self.consume_and_return(chars, Token::RArrow),
1593 Some('=') => self.consume_and_return(chars, Token::DoubleEq),
1594 _ => Ok(Some(Token::Eq)),
1595 }
1596 }
1597 '!' => {
1598 chars.next(); match chars.peek() {
1600 Some('=') => self.consume_and_return(chars, Token::Neq),
1601 Some('!') => self.consume_and_return(chars, Token::DoubleExclamationMark),
1602 Some('~') => {
1603 chars.next();
1604 match chars.peek() {
1605 Some('*') => self
1606 .consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
1607 Some('~') => {
1608 chars.next();
1609 match chars.peek() {
1610 Some('*') => self.consume_and_return(
1611 chars,
1612 Token::ExclamationMarkDoubleTildeAsterisk,
1613 ),
1614 _ => Ok(Some(Token::ExclamationMarkDoubleTilde)),
1615 }
1616 }
1617 _ => Ok(Some(Token::ExclamationMarkTilde)),
1618 }
1619 }
1620 _ => Ok(Some(Token::ExclamationMark)),
1621 }
1622 }
1623 '<' => {
1624 chars.next(); match chars.peek() {
1626 Some('=') => {
1627 chars.next();
1628 match chars.peek() {
1629 Some('>') => self.consume_for_binop(chars, "<=>", Token::Spaceship),
1630 Some('+') | Some('-') => Ok(Some(Token::LtEq)),
1633 _ => self.start_binop(chars, "<=", Token::LtEq),
1634 }
1635 }
1636 Some('|') if self.dialect.supports_geometric_types() => {
1637 self.consume_for_binop(chars, "<<|", Token::ShiftLeftVerticalBar)
1638 }
1639 Some('>') => self.consume_for_binop(chars, "<>", Token::Neq),
1640 Some('<') if self.dialect.supports_geometric_types() => {
1641 chars.next(); match chars.peek() {
1643 Some('|') => self.consume_for_binop(
1644 chars,
1645 "<<|",
1646 Token::ShiftLeftVerticalBar,
1647 ),
1648 _ => self.start_binop(chars, "<<", Token::ShiftLeft),
1649 }
1650 }
1651 Some('<') => self.consume_for_binop(chars, "<<", Token::ShiftLeft),
1652 Some('+') => Ok(Some(Token::Lt)),
1655 Some('-') if self.dialect.supports_geometric_types() => {
1656 if chars.peekable.clone().nth(1) == Some('>') {
1657 chars.next(); self.consume_for_binop(chars, "<->", Token::TwoWayArrow)
1659 } else {
1660 Ok(Some(Token::Lt))
1661 }
1662 }
1663 Some('^') if self.dialect.supports_geometric_types() => {
1664 self.consume_for_binop(chars, "<^", Token::LeftAngleBracketCaret)
1665 }
1666 Some('@') => self.consume_for_binop(chars, "<@", Token::ArrowAt),
1667 _ => self.start_binop(chars, "<", Token::Lt),
1668 }
1669 }
1670 '>' => {
1671 chars.next(); match chars.peek() {
1673 Some('=') => self.consume_for_binop(chars, ">=", Token::GtEq),
1674 Some('>') => self.consume_for_binop(chars, ">>", Token::ShiftRight),
1675 Some('^') if self.dialect.supports_geometric_types() => {
1676 self.consume_for_binop(chars, ">^", Token::RightAngleBracketCaret)
1677 }
1678 _ => self.start_binop(chars, ">", Token::Gt),
1679 }
1680 }
1681 ':' => {
1682 chars.next();
1683 match chars.peek() {
1684 Some(':') => self.consume_and_return(chars, Token::DoubleColon),
1685 Some('=') => self.consume_and_return(chars, Token::Assignment),
1686 _ => Ok(Some(Token::Colon)),
1687 }
1688 }
1689 ';' => self.consume_and_return(chars, Token::SemiColon),
1690 '\\' => self.consume_and_return(chars, Token::Backslash),
1691 '[' => self.consume_and_return(chars, Token::LBracket),
1692 ']' => self.consume_and_return(chars, Token::RBracket),
1693 '&' => {
1694 chars.next(); match chars.peek() {
1696 Some('>') if self.dialect.supports_geometric_types() => {
1697 chars.next();
1698 self.consume_and_return(chars, Token::AmpersandRightAngleBracket)
1699 }
1700 Some('<') if self.dialect.supports_geometric_types() => {
1701 chars.next(); match chars.peek() {
1703 Some('|') => self.consume_and_return(
1704 chars,
1705 Token::AmpersandLeftAngleBracketVerticalBar,
1706 ),
1707 _ => {
1708 self.start_binop(chars, "&<", Token::AmpersandLeftAngleBracket)
1709 }
1710 }
1711 }
1712 Some('&') => {
1713 chars.next(); self.start_binop(chars, "&&", Token::Overlap)
1715 }
1716 _ => self.start_binop(chars, "&", Token::Ampersand),
1718 }
1719 }
1720 '^' => {
1721 chars.next(); match chars.peek() {
1723 Some('@') => self.consume_and_return(chars, Token::CaretAt),
1724 _ => Ok(Some(Token::Caret)),
1725 }
1726 }
1727 '{' => self.consume_and_return(chars, Token::LBrace),
1728 '}' => self.consume_and_return(chars, Token::RBrace),
1729 '#' if dialect_of!(self is SnowflakeDialect | BigQueryDialect | MySqlDialect | HiveDialect) =>
1730 {
1731 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1733 Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1734 prefix: "#".to_owned(),
1735 comment,
1736 })))
1737 }
1738 '~' => {
1739 chars.next(); match chars.peek() {
1741 Some('*') => self.consume_for_binop(chars, "~*", Token::TildeAsterisk),
1742 Some('=') if self.dialect.supports_geometric_types() => {
1743 self.consume_for_binop(chars, "~=", Token::TildeEqual)
1744 }
1745 Some('~') => {
1746 chars.next();
1747 match chars.peek() {
1748 Some('*') => {
1749 self.consume_for_binop(chars, "~~*", Token::DoubleTildeAsterisk)
1750 }
1751 _ => self.start_binop(chars, "~~", Token::DoubleTilde),
1752 }
1753 }
1754 _ => self.start_binop(chars, "~", Token::Tilde),
1755 }
1756 }
1757 '#' => {
1758 chars.next();
1759 match chars.peek() {
1760 Some('-') => self.consume_for_binop(chars, "#-", Token::HashMinus),
1761 Some('>') => {
1762 chars.next();
1763 match chars.peek() {
1764 Some('>') => {
1765 self.consume_for_binop(chars, "#>>", Token::HashLongArrow)
1766 }
1767 _ => self.start_binop(chars, "#>", Token::HashArrow),
1768 }
1769 }
1770 Some(' ') => Ok(Some(Token::Sharp)),
1771 Some('#') if self.dialect.supports_geometric_types() => {
1772 self.consume_for_binop(chars, "##", Token::DoubleSharp)
1773 }
1774 Some(sch) if self.dialect.is_identifier_start('#') => {
1775 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1776 }
1777 _ => self.start_binop(chars, "#", Token::Sharp),
1778 }
1779 }
1780 '@' => {
1781 chars.next();
1782 match chars.peek() {
1783 Some('@') if self.dialect.supports_geometric_types() => {
1784 self.consume_and_return(chars, Token::AtAt)
1785 }
1786 Some('-') if self.dialect.supports_geometric_types() => {
1787 chars.next();
1788 match chars.peek() {
1789 Some('@') => self.consume_and_return(chars, Token::AtDashAt),
1790 _ => self.start_binop_opt(chars, "@-", None),
1791 }
1792 }
1793 Some('>') => self.consume_and_return(chars, Token::AtArrow),
1794 Some('?') => self.consume_and_return(chars, Token::AtQuestion),
1795 Some('@') => {
1796 chars.next();
1797 match chars.peek() {
1798 Some(' ') => Ok(Some(Token::AtAt)),
1799 Some(tch) if self.dialect.is_identifier_start('@') => {
1800 self.tokenize_identifier_or_keyword([ch, '@', *tch], chars)
1801 }
1802 _ => Ok(Some(Token::AtAt)),
1803 }
1804 }
1805 Some(' ') => Ok(Some(Token::AtSign)),
1806 Some('\'') => Ok(Some(Token::AtSign)),
1816 Some('\"') => Ok(Some(Token::AtSign)),
1817 Some('`') => Ok(Some(Token::AtSign)),
1818 Some(sch) if self.dialect.is_identifier_start('@') => {
1819 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1820 }
1821 _ => Ok(Some(Token::AtSign)),
1822 }
1823 }
1824 '?' if self.dialect.supports_geometric_types() => {
1826 chars.next(); match chars.peek() {
1828 Some('|') => {
1829 chars.next();
1830 match chars.peek() {
1831 Some('|') => self.consume_and_return(
1832 chars,
1833 Token::QuestionMarkDoubleVerticalBar,
1834 ),
1835 _ => Ok(Some(Token::QuestionPipe)),
1836 }
1837 }
1838
1839 Some('&') => self.consume_and_return(chars, Token::QuestionAnd),
1840 Some('-') => {
1841 chars.next(); match chars.peek() {
1843 Some('|') => self
1844 .consume_and_return(chars, Token::QuestionMarkDashVerticalBar),
1845 _ => Ok(Some(Token::QuestionMarkDash)),
1846 }
1847 }
1848 Some('#') => self.consume_and_return(chars, Token::QuestionMarkSharp),
1849 _ => Ok(Some(Token::Question)),
1850 }
1851 }
1852 '?' => {
1853 chars.next();
1854 let s = peeking_take_while(chars, |ch| ch.is_numeric());
1855 Ok(Some(Token::Placeholder(format!("?{s}"))))
1856 }
1857
1858 ch if self.dialect.is_identifier_start(ch) => {
1860 self.tokenize_identifier_or_keyword([ch], chars)
1861 }
1862 '$' => Ok(Some(self.tokenize_dollar_preceded_value(chars)?)),
1863
1864 ch if ch.is_whitespace() => {
1866 self.consume_and_return(chars, Token::Whitespace(Whitespace::Space))
1867 }
1868 other => self.consume_and_return(chars, Token::Char(other)),
1869 },
1870 None => Ok(None),
1871 }
1872 }
1873
1874 fn consume_for_binop(
1876 &self,
1877 chars: &mut State,
1878 prefix: &str,
1879 default: Token,
1880 ) -> Result<Option<Token>, TokenizerError> {
1881 chars.next(); self.start_binop_opt(chars, prefix, Some(default))
1883 }
1884
1885 fn start_binop(
1887 &self,
1888 chars: &mut State,
1889 prefix: &str,
1890 default: Token,
1891 ) -> Result<Option<Token>, TokenizerError> {
1892 self.start_binop_opt(chars, prefix, Some(default))
1893 }
1894
1895 fn start_binop_opt(
1897 &self,
1898 chars: &mut State,
1899 prefix: &str,
1900 default: Option<Token>,
1901 ) -> Result<Option<Token>, TokenizerError> {
1902 let mut custom = None;
1903 while let Some(&ch) = chars.peek() {
1904 if !self.dialect.is_custom_operator_part(ch) {
1905 break;
1906 }
1907
1908 custom.get_or_insert_with(|| prefix.to_string()).push(ch);
1909 chars.next();
1910 }
1911 match (custom, default) {
1912 (Some(custom), _) => Ok(Token::CustomBinaryOperator(custom).into()),
1913 (None, Some(tok)) => Ok(Some(tok)),
1914 (None, None) => self.tokenizer_error(
1915 chars.location(),
1916 format!("Expected a valid binary operator after '{prefix}'"),
1917 ),
1918 }
1919 }
1920
1921 fn tokenize_dollar_preceded_value(&self, chars: &mut State) -> Result<Token, TokenizerError> {
1923 let mut s = String::new();
1924 let mut value = String::new();
1925
1926 chars.next();
1927
1928 if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1930 chars.next();
1931
1932 let mut is_terminated = false;
1933 let mut prev: Option<char> = None;
1934
1935 while let Some(&ch) = chars.peek() {
1936 if prev == Some('$') {
1937 if ch == '$' {
1938 chars.next();
1939 is_terminated = true;
1940 break;
1941 } else {
1942 s.push('$');
1943 s.push(ch);
1944 }
1945 } else if ch != '$' {
1946 s.push(ch);
1947 }
1948
1949 prev = Some(ch);
1950 chars.next();
1951 }
1952
1953 return if chars.peek().is_none() && !is_terminated {
1954 self.tokenizer_error(chars.location(), "Unterminated dollar-quoted string")
1955 } else {
1956 Ok(Token::DollarQuotedString(DollarQuotedString {
1957 value: s,
1958 tag: None,
1959 }))
1960 };
1961 } else {
1962 value.push_str(&peeking_take_while(chars, |ch| {
1963 ch.is_alphanumeric()
1964 || ch == '_'
1965 || matches!(ch, '$' if self.dialect.supports_dollar_placeholder())
1967 }));
1968
1969 if matches!(chars.peek(), Some('.'))
1972 && self.dialect.supports_dollar_as_money_prefix()
1973 && !value.is_empty()
1974 && value.chars().all(|c| c.is_ascii_digit())
1975 {
1976 value.push('.');
1977 chars.next();
1978 value.push_str(&peeking_take_while(chars, |ch| ch.is_ascii_digit()));
1979 return Ok(Token::Placeholder(format!("${value}")));
1980 }
1981
1982 if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1984 chars.next();
1985
1986 let mut temp = String::new();
1987 let end_delimiter = format!("${value}$");
1988
1989 loop {
1990 match chars.next() {
1991 Some(ch) => {
1992 temp.push(ch);
1993
1994 if temp.ends_with(&end_delimiter) {
1995 if let Some(temp) = temp.strip_suffix(&end_delimiter) {
1996 s.push_str(temp);
1997 }
1998 break;
1999 }
2000 }
2001 None => {
2002 if temp.ends_with(&end_delimiter) {
2003 if let Some(temp) = temp.strip_suffix(&end_delimiter) {
2004 s.push_str(temp);
2005 }
2006 break;
2007 }
2008
2009 return self.tokenizer_error(
2010 chars.location(),
2011 "Unterminated dollar-quoted, expected $",
2012 );
2013 }
2014 }
2015 }
2016 } else {
2017 return Ok(Token::Placeholder(format!("${value}")));
2018 }
2019 }
2020
2021 Ok(Token::DollarQuotedString(DollarQuotedString {
2022 value: s,
2023 tag: if value.is_empty() { None } else { Some(value) },
2024 }))
2025 }
2026
2027 fn tokenizer_error<R>(
2028 &self,
2029 loc: Location,
2030 message: impl Into<String>,
2031 ) -> Result<R, TokenizerError> {
2032 Err(TokenizerError {
2033 message: message.into(),
2034 location: loc,
2035 })
2036 }
2037
2038 fn tokenize_single_line_comment(&self, chars: &mut State) -> String {
2040 peeking_take_while(chars, |ch| match ch {
2041 '\n' => false, '\r' if dialect_of!(self is PostgreSqlDialect) => false, _ => true, })
2045 }
2046
2047 fn tokenize_word(&self, first_chars: impl Into<String>, chars: &mut State) -> String {
2049 let mut s = first_chars.into();
2050 s.push_str(&peeking_take_while(chars, |ch| {
2051 self.dialect.is_identifier_part(ch)
2052 }));
2053 s
2054 }
2055
2056 fn tokenize_quoted_identifier(
2058 &self,
2059 quote_start: char,
2060 chars: &mut State,
2061 ) -> Result<String, TokenizerError> {
2062 let error_loc = chars.location();
2063 chars.next(); let quote_end = Word::matching_end_quote(quote_start);
2065 let (s, last_char) = self.parse_quoted_ident(chars, quote_end);
2066
2067 if last_char == Some(quote_end) {
2068 Ok(s)
2069 } else {
2070 self.tokenizer_error(
2071 error_loc,
2072 format!("Expected close delimiter '{quote_end}' before EOF."),
2073 )
2074 }
2075 }
2076
2077 fn tokenize_escaped_single_quoted_string(
2079 &self,
2080 starting_loc: Location,
2081 chars: &mut State,
2082 ) -> Result<String, TokenizerError> {
2083 if let Some(s) = unescape_single_quoted_string(chars) {
2084 return Ok(s);
2085 }
2086
2087 self.tokenizer_error(starting_loc, "Unterminated encoded string literal")
2088 }
2089
2090 fn tokenize_single_or_triple_quoted_string<F>(
2093 &self,
2094 chars: &mut State,
2095 quote_style: char,
2096 backslash_escape: bool,
2097 single_quote_token: F,
2098 triple_quote_token: F,
2099 ) -> Result<Option<Token>, TokenizerError>
2100 where
2101 F: Fn(String) -> Token,
2102 {
2103 let error_loc = chars.location();
2104
2105 let mut num_opening_quotes = 0u8;
2106 for _ in 0..3 {
2107 if Some("e_style) == chars.peek() {
2108 chars.next(); num_opening_quotes += 1;
2110 } else {
2111 break;
2112 }
2113 }
2114
2115 let (token_fn, num_quote_chars) = match num_opening_quotes {
2116 1 => (single_quote_token, NumStringQuoteChars::One),
2117 2 => {
2118 return Ok(Some(single_quote_token("".into())));
2120 }
2121 3 => {
2122 let Some(num_quote_chars) = NonZeroU8::new(3) else {
2123 return self.tokenizer_error(error_loc, "invalid number of opening quotes");
2124 };
2125 (
2126 triple_quote_token,
2127 NumStringQuoteChars::Many(num_quote_chars),
2128 )
2129 }
2130 _ => {
2131 return self.tokenizer_error(error_loc, "invalid string literal opening");
2132 }
2133 };
2134
2135 let settings = TokenizeQuotedStringSettings {
2136 quote_style,
2137 num_quote_chars,
2138 num_opening_quotes_to_consume: 0,
2139 backslash_escape,
2140 };
2141
2142 self.tokenize_quoted_string(chars, settings)
2143 .map(token_fn)
2144 .map(Some)
2145 }
2146
2147 fn tokenize_single_quoted_string(
2149 &self,
2150 chars: &mut State,
2151 quote_style: char,
2152 backslash_escape: bool,
2153 ) -> Result<String, TokenizerError> {
2154 self.tokenize_quoted_string(
2155 chars,
2156 TokenizeQuotedStringSettings {
2157 quote_style,
2158 num_quote_chars: NumStringQuoteChars::One,
2159 num_opening_quotes_to_consume: 1,
2160 backslash_escape,
2161 },
2162 )
2163 }
2164
2165 fn tokenize_quote_delimited_string(
2169 &self,
2170 chars: &mut State,
2171 literal_prefix: &[char],
2174 ) -> Result<QuoteDelimitedString, TokenizerError> {
2175 let literal_start_loc = chars.location();
2176 chars.next();
2177
2178 let start_quote_loc = chars.location();
2179 let (start_quote, end_quote) = match chars.next() {
2180 None | Some(' ') | Some('\t') | Some('\r') | Some('\n') => {
2181 return self.tokenizer_error(
2182 start_quote_loc,
2183 format!(
2184 "Invalid space, tab, newline, or EOF after '{}''",
2185 String::from_iter(literal_prefix)
2186 ),
2187 );
2188 }
2189 Some(c) => (
2190 c,
2191 match c {
2192 '[' => ']',
2193 '{' => '}',
2194 '<' => '>',
2195 '(' => ')',
2196 c => c,
2197 },
2198 ),
2199 };
2200
2201 let mut value = String::new();
2203 while let Some(ch) = chars.next() {
2204 if ch == end_quote {
2205 if let Some('\'') = chars.peek() {
2206 chars.next(); return Ok(QuoteDelimitedString {
2208 start_quote,
2209 value,
2210 end_quote,
2211 });
2212 }
2213 }
2214 value.push(ch);
2215 }
2216
2217 self.tokenizer_error(literal_start_loc, "Unterminated string literal")
2218 }
2219
2220 fn tokenize_quoted_string(
2222 &self,
2223 chars: &mut State,
2224 settings: TokenizeQuotedStringSettings,
2225 ) -> Result<String, TokenizerError> {
2226 let mut s = String::new();
2227 let error_loc = chars.location();
2228
2229 for _ in 0..settings.num_opening_quotes_to_consume {
2231 if Some(settings.quote_style) != chars.next() {
2232 return self.tokenizer_error(error_loc, "invalid string literal opening");
2233 }
2234 }
2235
2236 let mut num_consecutive_quotes = 0;
2237 while let Some(&ch) = chars.peek() {
2238 let pending_final_quote = match settings.num_quote_chars {
2239 NumStringQuoteChars::One => Some(NumStringQuoteChars::One),
2240 n @ NumStringQuoteChars::Many(count)
2241 if num_consecutive_quotes + 1 == count.get() =>
2242 {
2243 Some(n)
2244 }
2245 NumStringQuoteChars::Many(_) => None,
2246 };
2247
2248 match ch {
2249 char if char == settings.quote_style && pending_final_quote.is_some() => {
2250 chars.next(); if let Some(NumStringQuoteChars::Many(count)) = pending_final_quote {
2253 let mut buf = s.chars();
2258 for _ in 1..count.get() {
2259 buf.next_back();
2260 }
2261 return Ok(buf.as_str().to_string());
2262 } else if chars
2263 .peek()
2264 .map(|c| *c == settings.quote_style)
2265 .unwrap_or(false)
2266 {
2267 s.push(ch);
2268 if !self.unescape {
2269 s.push(ch);
2271 }
2272 chars.next();
2273 } else {
2274 return Ok(s);
2275 }
2276 }
2277 '\\' if settings.backslash_escape => {
2278 chars.next();
2280
2281 num_consecutive_quotes = 0;
2282
2283 if let Some(next) = chars.peek() {
2284 if !self.unescape
2285 || (self.dialect.ignores_wildcard_escapes()
2286 && (*next == '%' || *next == '_'))
2287 {
2288 s.push(ch);
2292 s.push(*next);
2293 chars.next(); } else {
2295 let n = match next {
2296 '0' => '\0',
2297 'a' => '\u{7}',
2298 'b' => '\u{8}',
2299 'f' => '\u{c}',
2300 'n' => '\n',
2301 'r' => '\r',
2302 't' => '\t',
2303 'Z' => '\u{1a}',
2304 _ => *next,
2305 };
2306 s.push(n);
2307 chars.next(); }
2309 }
2310 }
2311 ch => {
2312 chars.next(); if ch == settings.quote_style {
2315 num_consecutive_quotes += 1;
2316 } else {
2317 num_consecutive_quotes = 0;
2318 }
2319
2320 s.push(ch);
2321 }
2322 }
2323 }
2324 self.tokenizer_error(error_loc, "Unterminated string literal")
2325 }
2326
2327 fn tokenize_multiline_comment(
2328 &self,
2329 chars: &mut State,
2330 ) -> Result<Option<Token>, TokenizerError> {
2331 let mut s = String::new();
2332 let mut nested = 1;
2333 let supports_nested_comments = self.dialect.supports_nested_comments();
2334 loop {
2335 match chars.next() {
2336 Some('/') if matches!(chars.peek(), Some('*')) && supports_nested_comments => {
2337 chars.next(); s.push('/');
2339 s.push('*');
2340 nested += 1;
2341 }
2342 Some('*') if matches!(chars.peek(), Some('/')) => {
2343 chars.next(); nested -= 1;
2345 if nested == 0 {
2346 break Ok(Some(Token::Whitespace(Whitespace::MultiLineComment(s))));
2347 }
2348 s.push('*');
2349 s.push('/');
2350 }
2351 Some(ch) => {
2352 s.push(ch);
2353 }
2354 None => {
2355 break self.tokenizer_error(
2356 chars.location(),
2357 "Unexpected EOF while in a multi-line comment",
2358 );
2359 }
2360 }
2361 }
2362 }
2363
2364 fn parse_quoted_ident(&self, chars: &mut State, quote_end: char) -> (String, Option<char>) {
2365 let mut last_char = None;
2366 let mut s = String::new();
2367 while let Some(ch) = chars.next() {
2368 if ch == quote_end {
2369 if chars.peek() == Some("e_end) {
2370 chars.next();
2371 s.push(ch);
2372 if !self.unescape {
2373 s.push(ch);
2375 }
2376 } else {
2377 last_char = Some(quote_end);
2378 break;
2379 }
2380 } else {
2381 s.push(ch);
2382 }
2383 }
2384 (s, last_char)
2385 }
2386
2387 #[allow(clippy::unnecessary_wraps)]
2388 fn consume_and_return(
2389 &self,
2390 chars: &mut State,
2391 t: Token,
2392 ) -> Result<Option<Token>, TokenizerError> {
2393 chars.next();
2394 Ok(Some(t))
2395 }
2396}
2397
2398fn peeking_take_while(chars: &mut State, mut predicate: impl FnMut(char) -> bool) -> String {
2402 let mut s = String::new();
2403 while let Some(&ch) = chars.peek() {
2404 if predicate(ch) {
2405 chars.next(); s.push(ch);
2407 } else {
2408 break;
2409 }
2410 }
2411 s
2412}
2413
2414fn peeking_next_take_while(
2416 chars: &mut State,
2417 mut predicate: impl FnMut(char, Option<char>) -> bool,
2418) -> String {
2419 let mut s = String::new();
2420 while let Some(&ch) = chars.peek() {
2421 let next_char = chars.peekable.clone().nth(1);
2422 if predicate(ch, next_char) {
2423 chars.next(); s.push(ch);
2425 } else {
2426 break;
2427 }
2428 }
2429 s
2430}
2431
2432fn unescape_single_quoted_string(chars: &mut State<'_>) -> Option<String> {
2433 Unescape::new(chars).unescape()
2434}
2435
2436struct Unescape<'a: 'b, 'b> {
2437 chars: &'b mut State<'a>,
2438}
2439
2440impl<'a: 'b, 'b> Unescape<'a, 'b> {
2441 fn new(chars: &'b mut State<'a>) -> Self {
2442 Self { chars }
2443 }
2444 fn unescape(mut self) -> Option<String> {
2445 let mut unescaped = String::new();
2446
2447 self.chars.next();
2448
2449 while let Some(c) = self.chars.next() {
2450 if c == '\'' {
2451 if self.chars.peek().map(|c| *c == '\'').unwrap_or(false) {
2453 self.chars.next();
2454 unescaped.push('\'');
2455 continue;
2456 }
2457 return Some(unescaped);
2458 }
2459
2460 if c != '\\' {
2461 unescaped.push(c);
2462 continue;
2463 }
2464
2465 let c = match self.chars.next()? {
2466 'b' => '\u{0008}',
2467 'f' => '\u{000C}',
2468 'n' => '\n',
2469 'r' => '\r',
2470 't' => '\t',
2471 'u' => self.unescape_unicode_16()?,
2472 'U' => self.unescape_unicode_32()?,
2473 'x' => self.unescape_hex()?,
2474 c if c.is_digit(8) => self.unescape_octal(c)?,
2475 c => c,
2476 };
2477
2478 unescaped.push(Self::check_null(c)?);
2479 }
2480
2481 None
2482 }
2483
2484 #[inline]
2485 fn check_null(c: char) -> Option<char> {
2486 if c == '\0' {
2487 None
2488 } else {
2489 Some(c)
2490 }
2491 }
2492
2493 #[inline]
2494 fn byte_to_char<const RADIX: u32>(s: &str) -> Option<char> {
2495 match u32::from_str_radix(s, RADIX) {
2497 Err(_) => None,
2498 Ok(n) => {
2499 let n = n & 0xFF;
2500 if n <= 127 {
2501 char::from_u32(n)
2502 } else {
2503 None
2504 }
2505 }
2506 }
2507 }
2508
2509 fn unescape_hex(&mut self) -> Option<char> {
2511 let mut s = String::new();
2512
2513 for _ in 0..2 {
2514 match self.next_hex_digit() {
2515 Some(c) => s.push(c),
2516 None => break,
2517 }
2518 }
2519
2520 if s.is_empty() {
2521 return Some('x');
2522 }
2523
2524 Self::byte_to_char::<16>(&s)
2525 }
2526
2527 #[inline]
2528 fn next_hex_digit(&mut self) -> Option<char> {
2529 match self.chars.peek() {
2530 Some(c) if c.is_ascii_hexdigit() => self.chars.next(),
2531 _ => None,
2532 }
2533 }
2534
2535 fn unescape_octal(&mut self, c: char) -> Option<char> {
2537 let mut s = String::new();
2538
2539 s.push(c);
2540 for _ in 0..2 {
2541 match self.next_octal_digest() {
2542 Some(c) => s.push(c),
2543 None => break,
2544 }
2545 }
2546
2547 Self::byte_to_char::<8>(&s)
2548 }
2549
2550 #[inline]
2551 fn next_octal_digest(&mut self) -> Option<char> {
2552 match self.chars.peek() {
2553 Some(c) if c.is_digit(8) => self.chars.next(),
2554 _ => None,
2555 }
2556 }
2557
2558 fn unescape_unicode_16(&mut self) -> Option<char> {
2560 self.unescape_unicode::<4>()
2561 }
2562
2563 fn unescape_unicode_32(&mut self) -> Option<char> {
2565 self.unescape_unicode::<8>()
2566 }
2567
2568 fn unescape_unicode<const NUM: usize>(&mut self) -> Option<char> {
2569 let mut s = String::new();
2570 for _ in 0..NUM {
2571 s.push(self.chars.next()?);
2572 }
2573 match u32::from_str_radix(&s, 16) {
2574 Err(_) => None,
2575 Ok(n) => char::from_u32(n),
2576 }
2577 }
2578}
2579
2580fn unescape_unicode_single_quoted_string(chars: &mut State<'_>) -> Result<String, TokenizerError> {
2581 let mut unescaped = String::new();
2582 chars.next(); while let Some(c) = chars.next() {
2584 match c {
2585 '\'' => {
2586 if chars.peek() == Some(&'\'') {
2587 chars.next();
2588 unescaped.push('\'');
2589 } else {
2590 return Ok(unescaped);
2591 }
2592 }
2593 '\\' => match chars.peek() {
2594 Some('\\') => {
2595 chars.next();
2596 unescaped.push('\\');
2597 }
2598 Some('+') => {
2599 chars.next();
2600 unescaped.push(take_char_from_hex_digits(chars, 6)?);
2601 }
2602 _ => unescaped.push(take_char_from_hex_digits(chars, 4)?),
2603 },
2604 _ => {
2605 unescaped.push(c);
2606 }
2607 }
2608 }
2609 Err(TokenizerError {
2610 message: "Unterminated unicode encoded string literal".to_string(),
2611 location: chars.location(),
2612 })
2613}
2614
2615fn take_char_from_hex_digits(
2616 chars: &mut State<'_>,
2617 max_digits: usize,
2618) -> Result<char, TokenizerError> {
2619 let mut result = 0u32;
2620 for _ in 0..max_digits {
2621 let next_char = chars.next().ok_or_else(|| TokenizerError {
2622 message: "Unexpected EOF while parsing hex digit in escaped unicode string."
2623 .to_string(),
2624 location: chars.location(),
2625 })?;
2626 let digit = next_char.to_digit(16).ok_or_else(|| TokenizerError {
2627 message: format!("Invalid hex digit in escaped unicode string: {next_char}"),
2628 location: chars.location(),
2629 })?;
2630 result = result * 16 + digit;
2631 }
2632 char::from_u32(result).ok_or_else(|| TokenizerError {
2633 message: format!("Invalid unicode character: {result:x}"),
2634 location: chars.location(),
2635 })
2636}
2637
2638#[cfg(test)]
2639mod tests {
2640 use super::*;
2641 use crate::dialect::{
2642 BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, MySqlDialect,
2643 PostgreSqlDialect, SQLiteDialect,
2644 };
2645 use crate::test_utils::{all_dialects, all_dialects_except, all_dialects_where};
2646 use core::fmt::Debug;
2647
2648 #[test]
2649 fn tokenizer_error_impl() {
2650 let err = TokenizerError {
2651 message: "test".into(),
2652 location: Location { line: 1, column: 1 },
2653 };
2654 {
2655 use core::error::Error;
2656 assert!(err.source().is_none());
2657 }
2658 assert_eq!(err.to_string(), "test at Line: 1, Column: 1");
2659 }
2660
2661 #[test]
2662 fn tokenize_select_1() {
2663 let sql = String::from("SELECT 1");
2664 let dialect = GenericDialect {};
2665 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2666
2667 let expected = vec![
2668 Token::make_keyword("SELECT"),
2669 Token::Whitespace(Whitespace::Space),
2670 Token::Number(String::from("1"), false),
2671 ];
2672
2673 compare(expected, tokens);
2674 }
2675
2676 #[test]
2677 fn tokenize_select_float() {
2678 let sql = String::from("SELECT .1");
2679 let dialect = GenericDialect {};
2680 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2681
2682 let expected = vec![
2683 Token::make_keyword("SELECT"),
2684 Token::Whitespace(Whitespace::Space),
2685 Token::Number(String::from(".1"), false),
2686 ];
2687
2688 compare(expected, tokens);
2689 }
2690
2691 #[test]
2692 fn tokenize_with_mapper() {
2693 let sql = String::from("SELECT ?");
2694 let dialect = GenericDialect {};
2695 let mut param_num = 1;
2696
2697 let mut tokens = vec![];
2698 Tokenizer::new(&dialect, &sql)
2699 .tokenize_with_location_into_buf_with_mapper(&mut tokens, |mut token_span| {
2700 token_span.token = match token_span.token {
2701 Token::Placeholder(n) => Token::Placeholder(if n == "?" {
2702 let ret = format!("${}", param_num);
2703 param_num += 1;
2704 ret
2705 } else {
2706 n
2707 }),
2708 token => token,
2709 };
2710 token_span
2711 })
2712 .unwrap();
2713 let actual = tokens.into_iter().map(|t| t.token).collect();
2714 let expected = vec![
2715 Token::make_keyword("SELECT"),
2716 Token::Whitespace(Whitespace::Space),
2717 Token::Placeholder("$1".to_string()),
2718 ];
2719
2720 compare(expected, actual);
2721 }
2722
2723 #[test]
2724 fn tokenize_clickhouse_double_equal() {
2725 let sql = String::from("SELECT foo=='1'");
2726 let dialect = ClickHouseDialect {};
2727 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2728 let tokens = tokenizer.tokenize().unwrap();
2729
2730 let expected = vec![
2731 Token::make_keyword("SELECT"),
2732 Token::Whitespace(Whitespace::Space),
2733 Token::Word(Word {
2734 value: "foo".to_string(),
2735 quote_style: None,
2736 keyword: Keyword::NoKeyword,
2737 }),
2738 Token::DoubleEq,
2739 Token::SingleQuotedString("1".to_string()),
2740 ];
2741
2742 compare(expected, tokens);
2743 }
2744
2745 #[test]
2746 fn tokenize_numeric_literal_underscore() {
2747 let dialect = GenericDialect {};
2748 let sql = String::from("SELECT 10_000");
2749 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2750 let tokens = tokenizer.tokenize().unwrap();
2751 let expected = vec![
2752 Token::make_keyword("SELECT"),
2753 Token::Whitespace(Whitespace::Space),
2754 Token::Number("10".to_string(), false),
2755 Token::make_word("_000", None),
2756 ];
2757 compare(expected, tokens);
2758
2759 all_dialects_where(|dialect| dialect.supports_numeric_literal_underscores()).tokenizes_to(
2760 "SELECT 10_000, _10_000, 10_00_, 10___0",
2761 vec![
2762 Token::make_keyword("SELECT"),
2763 Token::Whitespace(Whitespace::Space),
2764 Token::Number("10_000".to_string(), false),
2765 Token::Comma,
2766 Token::Whitespace(Whitespace::Space),
2767 Token::make_word("_10_000", None), Token::Comma,
2769 Token::Whitespace(Whitespace::Space),
2770 Token::Number("10_00".to_string(), false),
2771 Token::make_word("_", None), Token::Comma,
2773 Token::Whitespace(Whitespace::Space),
2774 Token::Number("10".to_string(), false),
2775 Token::make_word("___0", None), ],
2777 );
2778 }
2779
2780 #[test]
2781 fn tokenize_select_exponent() {
2782 let sql = String::from("SELECT 1e10, 1e-10, 1e+10, 1ea, 1e-10a, 1e-10-10");
2783 let dialect = GenericDialect {};
2784 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2785
2786 let expected = vec![
2787 Token::make_keyword("SELECT"),
2788 Token::Whitespace(Whitespace::Space),
2789 Token::Number(String::from("1e10"), false),
2790 Token::Comma,
2791 Token::Whitespace(Whitespace::Space),
2792 Token::Number(String::from("1e-10"), false),
2793 Token::Comma,
2794 Token::Whitespace(Whitespace::Space),
2795 Token::Number(String::from("1e+10"), false),
2796 Token::Comma,
2797 Token::Whitespace(Whitespace::Space),
2798 Token::Number(String::from("1"), false),
2799 Token::make_word("ea", None),
2800 Token::Comma,
2801 Token::Whitespace(Whitespace::Space),
2802 Token::Number(String::from("1e-10"), false),
2803 Token::make_word("a", None),
2804 Token::Comma,
2805 Token::Whitespace(Whitespace::Space),
2806 Token::Number(String::from("1e-10"), false),
2807 Token::Minus,
2808 Token::Number(String::from("10"), false),
2809 ];
2810
2811 compare(expected, tokens);
2812 }
2813
2814 #[test]
2815 fn tokenize_scalar_function() {
2816 let sql = String::from("SELECT sqrt(1)");
2817 let dialect = GenericDialect {};
2818 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2819
2820 let expected = vec![
2821 Token::make_keyword("SELECT"),
2822 Token::Whitespace(Whitespace::Space),
2823 Token::make_word("sqrt", None),
2824 Token::LParen,
2825 Token::Number(String::from("1"), false),
2826 Token::RParen,
2827 ];
2828
2829 compare(expected, tokens);
2830 }
2831
2832 #[test]
2833 fn tokenize_string_string_concat() {
2834 let sql = String::from("SELECT 'a' || 'b'");
2835 let dialect = GenericDialect {};
2836 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2837
2838 let expected = vec![
2839 Token::make_keyword("SELECT"),
2840 Token::Whitespace(Whitespace::Space),
2841 Token::SingleQuotedString(String::from("a")),
2842 Token::Whitespace(Whitespace::Space),
2843 Token::StringConcat,
2844 Token::Whitespace(Whitespace::Space),
2845 Token::SingleQuotedString(String::from("b")),
2846 ];
2847
2848 compare(expected, tokens);
2849 }
2850 #[test]
2851 fn tokenize_bitwise_op() {
2852 let sql = String::from("SELECT one | two ^ three");
2853 let dialect = GenericDialect {};
2854 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2855
2856 let expected = vec![
2857 Token::make_keyword("SELECT"),
2858 Token::Whitespace(Whitespace::Space),
2859 Token::make_word("one", None),
2860 Token::Whitespace(Whitespace::Space),
2861 Token::Pipe,
2862 Token::Whitespace(Whitespace::Space),
2863 Token::make_word("two", None),
2864 Token::Whitespace(Whitespace::Space),
2865 Token::Caret,
2866 Token::Whitespace(Whitespace::Space),
2867 Token::make_word("three", None),
2868 ];
2869 compare(expected, tokens);
2870 }
2871
2872 #[test]
2873 fn tokenize_logical_xor() {
2874 let sql =
2875 String::from("SELECT true XOR true, false XOR false, true XOR false, false XOR true");
2876 let dialect = GenericDialect {};
2877 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2878
2879 let expected = vec![
2880 Token::make_keyword("SELECT"),
2881 Token::Whitespace(Whitespace::Space),
2882 Token::make_keyword("true"),
2883 Token::Whitespace(Whitespace::Space),
2884 Token::make_keyword("XOR"),
2885 Token::Whitespace(Whitespace::Space),
2886 Token::make_keyword("true"),
2887 Token::Comma,
2888 Token::Whitespace(Whitespace::Space),
2889 Token::make_keyword("false"),
2890 Token::Whitespace(Whitespace::Space),
2891 Token::make_keyword("XOR"),
2892 Token::Whitespace(Whitespace::Space),
2893 Token::make_keyword("false"),
2894 Token::Comma,
2895 Token::Whitespace(Whitespace::Space),
2896 Token::make_keyword("true"),
2897 Token::Whitespace(Whitespace::Space),
2898 Token::make_keyword("XOR"),
2899 Token::Whitespace(Whitespace::Space),
2900 Token::make_keyword("false"),
2901 Token::Comma,
2902 Token::Whitespace(Whitespace::Space),
2903 Token::make_keyword("false"),
2904 Token::Whitespace(Whitespace::Space),
2905 Token::make_keyword("XOR"),
2906 Token::Whitespace(Whitespace::Space),
2907 Token::make_keyword("true"),
2908 ];
2909 compare(expected, tokens);
2910 }
2911
2912 #[test]
2913 fn tokenize_simple_select() {
2914 let sql = String::from("SELECT * FROM customer WHERE id = 1 LIMIT 5");
2915 let dialect = GenericDialect {};
2916 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2917
2918 let expected = vec![
2919 Token::make_keyword("SELECT"),
2920 Token::Whitespace(Whitespace::Space),
2921 Token::Mul,
2922 Token::Whitespace(Whitespace::Space),
2923 Token::make_keyword("FROM"),
2924 Token::Whitespace(Whitespace::Space),
2925 Token::make_word("customer", None),
2926 Token::Whitespace(Whitespace::Space),
2927 Token::make_keyword("WHERE"),
2928 Token::Whitespace(Whitespace::Space),
2929 Token::make_word("id", None),
2930 Token::Whitespace(Whitespace::Space),
2931 Token::Eq,
2932 Token::Whitespace(Whitespace::Space),
2933 Token::Number(String::from("1"), false),
2934 Token::Whitespace(Whitespace::Space),
2935 Token::make_keyword("LIMIT"),
2936 Token::Whitespace(Whitespace::Space),
2937 Token::Number(String::from("5"), false),
2938 ];
2939
2940 compare(expected, tokens);
2941 }
2942
2943 #[test]
2944 fn tokenize_explain_select() {
2945 let sql = String::from("EXPLAIN SELECT * FROM customer WHERE id = 1");
2946 let dialect = GenericDialect {};
2947 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2948
2949 let expected = vec![
2950 Token::make_keyword("EXPLAIN"),
2951 Token::Whitespace(Whitespace::Space),
2952 Token::make_keyword("SELECT"),
2953 Token::Whitespace(Whitespace::Space),
2954 Token::Mul,
2955 Token::Whitespace(Whitespace::Space),
2956 Token::make_keyword("FROM"),
2957 Token::Whitespace(Whitespace::Space),
2958 Token::make_word("customer", None),
2959 Token::Whitespace(Whitespace::Space),
2960 Token::make_keyword("WHERE"),
2961 Token::Whitespace(Whitespace::Space),
2962 Token::make_word("id", None),
2963 Token::Whitespace(Whitespace::Space),
2964 Token::Eq,
2965 Token::Whitespace(Whitespace::Space),
2966 Token::Number(String::from("1"), false),
2967 ];
2968
2969 compare(expected, tokens);
2970 }
2971
2972 #[test]
2973 fn tokenize_explain_analyze_select() {
2974 let sql = String::from("EXPLAIN ANALYZE SELECT * FROM customer WHERE id = 1");
2975 let dialect = GenericDialect {};
2976 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2977
2978 let expected = vec![
2979 Token::make_keyword("EXPLAIN"),
2980 Token::Whitespace(Whitespace::Space),
2981 Token::make_keyword("ANALYZE"),
2982 Token::Whitespace(Whitespace::Space),
2983 Token::make_keyword("SELECT"),
2984 Token::Whitespace(Whitespace::Space),
2985 Token::Mul,
2986 Token::Whitespace(Whitespace::Space),
2987 Token::make_keyword("FROM"),
2988 Token::Whitespace(Whitespace::Space),
2989 Token::make_word("customer", None),
2990 Token::Whitespace(Whitespace::Space),
2991 Token::make_keyword("WHERE"),
2992 Token::Whitespace(Whitespace::Space),
2993 Token::make_word("id", None),
2994 Token::Whitespace(Whitespace::Space),
2995 Token::Eq,
2996 Token::Whitespace(Whitespace::Space),
2997 Token::Number(String::from("1"), false),
2998 ];
2999
3000 compare(expected, tokens);
3001 }
3002
3003 #[test]
3004 fn tokenize_string_predicate() {
3005 let sql = String::from("SELECT * FROM customer WHERE salary != 'Not Provided'");
3006 let dialect = GenericDialect {};
3007 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3008
3009 let expected = vec![
3010 Token::make_keyword("SELECT"),
3011 Token::Whitespace(Whitespace::Space),
3012 Token::Mul,
3013 Token::Whitespace(Whitespace::Space),
3014 Token::make_keyword("FROM"),
3015 Token::Whitespace(Whitespace::Space),
3016 Token::make_word("customer", None),
3017 Token::Whitespace(Whitespace::Space),
3018 Token::make_keyword("WHERE"),
3019 Token::Whitespace(Whitespace::Space),
3020 Token::make_word("salary", None),
3021 Token::Whitespace(Whitespace::Space),
3022 Token::Neq,
3023 Token::Whitespace(Whitespace::Space),
3024 Token::SingleQuotedString(String::from("Not Provided")),
3025 ];
3026
3027 compare(expected, tokens);
3028 }
3029
3030 #[test]
3031 fn tokenize_invalid_string() {
3032 let sql = String::from("\n💝مصطفىh");
3033
3034 let dialect = GenericDialect {};
3035 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3036 let expected = vec![
3038 Token::Whitespace(Whitespace::Newline),
3039 Token::Char('💝'),
3040 Token::make_word("مصطفىh", None),
3041 ];
3042 compare(expected, tokens);
3043 }
3044
3045 #[test]
3046 fn tokenize_newline_in_string_literal() {
3047 let sql = String::from("'foo\r\nbar\nbaz'");
3048
3049 let dialect = GenericDialect {};
3050 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3051 let expected = vec![Token::SingleQuotedString("foo\r\nbar\nbaz".to_string())];
3052 compare(expected, tokens);
3053 }
3054
3055 #[test]
3056 fn tokenize_unterminated_string_literal() {
3057 let sql = String::from("select 'foo");
3058
3059 let dialect = GenericDialect {};
3060 let mut tokenizer = Tokenizer::new(&dialect, &sql);
3061 assert_eq!(
3062 tokenizer.tokenize(),
3063 Err(TokenizerError {
3064 message: "Unterminated string literal".to_string(),
3065 location: Location { line: 1, column: 8 },
3066 })
3067 );
3068 }
3069
3070 #[test]
3071 fn tokenize_unterminated_string_literal_utf8() {
3072 let sql = String::from("SELECT \"なにか\" FROM Y WHERE \"なにか\" = 'test;");
3073
3074 let dialect = GenericDialect {};
3075 let mut tokenizer = Tokenizer::new(&dialect, &sql);
3076 assert_eq!(
3077 tokenizer.tokenize(),
3078 Err(TokenizerError {
3079 message: "Unterminated string literal".to_string(),
3080 location: Location {
3081 line: 1,
3082 column: 35
3083 }
3084 })
3085 );
3086 }
3087
3088 #[test]
3089 fn tokenize_invalid_string_cols() {
3090 let sql = String::from("\n\nSELECT * FROM table\t💝مصطفىh");
3091
3092 let dialect = GenericDialect {};
3093 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3094 let expected = vec![
3096 Token::Whitespace(Whitespace::Newline),
3097 Token::Whitespace(Whitespace::Newline),
3098 Token::make_keyword("SELECT"),
3099 Token::Whitespace(Whitespace::Space),
3100 Token::Mul,
3101 Token::Whitespace(Whitespace::Space),
3102 Token::make_keyword("FROM"),
3103 Token::Whitespace(Whitespace::Space),
3104 Token::make_keyword("table"),
3105 Token::Whitespace(Whitespace::Tab),
3106 Token::Char('💝'),
3107 Token::make_word("مصطفىh", None),
3108 ];
3109 compare(expected, tokens);
3110 }
3111
3112 #[test]
3113 fn tokenize_dollar_quoted_string_tagged() {
3114 let test_cases = vec![
3115 (
3116 String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$tag$"),
3117 vec![
3118 Token::make_keyword("SELECT"),
3119 Token::Whitespace(Whitespace::Space),
3120 Token::DollarQuotedString(DollarQuotedString {
3121 value: "dollar '$' quoted strings have $tags like this$ or like this $$".into(),
3122 tag: Some("tag".into()),
3123 })
3124 ]
3125 ),
3126 (
3127 String::from("SELECT $abc$x$ab$abc$"),
3128 vec![
3129 Token::make_keyword("SELECT"),
3130 Token::Whitespace(Whitespace::Space),
3131 Token::DollarQuotedString(DollarQuotedString {
3132 value: "x$ab".into(),
3133 tag: Some("abc".into()),
3134 })
3135 ]
3136 ),
3137 (
3138 String::from("SELECT $abc$$abc$"),
3139 vec![
3140 Token::make_keyword("SELECT"),
3141 Token::Whitespace(Whitespace::Space),
3142 Token::DollarQuotedString(DollarQuotedString {
3143 value: "".into(),
3144 tag: Some("abc".into()),
3145 })
3146 ]
3147 ),
3148 (
3149 String::from("0$abc$$abc$1"),
3150 vec![
3151 Token::Number("0".into(), false),
3152 Token::DollarQuotedString(DollarQuotedString {
3153 value: "".into(),
3154 tag: Some("abc".into()),
3155 }),
3156 Token::Number("1".into(), false),
3157 ]
3158 ),
3159 (
3160 String::from("$function$abc$q$data$q$$function$"),
3161 vec![
3162 Token::DollarQuotedString(DollarQuotedString {
3163 value: "abc$q$data$q$".into(),
3164 tag: Some("function".into()),
3165 }),
3166 ]
3167 ),
3168 ];
3169
3170 let dialect = GenericDialect {};
3171 for (sql, expected) in test_cases {
3172 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3173 compare(expected, tokens);
3174 }
3175 }
3176
3177 #[test]
3178 fn tokenize_dollar_quoted_string_tagged_unterminated() {
3179 let sql = String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$different tag$");
3180 let dialect = GenericDialect {};
3181 assert_eq!(
3182 Tokenizer::new(&dialect, &sql).tokenize(),
3183 Err(TokenizerError {
3184 message: "Unterminated dollar-quoted, expected $".into(),
3185 location: Location {
3186 line: 1,
3187 column: 91
3188 }
3189 })
3190 );
3191 }
3192
3193 #[test]
3194 fn tokenize_dollar_quoted_string_tagged_unterminated_mirror() {
3195 let sql = String::from("SELECT $abc$abc$");
3196 let dialect = GenericDialect {};
3197 assert_eq!(
3198 Tokenizer::new(&dialect, &sql).tokenize(),
3199 Err(TokenizerError {
3200 message: "Unterminated dollar-quoted, expected $".into(),
3201 location: Location {
3202 line: 1,
3203 column: 17
3204 }
3205 })
3206 );
3207 }
3208
3209 #[test]
3210 fn tokenize_dollar_placeholder() {
3211 let sql = String::from("SELECT $$, $$ABC$$, $ABC$, $ABC");
3212 let dialect = SQLiteDialect {};
3213 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3214 assert_eq!(
3215 tokens,
3216 vec![
3217 Token::make_keyword("SELECT"),
3218 Token::Whitespace(Whitespace::Space),
3219 Token::Placeholder("$$".into()),
3220 Token::Comma,
3221 Token::Whitespace(Whitespace::Space),
3222 Token::Placeholder("$$ABC$$".into()),
3223 Token::Comma,
3224 Token::Whitespace(Whitespace::Space),
3225 Token::Placeholder("$ABC$".into()),
3226 Token::Comma,
3227 Token::Whitespace(Whitespace::Space),
3228 Token::Placeholder("$ABC".into()),
3229 ]
3230 );
3231 }
3232
3233 #[test]
3234 fn tokenize_nested_dollar_quoted_strings() {
3235 let sql = String::from("SELECT $tag$dollar $nested$ string$tag$");
3236 let dialect = GenericDialect {};
3237 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3238 let expected = vec![
3239 Token::make_keyword("SELECT"),
3240 Token::Whitespace(Whitespace::Space),
3241 Token::DollarQuotedString(DollarQuotedString {
3242 value: "dollar $nested$ string".into(),
3243 tag: Some("tag".into()),
3244 }),
3245 ];
3246 compare(expected, tokens);
3247 }
3248
3249 #[test]
3250 fn tokenize_dollar_quoted_string_untagged_empty() {
3251 let sql = String::from("SELECT $$$$");
3252 let dialect = GenericDialect {};
3253 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3254 let expected = vec![
3255 Token::make_keyword("SELECT"),
3256 Token::Whitespace(Whitespace::Space),
3257 Token::DollarQuotedString(DollarQuotedString {
3258 value: "".into(),
3259 tag: None,
3260 }),
3261 ];
3262 compare(expected, tokens);
3263 }
3264
3265 #[test]
3266 fn tokenize_dollar_quoted_string_untagged() {
3267 let sql =
3268 String::from("SELECT $$within dollar '$' quoted strings have $tags like this$ $$");
3269 let dialect = GenericDialect {};
3270 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3271 let expected = vec![
3272 Token::make_keyword("SELECT"),
3273 Token::Whitespace(Whitespace::Space),
3274 Token::DollarQuotedString(DollarQuotedString {
3275 value: "within dollar '$' quoted strings have $tags like this$ ".into(),
3276 tag: None,
3277 }),
3278 ];
3279 compare(expected, tokens);
3280 }
3281
3282 #[test]
3283 fn tokenize_dollar_quoted_string_untagged_unterminated() {
3284 let sql = String::from(
3285 "SELECT $$dollar '$' quoted strings have $tags like this$ or like this $different tag$",
3286 );
3287 let dialect = GenericDialect {};
3288 assert_eq!(
3289 Tokenizer::new(&dialect, &sql).tokenize(),
3290 Err(TokenizerError {
3291 message: "Unterminated dollar-quoted string".into(),
3292 location: Location {
3293 line: 1,
3294 column: 86
3295 }
3296 })
3297 );
3298 }
3299
3300 #[test]
3301 fn tokenize_right_arrow() {
3302 let sql = String::from("FUNCTION(key=>value)");
3303 let dialect = GenericDialect {};
3304 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3305 let expected = vec![
3306 Token::make_word("FUNCTION", None),
3307 Token::LParen,
3308 Token::make_word("key", None),
3309 Token::RArrow,
3310 Token::make_word("value", None),
3311 Token::RParen,
3312 ];
3313 compare(expected, tokens);
3314 }
3315
3316 #[test]
3317 fn tokenize_is_null() {
3318 let sql = String::from("a IS NULL");
3319 let dialect = GenericDialect {};
3320 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3321
3322 let expected = vec![
3323 Token::make_word("a", None),
3324 Token::Whitespace(Whitespace::Space),
3325 Token::make_keyword("IS"),
3326 Token::Whitespace(Whitespace::Space),
3327 Token::make_keyword("NULL"),
3328 ];
3329
3330 compare(expected, tokens);
3331 }
3332
3333 #[test]
3334 fn tokenize_comment() {
3335 let test_cases = vec![
3336 (
3337 String::from("0--this is a comment\n1"),
3338 vec![
3339 Token::Number("0".to_string(), false),
3340 Token::Whitespace(Whitespace::SingleLineComment {
3341 prefix: "--".to_string(),
3342 comment: "this is a comment".to_string(),
3343 }),
3344 Token::Whitespace(Whitespace::Newline),
3345 Token::Number("1".to_string(), false),
3346 ],
3347 ),
3348 (
3349 String::from("0--this is a comment\r1"),
3350 vec![
3351 Token::Number("0".to_string(), false),
3352 Token::Whitespace(Whitespace::SingleLineComment {
3353 prefix: "--".to_string(),
3354 comment: "this is a comment\r1".to_string(),
3355 }),
3356 ],
3357 ),
3358 (
3359 String::from("0--this is a comment\r\n1"),
3360 vec![
3361 Token::Number("0".to_string(), false),
3362 Token::Whitespace(Whitespace::SingleLineComment {
3363 prefix: "--".to_string(),
3364 comment: "this is a comment\r".to_string(),
3365 }),
3366 Token::Whitespace(Whitespace::Newline),
3367 Token::Number("1".to_string(), false),
3368 ],
3369 ),
3370 ];
3371
3372 let dialect = GenericDialect {};
3373
3374 for (sql, expected) in test_cases {
3375 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3376 compare(expected, tokens);
3377 }
3378 }
3379
3380 #[test]
3381 fn tokenize_comment_postgres() {
3382 let sql = String::from("1--\r0");
3383
3384 let dialect = PostgreSqlDialect {};
3385 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3386 let expected = vec![
3387 Token::Number("1".to_string(), false),
3388 Token::Whitespace(Whitespace::SingleLineComment {
3389 prefix: "--".to_string(),
3390 comment: "".to_string(),
3391 }),
3392 Token::Whitespace(Whitespace::Newline), Token::Number("0".to_string(), false),
3394 ];
3395 compare(expected, tokens);
3396 }
3397
3398 #[test]
3399 fn tokenize_comment_at_eof() {
3400 let sql = String::from("--this is a comment");
3401
3402 let dialect = GenericDialect {};
3403 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3404 let expected = vec![Token::Whitespace(Whitespace::SingleLineComment {
3405 prefix: "--".to_string(),
3406 comment: "this is a comment".to_string(),
3407 })];
3408 compare(expected, tokens);
3409 }
3410
3411 #[test]
3412 fn tokenize_multiline_comment() {
3413 let sql = String::from("0/*multi-line\n* /comment*/1");
3414
3415 let dialect = GenericDialect {};
3416 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3417 let expected = vec![
3418 Token::Number("0".to_string(), false),
3419 Token::Whitespace(Whitespace::MultiLineComment(
3420 "multi-line\n* /comment".to_string(),
3421 )),
3422 Token::Number("1".to_string(), false),
3423 ];
3424 compare(expected, tokens);
3425 }
3426
3427 #[test]
3428 fn tokenize_nested_multiline_comment() {
3429 all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3430 "0/*multi-line\n* \n/* comment \n /*comment*/*/ */ /comment*/1",
3431 vec![
3432 Token::Number("0".to_string(), false),
3433 Token::Whitespace(Whitespace::MultiLineComment(
3434 "multi-line\n* \n/* comment \n /*comment*/*/ ".into(),
3435 )),
3436 Token::Whitespace(Whitespace::Space),
3437 Token::Div,
3438 Token::Word(Word {
3439 value: "comment".to_string(),
3440 quote_style: None,
3441 keyword: Keyword::COMMENT,
3442 }),
3443 Token::Mul,
3444 Token::Div,
3445 Token::Number("1".to_string(), false),
3446 ],
3447 );
3448
3449 all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3450 "0/*multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/*/1",
3451 vec![
3452 Token::Number("0".to_string(), false),
3453 Token::Whitespace(Whitespace::MultiLineComment(
3454 "multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/".into(),
3455 )),
3456 Token::Number("1".to_string(), false),
3457 ],
3458 );
3459
3460 all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3461 "SELECT 1/* a /* b */ c */0",
3462 vec![
3463 Token::make_keyword("SELECT"),
3464 Token::Whitespace(Whitespace::Space),
3465 Token::Number("1".to_string(), false),
3466 Token::Whitespace(Whitespace::MultiLineComment(" a /* b */ c ".to_string())),
3467 Token::Number("0".to_string(), false),
3468 ],
3469 );
3470 }
3471
3472 #[test]
3473 fn tokenize_nested_multiline_comment_empty() {
3474 all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3475 "select 1/*/**/*/0",
3476 vec![
3477 Token::make_keyword("select"),
3478 Token::Whitespace(Whitespace::Space),
3479 Token::Number("1".to_string(), false),
3480 Token::Whitespace(Whitespace::MultiLineComment("/**/".to_string())),
3481 Token::Number("0".to_string(), false),
3482 ],
3483 );
3484 }
3485
3486 #[test]
3487 fn tokenize_nested_comments_if_not_supported() {
3488 all_dialects_except(|d| d.supports_nested_comments()).tokenizes_to(
3489 "SELECT 1/*/* nested comment */*/0",
3490 vec![
3491 Token::make_keyword("SELECT"),
3492 Token::Whitespace(Whitespace::Space),
3493 Token::Number("1".to_string(), false),
3494 Token::Whitespace(Whitespace::MultiLineComment(
3495 "/* nested comment ".to_string(),
3496 )),
3497 Token::Mul,
3498 Token::Div,
3499 Token::Number("0".to_string(), false),
3500 ],
3501 );
3502 }
3503
3504 #[test]
3505 fn tokenize_multiline_comment_with_even_asterisks() {
3506 let sql = String::from("\n/** Comment **/\n");
3507
3508 let dialect = GenericDialect {};
3509 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3510 let expected = vec![
3511 Token::Whitespace(Whitespace::Newline),
3512 Token::Whitespace(Whitespace::MultiLineComment("* Comment *".to_string())),
3513 Token::Whitespace(Whitespace::Newline),
3514 ];
3515 compare(expected, tokens);
3516 }
3517
3518 #[test]
3519 fn tokenize_unicode_whitespace() {
3520 let sql = String::from(" \u{2003}\n");
3521
3522 let dialect = GenericDialect {};
3523 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3524 let expected = vec![
3525 Token::Whitespace(Whitespace::Space),
3526 Token::Whitespace(Whitespace::Space),
3527 Token::Whitespace(Whitespace::Newline),
3528 ];
3529 compare(expected, tokens);
3530 }
3531
3532 #[test]
3533 fn tokenize_mismatched_quotes() {
3534 let sql = String::from("\"foo");
3535
3536 let dialect = GenericDialect {};
3537 let mut tokenizer = Tokenizer::new(&dialect, &sql);
3538 assert_eq!(
3539 tokenizer.tokenize(),
3540 Err(TokenizerError {
3541 message: "Expected close delimiter '\"' before EOF.".to_string(),
3542 location: Location { line: 1, column: 1 },
3543 })
3544 );
3545 }
3546
3547 #[test]
3548 fn tokenize_newlines() {
3549 let sql = String::from("line1\nline2\rline3\r\nline4\r");
3550
3551 let dialect = GenericDialect {};
3552 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3553 let expected = vec![
3554 Token::make_word("line1", None),
3555 Token::Whitespace(Whitespace::Newline),
3556 Token::make_word("line2", None),
3557 Token::Whitespace(Whitespace::Newline),
3558 Token::make_word("line3", None),
3559 Token::Whitespace(Whitespace::Newline),
3560 Token::make_word("line4", None),
3561 Token::Whitespace(Whitespace::Newline),
3562 ];
3563 compare(expected, tokens);
3564 }
3565
3566 #[test]
3567 fn tokenize_mssql_top() {
3568 let sql = "SELECT TOP 5 [bar] FROM foo";
3569 let dialect = MsSqlDialect {};
3570 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3571 let expected = vec![
3572 Token::make_keyword("SELECT"),
3573 Token::Whitespace(Whitespace::Space),
3574 Token::make_keyword("TOP"),
3575 Token::Whitespace(Whitespace::Space),
3576 Token::Number(String::from("5"), false),
3577 Token::Whitespace(Whitespace::Space),
3578 Token::make_word("bar", Some('[')),
3579 Token::Whitespace(Whitespace::Space),
3580 Token::make_keyword("FROM"),
3581 Token::Whitespace(Whitespace::Space),
3582 Token::make_word("foo", None),
3583 ];
3584 compare(expected, tokens);
3585 }
3586
3587 #[test]
3588 fn tokenize_pg_regex_match() {
3589 let sql = "SELECT col ~ '^a', col ~* '^a', col !~ '^a', col !~* '^a'";
3590 let dialect = GenericDialect {};
3591 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3592 let expected = vec![
3593 Token::make_keyword("SELECT"),
3594 Token::Whitespace(Whitespace::Space),
3595 Token::make_word("col", None),
3596 Token::Whitespace(Whitespace::Space),
3597 Token::Tilde,
3598 Token::Whitespace(Whitespace::Space),
3599 Token::SingleQuotedString("^a".into()),
3600 Token::Comma,
3601 Token::Whitespace(Whitespace::Space),
3602 Token::make_word("col", None),
3603 Token::Whitespace(Whitespace::Space),
3604 Token::TildeAsterisk,
3605 Token::Whitespace(Whitespace::Space),
3606 Token::SingleQuotedString("^a".into()),
3607 Token::Comma,
3608 Token::Whitespace(Whitespace::Space),
3609 Token::make_word("col", None),
3610 Token::Whitespace(Whitespace::Space),
3611 Token::ExclamationMarkTilde,
3612 Token::Whitespace(Whitespace::Space),
3613 Token::SingleQuotedString("^a".into()),
3614 Token::Comma,
3615 Token::Whitespace(Whitespace::Space),
3616 Token::make_word("col", None),
3617 Token::Whitespace(Whitespace::Space),
3618 Token::ExclamationMarkTildeAsterisk,
3619 Token::Whitespace(Whitespace::Space),
3620 Token::SingleQuotedString("^a".into()),
3621 ];
3622 compare(expected, tokens);
3623 }
3624
3625 #[test]
3626 fn tokenize_pg_like_match() {
3627 let sql = "SELECT col ~~ '_a%', col ~~* '_a%', col !~~ '_a%', col !~~* '_a%'";
3628 let dialect = GenericDialect {};
3629 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3630 let expected = vec![
3631 Token::make_keyword("SELECT"),
3632 Token::Whitespace(Whitespace::Space),
3633 Token::make_word("col", None),
3634 Token::Whitespace(Whitespace::Space),
3635 Token::DoubleTilde,
3636 Token::Whitespace(Whitespace::Space),
3637 Token::SingleQuotedString("_a%".into()),
3638 Token::Comma,
3639 Token::Whitespace(Whitespace::Space),
3640 Token::make_word("col", None),
3641 Token::Whitespace(Whitespace::Space),
3642 Token::DoubleTildeAsterisk,
3643 Token::Whitespace(Whitespace::Space),
3644 Token::SingleQuotedString("_a%".into()),
3645 Token::Comma,
3646 Token::Whitespace(Whitespace::Space),
3647 Token::make_word("col", None),
3648 Token::Whitespace(Whitespace::Space),
3649 Token::ExclamationMarkDoubleTilde,
3650 Token::Whitespace(Whitespace::Space),
3651 Token::SingleQuotedString("_a%".into()),
3652 Token::Comma,
3653 Token::Whitespace(Whitespace::Space),
3654 Token::make_word("col", None),
3655 Token::Whitespace(Whitespace::Space),
3656 Token::ExclamationMarkDoubleTildeAsterisk,
3657 Token::Whitespace(Whitespace::Space),
3658 Token::SingleQuotedString("_a%".into()),
3659 ];
3660 compare(expected, tokens);
3661 }
3662
3663 #[test]
3664 fn tokenize_quoted_identifier() {
3665 let sql = r#" "a "" b" "a """ "c """"" "#;
3666 let dialect = GenericDialect {};
3667 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3668 let expected = vec![
3669 Token::Whitespace(Whitespace::Space),
3670 Token::make_word(r#"a " b"#, Some('"')),
3671 Token::Whitespace(Whitespace::Space),
3672 Token::make_word(r#"a ""#, Some('"')),
3673 Token::Whitespace(Whitespace::Space),
3674 Token::make_word(r#"c """#, Some('"')),
3675 Token::Whitespace(Whitespace::Space),
3676 ];
3677 compare(expected, tokens);
3678 }
3679
3680 #[test]
3681 fn tokenize_snowflake_div() {
3682 let sql = r#"field/1000"#;
3683 let dialect = SnowflakeDialect {};
3684 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3685 let expected = vec![
3686 Token::make_word(r#"field"#, None),
3687 Token::Div,
3688 Token::Number("1000".to_string(), false),
3689 ];
3690 compare(expected, tokens);
3691 }
3692
3693 #[test]
3694 fn tokenize_quoted_identifier_with_no_escape() {
3695 let sql = r#" "a "" b" "a """ "c """"" "#;
3696 let dialect = GenericDialect {};
3697 let tokens = Tokenizer::new(&dialect, sql)
3698 .with_unescape(false)
3699 .tokenize()
3700 .unwrap();
3701 let expected = vec![
3702 Token::Whitespace(Whitespace::Space),
3703 Token::make_word(r#"a "" b"#, Some('"')),
3704 Token::Whitespace(Whitespace::Space),
3705 Token::make_word(r#"a """#, Some('"')),
3706 Token::Whitespace(Whitespace::Space),
3707 Token::make_word(r#"c """""#, Some('"')),
3708 Token::Whitespace(Whitespace::Space),
3709 ];
3710 compare(expected, tokens);
3711 }
3712
3713 #[test]
3714 fn tokenize_with_location() {
3715 let sql = "SELECT a,\n b";
3716 let dialect = GenericDialect {};
3717 let tokens = Tokenizer::new(&dialect, sql)
3718 .tokenize_with_location()
3719 .unwrap();
3720 let expected = vec![
3721 TokenWithSpan::at(Token::make_keyword("SELECT"), (1, 1).into(), (1, 7).into()),
3722 TokenWithSpan::at(
3723 Token::Whitespace(Whitespace::Space),
3724 (1, 7).into(),
3725 (1, 8).into(),
3726 ),
3727 TokenWithSpan::at(Token::make_word("a", None), (1, 8).into(), (1, 9).into()),
3728 TokenWithSpan::at(Token::Comma, (1, 9).into(), (1, 10).into()),
3729 TokenWithSpan::at(
3730 Token::Whitespace(Whitespace::Newline),
3731 (1, 10).into(),
3732 (2, 1).into(),
3733 ),
3734 TokenWithSpan::at(
3735 Token::Whitespace(Whitespace::Space),
3736 (2, 1).into(),
3737 (2, 2).into(),
3738 ),
3739 TokenWithSpan::at(Token::make_word("b", None), (2, 2).into(), (2, 3).into()),
3740 ];
3741 compare(expected, tokens);
3742 }
3743
3744 fn compare<T: PartialEq + fmt::Debug>(expected: Vec<T>, actual: Vec<T>) {
3745 assert_eq!(expected, actual);
3750 }
3751
3752 fn check_unescape(s: &str, expected: Option<&str>) {
3753 let s = format!("'{s}'");
3754 let mut state = State {
3755 peekable: s.chars().peekable(),
3756 line: 0,
3757 col: 0,
3758 };
3759
3760 assert_eq!(
3761 unescape_single_quoted_string(&mut state),
3762 expected.map(|s| s.to_string())
3763 );
3764 }
3765
3766 #[test]
3767 fn test_unescape() {
3768 check_unescape(r"\b", Some("\u{0008}"));
3769 check_unescape(r"\f", Some("\u{000C}"));
3770 check_unescape(r"\t", Some("\t"));
3771 check_unescape(r"\r\n", Some("\r\n"));
3772 check_unescape(r"\/", Some("/"));
3773 check_unescape(r"/", Some("/"));
3774 check_unescape(r"\\", Some("\\"));
3775
3776 check_unescape(r"\u0001", Some("\u{0001}"));
3778 check_unescape(r"\u4c91", Some("\u{4c91}"));
3779 check_unescape(r"\u4c916", Some("\u{4c91}6"));
3780 check_unescape(r"\u4c", None);
3781 check_unescape(r"\u0000", None);
3782 check_unescape(r"\U0010FFFF", Some("\u{10FFFF}"));
3783 check_unescape(r"\U00110000", None);
3784 check_unescape(r"\U00000000", None);
3785 check_unescape(r"\u", None);
3786 check_unescape(r"\U", None);
3787 check_unescape(r"\U1010FFFF", None);
3788
3789 check_unescape(r"\x4B", Some("\u{004b}"));
3791 check_unescape(r"\x4", Some("\u{0004}"));
3792 check_unescape(r"\x4L", Some("\u{0004}L"));
3793 check_unescape(r"\x", Some("x"));
3794 check_unescape(r"\xP", Some("xP"));
3795 check_unescape(r"\x0", None);
3796 check_unescape(r"\xCAD", None);
3797 check_unescape(r"\xA9", None);
3798
3799 check_unescape(r"\1", Some("\u{0001}"));
3801 check_unescape(r"\12", Some("\u{000a}"));
3802 check_unescape(r"\123", Some("\u{0053}"));
3803 check_unescape(r"\1232", Some("\u{0053}2"));
3804 check_unescape(r"\4", Some("\u{0004}"));
3805 check_unescape(r"\45", Some("\u{0025}"));
3806 check_unescape(r"\450", Some("\u{0028}"));
3807 check_unescape(r"\603", None);
3808 check_unescape(r"\0", None);
3809 check_unescape(r"\080", None);
3810
3811 check_unescape(r"\9", Some("9"));
3813 check_unescape(r"''", Some("'"));
3814 check_unescape(
3815 r"Hello\r\nRust/\u4c91 SQL Parser\U0010ABCD\1232",
3816 Some("Hello\r\nRust/\u{4c91} SQL Parser\u{10abcd}\u{0053}2"),
3817 );
3818 check_unescape(r"Hello\0", None);
3819 check_unescape(r"Hello\xCADRust", None);
3820 }
3821
3822 #[test]
3823 fn tokenize_numeric_prefix_trait() {
3824 #[derive(Debug)]
3825 struct NumericPrefixDialect;
3826
3827 impl Dialect for NumericPrefixDialect {
3828 fn is_identifier_start(&self, ch: char) -> bool {
3829 ch.is_ascii_lowercase()
3830 || ch.is_ascii_uppercase()
3831 || ch.is_ascii_digit()
3832 || ch == '$'
3833 }
3834
3835 fn is_identifier_part(&self, ch: char) -> bool {
3836 ch.is_ascii_lowercase()
3837 || ch.is_ascii_uppercase()
3838 || ch.is_ascii_digit()
3839 || ch == '_'
3840 || ch == '$'
3841 || ch == '{'
3842 || ch == '}'
3843 }
3844
3845 fn supports_numeric_prefix(&self) -> bool {
3846 true
3847 }
3848 }
3849
3850 tokenize_numeric_prefix_inner(&NumericPrefixDialect {});
3851 tokenize_numeric_prefix_inner(&HiveDialect {});
3852 tokenize_numeric_prefix_inner(&MySqlDialect {});
3853 }
3854
3855 fn tokenize_numeric_prefix_inner(dialect: &dyn Dialect) {
3856 let sql = r#"SELECT * FROM 1"#;
3857 let tokens = Tokenizer::new(dialect, sql).tokenize().unwrap();
3858 let expected = vec![
3859 Token::make_keyword("SELECT"),
3860 Token::Whitespace(Whitespace::Space),
3861 Token::Mul,
3862 Token::Whitespace(Whitespace::Space),
3863 Token::make_keyword("FROM"),
3864 Token::Whitespace(Whitespace::Space),
3865 Token::Number(String::from("1"), false),
3866 ];
3867 compare(expected, tokens);
3868 }
3869
3870 #[test]
3871 fn tokenize_quoted_string_escape() {
3872 let dialect = SnowflakeDialect {};
3873 for (sql, expected, expected_unescaped) in [
3874 (r#"'%a\'%b'"#, r#"%a\'%b"#, r#"%a'%b"#),
3875 (r#"'a\'\'b\'c\'d'"#, r#"a\'\'b\'c\'d"#, r#"a''b'c'd"#),
3876 (r#"'\\'"#, r#"\\"#, r#"\"#),
3877 (
3878 r#"'\0\a\b\f\n\r\t\Z'"#,
3879 r#"\0\a\b\f\n\r\t\Z"#,
3880 "\0\u{7}\u{8}\u{c}\n\r\t\u{1a}",
3881 ),
3882 (r#"'\"'"#, r#"\""#, "\""),
3883 (r#"'\\a\\b\'c'"#, r#"\\a\\b\'c"#, r#"\a\b'c"#),
3884 (r#"'\'abcd'"#, r#"\'abcd"#, r#"'abcd"#),
3885 (r#"'''a''b'"#, r#"''a''b"#, r#"'a'b"#),
3886 (r#"'\q'"#, r#"\q"#, r#"q"#),
3887 (r#"'\%\_'"#, r#"\%\_"#, r#"%_"#),
3888 (r#"'\\%\\_'"#, r#"\\%\\_"#, r#"\%\_"#),
3889 ] {
3890 let tokens = Tokenizer::new(&dialect, sql)
3891 .with_unescape(false)
3892 .tokenize()
3893 .unwrap();
3894 let expected = vec![Token::SingleQuotedString(expected.to_string())];
3895 compare(expected, tokens);
3896
3897 let tokens = Tokenizer::new(&dialect, sql)
3898 .with_unescape(true)
3899 .tokenize()
3900 .unwrap();
3901 let expected = vec![Token::SingleQuotedString(expected_unescaped.to_string())];
3902 compare(expected, tokens);
3903 }
3904
3905 for sql in [r#"'\'"#, r#"'ab\'"#] {
3906 let mut tokenizer = Tokenizer::new(&dialect, sql);
3907 assert_eq!(
3908 "Unterminated string literal",
3909 tokenizer.tokenize().unwrap_err().message.as_str(),
3910 );
3911 }
3912
3913 for (sql, expected) in [(r#"'\'"#, r#"\"#), (r#"'ab\'"#, r#"ab\"#)] {
3915 let dialect = GenericDialect {};
3916 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3917
3918 let expected = vec![Token::SingleQuotedString(expected.to_string())];
3919
3920 compare(expected, tokens);
3921 }
3922
3923 for (sql, expected) in [(r#"'\%'"#, r#"\%"#), (r#"'\_'"#, r#"\_"#)] {
3925 let dialect = MySqlDialect {};
3926 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3927
3928 let expected = vec![Token::SingleQuotedString(expected.to_string())];
3929
3930 compare(expected, tokens);
3931 }
3932 }
3933
3934 #[test]
3935 fn tokenize_triple_quoted_string() {
3936 fn check<F>(
3937 q: char, r: char, quote_token: F,
3940 ) where
3941 F: Fn(String) -> Token,
3942 {
3943 let dialect = BigQueryDialect {};
3944
3945 for (sql, expected, expected_unescaped) in [
3946 (format!(r#"{q}{q}{q}{q}{q}{q}"#), "".into(), "".into()),
3948 (
3950 format!(r#"{q}{q}{q}ab{q}{q}\{q}{q}cd{q}{q}{q}"#),
3951 format!(r#"ab{q}{q}\{q}{q}cd"#),
3952 format!(r#"ab{q}{q}{q}{q}cd"#),
3953 ),
3954 (
3956 format!(r#"{q}{q}{q}abc{q}{q}{q}"#),
3957 "abc".into(),
3958 "abc".into(),
3959 ),
3960 (
3962 format!(r#"{q}{q}{q}ab{r}{r}{r}c{r}def{r}{r}{r}{q}{q}{q}"#),
3963 format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
3964 format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
3965 ),
3966 (
3968 format!(r#"{q}{q}{q}ab{q}{q}c{q}{q}\{q}de{q}{q}f{q}{q}{q}"#),
3969 format!(r#"ab{q}{q}c{q}{q}\{q}de{q}{q}f"#),
3970 format!(r#"ab{q}{q}c{q}{q}{q}de{q}{q}f"#),
3971 ),
3972 (
3974 format!(r#"{q}{q}{q}a\'\'b\'c\'d{q}{q}{q}"#),
3975 r#"a\'\'b\'c\'d"#.into(),
3976 r#"a''b'c'd"#.into(),
3977 ),
3978 (
3980 format!(r#"{q}{q}{q}abc\0\n\rdef{q}{q}{q}"#),
3981 r#"abc\0\n\rdef"#.into(),
3982 "abc\0\n\rdef".into(),
3983 ),
3984 ] {
3985 let tokens = Tokenizer::new(&dialect, sql.as_str())
3986 .with_unescape(false)
3987 .tokenize()
3988 .unwrap();
3989 let expected = vec![quote_token(expected.to_string())];
3990 compare(expected, tokens);
3991
3992 let tokens = Tokenizer::new(&dialect, sql.as_str())
3993 .with_unescape(true)
3994 .tokenize()
3995 .unwrap();
3996 let expected = vec![quote_token(expected_unescaped.to_string())];
3997 compare(expected, tokens);
3998 }
3999
4000 for sql in [
4001 format!(r#"{q}{q}{q}{q}{q}\{q}"#),
4002 format!(r#"{q}{q}{q}abc{q}{q}\{q}"#),
4003 format!(r#"{q}{q}{q}{q}"#),
4004 format!(r#"{q}{q}{q}{r}{r}"#),
4005 format!(r#"{q}{q}{q}abc{q}"#),
4006 format!(r#"{q}{q}{q}abc{q}{q}"#),
4007 format!(r#"{q}{q}{q}abc"#),
4008 ] {
4009 let dialect = BigQueryDialect {};
4010 let mut tokenizer = Tokenizer::new(&dialect, sql.as_str());
4011 assert_eq!(
4012 "Unterminated string literal",
4013 tokenizer.tokenize().unwrap_err().message.as_str(),
4014 );
4015 }
4016 }
4017
4018 check('"', '\'', Token::TripleDoubleQuotedString);
4019
4020 check('\'', '"', Token::TripleSingleQuotedString);
4021
4022 let dialect = BigQueryDialect {};
4023
4024 let sql = r#"""''"#;
4025 let tokens = Tokenizer::new(&dialect, sql)
4026 .with_unescape(true)
4027 .tokenize()
4028 .unwrap();
4029 let expected = vec![
4030 Token::DoubleQuotedString("".to_string()),
4031 Token::SingleQuotedString("".to_string()),
4032 ];
4033 compare(expected, tokens);
4034
4035 let sql = r#"''"""#;
4036 let tokens = Tokenizer::new(&dialect, sql)
4037 .with_unescape(true)
4038 .tokenize()
4039 .unwrap();
4040 let expected = vec![
4041 Token::SingleQuotedString("".to_string()),
4042 Token::DoubleQuotedString("".to_string()),
4043 ];
4044 compare(expected, tokens);
4045
4046 let dialect = SnowflakeDialect {};
4048 let sql = r#"''''''"#;
4049 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4050 let expected = vec![Token::SingleQuotedString("''".to_string())];
4051 compare(expected, tokens);
4052 }
4053
4054 #[test]
4055 fn test_mysql_users_grantees() {
4056 let dialect = MySqlDialect {};
4057
4058 let sql = "CREATE USER `root`@`%`";
4059 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4060 let expected = vec![
4061 Token::make_keyword("CREATE"),
4062 Token::Whitespace(Whitespace::Space),
4063 Token::make_keyword("USER"),
4064 Token::Whitespace(Whitespace::Space),
4065 Token::make_word("root", Some('`')),
4066 Token::AtSign,
4067 Token::make_word("%", Some('`')),
4068 ];
4069 compare(expected, tokens);
4070 }
4071
4072 #[test]
4073 fn test_postgres_abs_without_space_and_string_literal() {
4074 let dialect = MySqlDialect {};
4075
4076 let sql = "SELECT @'1'";
4077 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4078 let expected = vec![
4079 Token::make_keyword("SELECT"),
4080 Token::Whitespace(Whitespace::Space),
4081 Token::AtSign,
4082 Token::SingleQuotedString("1".to_string()),
4083 ];
4084 compare(expected, tokens);
4085 }
4086
4087 #[test]
4088 fn test_postgres_abs_without_space_and_quoted_column() {
4089 let dialect = MySqlDialect {};
4090
4091 let sql = r#"SELECT @"bar" FROM foo"#;
4092 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4093 let expected = vec![
4094 Token::make_keyword("SELECT"),
4095 Token::Whitespace(Whitespace::Space),
4096 Token::AtSign,
4097 Token::DoubleQuotedString("bar".to_string()),
4098 Token::Whitespace(Whitespace::Space),
4099 Token::make_keyword("FROM"),
4100 Token::Whitespace(Whitespace::Space),
4101 Token::make_word("foo", None),
4102 ];
4103 compare(expected, tokens);
4104 }
4105
4106 #[test]
4107 fn test_national_strings_backslash_escape_not_supported() {
4108 all_dialects_where(|dialect| !dialect.supports_string_literal_backslash_escape())
4109 .tokenizes_to(
4110 "select n'''''\\'",
4111 vec![
4112 Token::make_keyword("select"),
4113 Token::Whitespace(Whitespace::Space),
4114 Token::NationalStringLiteral("''\\".to_string()),
4115 ],
4116 );
4117 }
4118
4119 #[test]
4120 fn test_national_strings_backslash_escape_supported() {
4121 all_dialects_where(|dialect| dialect.supports_string_literal_backslash_escape())
4122 .tokenizes_to(
4123 "select n'''''\\''",
4124 vec![
4125 Token::make_keyword("select"),
4126 Token::Whitespace(Whitespace::Space),
4127 Token::NationalStringLiteral("'''".to_string()),
4128 ],
4129 );
4130 }
4131
4132 #[test]
4133 fn test_string_escape_constant_not_supported() {
4134 all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
4135 "select e'...'",
4136 vec![
4137 Token::make_keyword("select"),
4138 Token::Whitespace(Whitespace::Space),
4139 Token::make_word("e", None),
4140 Token::SingleQuotedString("...".to_string()),
4141 ],
4142 );
4143
4144 all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
4145 "select E'...'",
4146 vec![
4147 Token::make_keyword("select"),
4148 Token::Whitespace(Whitespace::Space),
4149 Token::make_word("E", None),
4150 Token::SingleQuotedString("...".to_string()),
4151 ],
4152 );
4153 }
4154
4155 #[test]
4156 fn test_string_escape_constant_supported() {
4157 all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
4158 "select e'\\''",
4159 vec![
4160 Token::make_keyword("select"),
4161 Token::Whitespace(Whitespace::Space),
4162 Token::EscapedStringLiteral("'".to_string()),
4163 ],
4164 );
4165
4166 all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
4167 "select E'\\''",
4168 vec![
4169 Token::make_keyword("select"),
4170 Token::Whitespace(Whitespace::Space),
4171 Token::EscapedStringLiteral("'".to_string()),
4172 ],
4173 );
4174 }
4175
4176 #[test]
4177 fn test_whitespace_required_after_single_line_comment() {
4178 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4179 .tokenizes_to(
4180 "SELECT --'abc'",
4181 vec![
4182 Token::make_keyword("SELECT"),
4183 Token::Whitespace(Whitespace::Space),
4184 Token::Minus,
4185 Token::Minus,
4186 Token::SingleQuotedString("abc".to_string()),
4187 ],
4188 );
4189
4190 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4191 .tokenizes_to(
4192 "SELECT -- 'abc'",
4193 vec![
4194 Token::make_keyword("SELECT"),
4195 Token::Whitespace(Whitespace::Space),
4196 Token::Whitespace(Whitespace::SingleLineComment {
4197 prefix: "--".to_string(),
4198 comment: " 'abc'".to_string(),
4199 }),
4200 ],
4201 );
4202
4203 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4204 .tokenizes_to(
4205 "SELECT --",
4206 vec![
4207 Token::make_keyword("SELECT"),
4208 Token::Whitespace(Whitespace::Space),
4209 Token::Minus,
4210 Token::Minus,
4211 ],
4212 );
4213
4214 all_dialects_where(|d| d.requires_single_line_comment_whitespace()).tokenizes_to(
4215 "--\n-- Table structure for table...\n--\n",
4216 vec![
4217 Token::Whitespace(Whitespace::SingleLineComment {
4218 prefix: "--".to_string(),
4219 comment: "".to_string(),
4220 }),
4221 Token::Whitespace(Whitespace::Newline),
4222 Token::Whitespace(Whitespace::SingleLineComment {
4223 prefix: "--".to_string(),
4224 comment: " Table structure for table...".to_string(),
4225 }),
4226 Token::Whitespace(Whitespace::Newline),
4227 Token::Whitespace(Whitespace::SingleLineComment {
4228 prefix: "--".to_string(),
4229 comment: "".to_string(),
4230 }),
4231 Token::Whitespace(Whitespace::Newline),
4232 ],
4233 );
4234 }
4235
4236 #[test]
4237 fn test_whitespace_not_required_after_single_line_comment() {
4238 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4239 .tokenizes_to(
4240 "SELECT --'abc'",
4241 vec![
4242 Token::make_keyword("SELECT"),
4243 Token::Whitespace(Whitespace::Space),
4244 Token::Whitespace(Whitespace::SingleLineComment {
4245 prefix: "--".to_string(),
4246 comment: "'abc'".to_string(),
4247 }),
4248 ],
4249 );
4250
4251 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4252 .tokenizes_to(
4253 "SELECT -- 'abc'",
4254 vec![
4255 Token::make_keyword("SELECT"),
4256 Token::Whitespace(Whitespace::Space),
4257 Token::Whitespace(Whitespace::SingleLineComment {
4258 prefix: "--".to_string(),
4259 comment: " 'abc'".to_string(),
4260 }),
4261 ],
4262 );
4263
4264 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4265 .tokenizes_to(
4266 "SELECT --",
4267 vec![
4268 Token::make_keyword("SELECT"),
4269 Token::Whitespace(Whitespace::Space),
4270 Token::Whitespace(Whitespace::SingleLineComment {
4271 prefix: "--".to_string(),
4272 comment: "".to_string(),
4273 }),
4274 ],
4275 );
4276 }
4277
4278 #[test]
4279 fn test_tokenize_identifiers_numeric_prefix() {
4280 all_dialects_where(|dialect| dialect.supports_numeric_prefix())
4281 .tokenizes_to("123abc", vec![Token::make_word("123abc", None)]);
4282
4283 all_dialects_where(|dialect| dialect.supports_numeric_prefix())
4284 .tokenizes_to("12e34", vec![Token::Number("12e34".to_string(), false)]);
4285
4286 all_dialects_where(|dialect| dialect.supports_numeric_prefix()).tokenizes_to(
4287 "t.12e34",
4288 vec![
4289 Token::make_word("t", None),
4290 Token::Period,
4291 Token::make_word("12e34", None),
4292 ],
4293 );
4294
4295 all_dialects_where(|dialect| dialect.supports_numeric_prefix()).tokenizes_to(
4296 "t.1two3",
4297 vec![
4298 Token::make_word("t", None),
4299 Token::Period,
4300 Token::make_word("1two3", None),
4301 ],
4302 );
4303 }
4304
4305 #[test]
4306 fn tokenize_period_underscore() {
4307 let sql = String::from("SELECT table._col");
4308 let dialect = PostgreSqlDialect {};
4310 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
4311
4312 let expected = vec![
4313 Token::make_keyword("SELECT"),
4314 Token::Whitespace(Whitespace::Space),
4315 Token::Word(Word {
4316 value: "table".to_string(),
4317 quote_style: None,
4318 keyword: Keyword::TABLE,
4319 }),
4320 Token::Period,
4321 Token::Word(Word {
4322 value: "_col".to_string(),
4323 quote_style: None,
4324 keyword: Keyword::NoKeyword,
4325 }),
4326 ];
4327
4328 compare(expected, tokens);
4329
4330 let sql = String::from("SELECT ._123");
4331 if let Ok(tokens) = Tokenizer::new(&dialect, &sql).tokenize() {
4332 panic!("Tokenizer should have failed on {sql}, but it succeeded with {tokens:?}");
4333 }
4334
4335 let sql = String::from("SELECT ._abc");
4336 if let Ok(tokens) = Tokenizer::new(&dialect, &sql).tokenize() {
4337 panic!("Tokenizer should have failed on {sql}, but it succeeded with {tokens:?}");
4338 }
4339 }
4340
4341 #[test]
4342 fn tokenize_question_mark() {
4343 let dialect = PostgreSqlDialect {};
4344 let sql = "SELECT x ? y";
4345 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4346 compare(
4347 tokens,
4348 vec![
4349 Token::make_keyword("SELECT"),
4350 Token::Whitespace(Whitespace::Space),
4351 Token::make_word("x", None),
4352 Token::Whitespace(Whitespace::Space),
4353 Token::Question,
4354 Token::Whitespace(Whitespace::Space),
4355 Token::make_word("y", None),
4356 ],
4357 );
4358 }
4359
4360 #[test]
4361 fn tokenize_multiline_comment_with_comment_hint() {
4362 let sql = String::from("0/*! word */1");
4363
4364 let dialect = MySqlDialect {};
4365 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
4366 let expected = vec![
4367 Token::Number("0".to_string(), false),
4368 Token::Whitespace(Whitespace::Space),
4369 Token::Word(Word {
4370 value: "word".to_string(),
4371 quote_style: None,
4372 keyword: Keyword::NoKeyword,
4373 }),
4374 Token::Whitespace(Whitespace::Space),
4375 Token::Number("1".to_string(), false),
4376 ];
4377 compare(expected, tokens);
4378 }
4379
4380 #[test]
4381 fn tokenize_multiline_comment_with_comment_hint_and_version() {
4382 let sql_multi = String::from("0 /*!50110 KEY_BLOCK_SIZE = 1024*/ 1");
4383 let dialect = MySqlDialect {};
4384 let tokens = Tokenizer::new(&dialect, &sql_multi).tokenize().unwrap();
4385 let expected = vec![
4386 Token::Number("0".to_string(), false),
4387 Token::Whitespace(Whitespace::Space),
4388 Token::Whitespace(Whitespace::Space),
4389 Token::Word(Word {
4390 value: "KEY_BLOCK_SIZE".to_string(),
4391 quote_style: None,
4392 keyword: Keyword::KEY_BLOCK_SIZE,
4393 }),
4394 Token::Whitespace(Whitespace::Space),
4395 Token::Eq,
4396 Token::Whitespace(Whitespace::Space),
4397 Token::Number("1024".to_string(), false),
4398 Token::Whitespace(Whitespace::Space),
4399 Token::Number("1".to_string(), false),
4400 ];
4401 compare(expected, tokens);
4402
4403 let tokens = Tokenizer::new(&dialect, "0 /*!50110 */ 1")
4404 .tokenize()
4405 .unwrap();
4406 compare(
4407 vec![
4408 Token::Number("0".to_string(), false),
4409 Token::Whitespace(Whitespace::Space),
4410 Token::Whitespace(Whitespace::Space),
4411 Token::Whitespace(Whitespace::Space),
4412 Token::Number("1".to_string(), false),
4413 ],
4414 tokens,
4415 );
4416
4417 let tokens = Tokenizer::new(&dialect, "0 /*!*/ 1").tokenize().unwrap();
4418 compare(
4419 vec![
4420 Token::Number("0".to_string(), false),
4421 Token::Whitespace(Whitespace::Space),
4422 Token::Whitespace(Whitespace::Space),
4423 Token::Number("1".to_string(), false),
4424 ],
4425 tokens,
4426 );
4427 let tokens = Tokenizer::new(&dialect, "0 /*! */ 1").tokenize().unwrap();
4428 compare(
4429 vec![
4430 Token::Number("0".to_string(), false),
4431 Token::Whitespace(Whitespace::Space),
4432 Token::Whitespace(Whitespace::Space),
4433 Token::Whitespace(Whitespace::Space),
4434 Token::Whitespace(Whitespace::Space),
4435 Token::Whitespace(Whitespace::Space),
4436 Token::Number("1".to_string(), false),
4437 ],
4438 tokens,
4439 );
4440 }
4441
4442 #[test]
4443 fn tokenize_lt() {
4444 all_dialects().tokenizes_to(
4445 "select a <-50",
4446 vec![
4447 Token::make_keyword("select"),
4448 Token::Whitespace(Whitespace::Space),
4449 Token::make_word("a", None),
4450 Token::Whitespace(Whitespace::Space),
4451 Token::Lt,
4452 Token::Minus,
4453 Token::Number("50".to_string(), false),
4454 ],
4455 );
4456 all_dialects().tokenizes_to(
4457 "select a <+50",
4458 vec![
4459 Token::make_keyword("select"),
4460 Token::Whitespace(Whitespace::Space),
4461 Token::make_word("a", None),
4462 Token::Whitespace(Whitespace::Space),
4463 Token::Lt,
4464 Token::Plus,
4465 Token::Number("50".to_string(), false),
4466 ],
4467 );
4468 all_dialects().tokenizes_to(
4469 "select a <=-50",
4470 vec![
4471 Token::make_keyword("select"),
4472 Token::Whitespace(Whitespace::Space),
4473 Token::make_word("a", None),
4474 Token::Whitespace(Whitespace::Space),
4475 Token::LtEq,
4476 Token::Minus,
4477 Token::Number("50".to_string(), false),
4478 ],
4479 );
4480 all_dialects().tokenizes_to(
4481 "select a <=+50",
4482 vec![
4483 Token::make_keyword("select"),
4484 Token::Whitespace(Whitespace::Space),
4485 Token::make_word("a", None),
4486 Token::Whitespace(Whitespace::Space),
4487 Token::LtEq,
4488 Token::Plus,
4489 Token::Number("50".to_string(), false),
4490 ],
4491 );
4492 all_dialects_where(|d| d.supports_geometric_types()).tokenizes_to(
4493 "select a <->b",
4494 vec![
4495 Token::make_keyword("select"),
4496 Token::Whitespace(Whitespace::Space),
4497 Token::make_word("a", None),
4498 Token::Whitespace(Whitespace::Space),
4499 Token::TwoWayArrow,
4500 Token::make_word("b", None),
4501 ],
4502 );
4503
4504 all_dialects().tokenizes_to(
4505 "select a <-b",
4506 vec![
4507 Token::make_keyword("select"),
4508 Token::Whitespace(Whitespace::Space),
4509 Token::make_word("a", None),
4510 Token::Whitespace(Whitespace::Space),
4511 Token::Lt,
4512 Token::Minus,
4513 Token::make_word("b", None),
4514 ],
4515 );
4516 all_dialects().tokenizes_to(
4517 "select a <+b",
4518 vec![
4519 Token::make_keyword("select"),
4520 Token::Whitespace(Whitespace::Space),
4521 Token::make_word("a", None),
4522 Token::Whitespace(Whitespace::Space),
4523 Token::Lt,
4524 Token::Plus,
4525 Token::make_word("b", None),
4526 ],
4527 );
4528 }
4529}