1#[cfg(not(feature = "std"))]
25use alloc::{
26 borrow::ToOwned,
27 format,
28 string::{String, ToString},
29 vec,
30 vec::Vec,
31};
32use core::iter::Peekable;
33use core::num::NonZeroU8;
34use core::str::Chars;
35use core::{cmp, fmt};
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::{ast::DollarQuotedString, dialect::HiveDialect};
50
51#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
55pub enum Token {
56 EOF,
58 Word(Word),
60 Number(String, bool),
62 Char(char),
64 SingleQuotedString(String),
66 DoubleQuotedString(String),
68 TripleSingleQuotedString(String),
71 TripleDoubleQuotedString(String),
74 DollarQuotedString(DollarQuotedString),
76 SingleQuotedByteStringLiteral(String),
79 DoubleQuotedByteStringLiteral(String),
81 TripleSingleQuotedByteStringLiteral(String),
84 TripleDoubleQuotedByteStringLiteral(String),
87 SingleQuotedRawStringLiteral(String),
90 DoubleQuotedRawStringLiteral(String),
93 TripleSingleQuotedRawStringLiteral(String),
96 TripleDoubleQuotedRawStringLiteral(String),
99 NationalStringLiteral(String),
101 EscapedStringLiteral(String),
103 UnicodeStringLiteral(String),
105 HexStringLiteral(String),
107 Comma,
109 Whitespace(Whitespace),
111 DoubleEq,
113 Eq,
115 Neq,
117 Lt,
119 Gt,
121 LtEq,
123 GtEq,
125 Spaceship,
127 Plus,
129 Minus,
131 Mul,
133 Div,
135 DuckIntDiv,
137 Mod,
139 StringConcat,
141 LParen,
143 RParen,
145 Period,
147 Colon,
149 DoubleColon,
151 Assignment,
153 SemiColon,
155 Backslash,
157 LBracket,
159 RBracket,
161 Ampersand,
163 Pipe,
165 Caret,
167 LBrace,
169 RBrace,
171 RArrow,
173 Sharp,
175 DoubleSharp,
177 Tilde,
179 TildeAsterisk,
181 ExclamationMarkTilde,
183 ExclamationMarkTildeAsterisk,
185 DoubleTilde,
187 DoubleTildeAsterisk,
189 ExclamationMarkDoubleTilde,
191 ExclamationMarkDoubleTildeAsterisk,
193 ShiftLeft,
195 ShiftRight,
197 Overlap,
199 ExclamationMark,
201 DoubleExclamationMark,
203 AtSign,
205 CaretAt,
207 PGSquareRoot,
209 PGCubeRoot,
211 Placeholder(String),
213 Arrow,
215 LongArrow,
217 HashArrow,
219 AtDashAt,
221 QuestionMarkDash,
223 AmpersandLeftAngleBracket,
225 AmpersandRightAngleBracket,
227 AmpersandLeftAngleBracketVerticalBar,
229 VerticalBarAmpersandRightAngleBracket,
231 TwoWayArrow,
233 LeftAngleBracketCaret,
235 RightAngleBracketCaret,
237 QuestionMarkSharp,
239 QuestionMarkDashVerticalBar,
241 QuestionMarkDoubleVerticalBar,
243 TildeEqual,
245 ShiftLeftVerticalBar,
247 VerticalBarShiftRight,
249 HashLongArrow,
251 AtArrow,
253 ArrowAt,
255 HashMinus,
258 AtQuestion,
261 AtAt,
265 Question,
268 QuestionAnd,
271 QuestionPipe,
274 CustomBinaryOperator(String),
278}
279
280impl fmt::Display for Token {
281 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
282 match self {
283 Token::EOF => f.write_str("EOF"),
284 Token::Word(ref w) => write!(f, "{w}"),
285 Token::Number(ref n, l) => write!(f, "{}{long}", n, long = if *l { "L" } else { "" }),
286 Token::Char(ref c) => write!(f, "{c}"),
287 Token::SingleQuotedString(ref s) => write!(f, "'{s}'"),
288 Token::TripleSingleQuotedString(ref s) => write!(f, "'''{s}'''"),
289 Token::DoubleQuotedString(ref s) => write!(f, "\"{s}\""),
290 Token::TripleDoubleQuotedString(ref s) => write!(f, "\"\"\"{s}\"\"\""),
291 Token::DollarQuotedString(ref s) => write!(f, "{s}"),
292 Token::NationalStringLiteral(ref s) => write!(f, "N'{s}'"),
293 Token::EscapedStringLiteral(ref s) => write!(f, "E'{s}'"),
294 Token::UnicodeStringLiteral(ref s) => write!(f, "U&'{s}'"),
295 Token::HexStringLiteral(ref s) => write!(f, "X'{s}'"),
296 Token::SingleQuotedByteStringLiteral(ref s) => write!(f, "B'{s}'"),
297 Token::TripleSingleQuotedByteStringLiteral(ref s) => write!(f, "B'''{s}'''"),
298 Token::DoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"{s}\""),
299 Token::TripleDoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"\"\"{s}\"\"\""),
300 Token::SingleQuotedRawStringLiteral(ref s) => write!(f, "R'{s}'"),
301 Token::DoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"{s}\""),
302 Token::TripleSingleQuotedRawStringLiteral(ref s) => write!(f, "R'''{s}'''"),
303 Token::TripleDoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"\"\"{s}\"\"\""),
304 Token::Comma => f.write_str(","),
305 Token::Whitespace(ws) => write!(f, "{ws}"),
306 Token::DoubleEq => f.write_str("=="),
307 Token::Spaceship => f.write_str("<=>"),
308 Token::Eq => f.write_str("="),
309 Token::Neq => f.write_str("<>"),
310 Token::Lt => f.write_str("<"),
311 Token::Gt => f.write_str(">"),
312 Token::LtEq => f.write_str("<="),
313 Token::GtEq => f.write_str(">="),
314 Token::Plus => f.write_str("+"),
315 Token::Minus => f.write_str("-"),
316 Token::Mul => f.write_str("*"),
317 Token::Div => f.write_str("/"),
318 Token::DuckIntDiv => f.write_str("//"),
319 Token::StringConcat => f.write_str("||"),
320 Token::Mod => f.write_str("%"),
321 Token::LParen => f.write_str("("),
322 Token::RParen => f.write_str(")"),
323 Token::Period => f.write_str("."),
324 Token::Colon => f.write_str(":"),
325 Token::DoubleColon => f.write_str("::"),
326 Token::Assignment => f.write_str(":="),
327 Token::SemiColon => f.write_str(";"),
328 Token::Backslash => f.write_str("\\"),
329 Token::LBracket => f.write_str("["),
330 Token::RBracket => f.write_str("]"),
331 Token::Ampersand => f.write_str("&"),
332 Token::Caret => f.write_str("^"),
333 Token::Pipe => f.write_str("|"),
334 Token::LBrace => f.write_str("{"),
335 Token::RBrace => f.write_str("}"),
336 Token::RArrow => f.write_str("=>"),
337 Token::Sharp => f.write_str("#"),
338 Token::DoubleSharp => f.write_str("##"),
339 Token::ExclamationMark => f.write_str("!"),
340 Token::DoubleExclamationMark => f.write_str("!!"),
341 Token::Tilde => f.write_str("~"),
342 Token::TildeAsterisk => f.write_str("~*"),
343 Token::ExclamationMarkTilde => f.write_str("!~"),
344 Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
345 Token::DoubleTilde => f.write_str("~~"),
346 Token::DoubleTildeAsterisk => f.write_str("~~*"),
347 Token::ExclamationMarkDoubleTilde => f.write_str("!~~"),
348 Token::ExclamationMarkDoubleTildeAsterisk => f.write_str("!~~*"),
349 Token::AtSign => f.write_str("@"),
350 Token::CaretAt => f.write_str("^@"),
351 Token::ShiftLeft => f.write_str("<<"),
352 Token::ShiftRight => f.write_str(">>"),
353 Token::Overlap => f.write_str("&&"),
354 Token::PGSquareRoot => f.write_str("|/"),
355 Token::PGCubeRoot => f.write_str("||/"),
356 Token::AtDashAt => f.write_str("@-@"),
357 Token::QuestionMarkDash => f.write_str("?-"),
358 Token::AmpersandLeftAngleBracket => f.write_str("&<"),
359 Token::AmpersandRightAngleBracket => f.write_str("&>"),
360 Token::AmpersandLeftAngleBracketVerticalBar => f.write_str("&<|"),
361 Token::VerticalBarAmpersandRightAngleBracket => f.write_str("|&>"),
362 Token::TwoWayArrow => f.write_str("<->"),
363 Token::LeftAngleBracketCaret => f.write_str("<^"),
364 Token::RightAngleBracketCaret => f.write_str(">^"),
365 Token::QuestionMarkSharp => f.write_str("?#"),
366 Token::QuestionMarkDashVerticalBar => f.write_str("?-|"),
367 Token::QuestionMarkDoubleVerticalBar => f.write_str("?||"),
368 Token::TildeEqual => f.write_str("~="),
369 Token::ShiftLeftVerticalBar => f.write_str("<<|"),
370 Token::VerticalBarShiftRight => f.write_str("|>>"),
371 Token::Placeholder(ref s) => write!(f, "{s}"),
372 Token::Arrow => write!(f, "->"),
373 Token::LongArrow => write!(f, "->>"),
374 Token::HashArrow => write!(f, "#>"),
375 Token::HashLongArrow => write!(f, "#>>"),
376 Token::AtArrow => write!(f, "@>"),
377 Token::ArrowAt => write!(f, "<@"),
378 Token::HashMinus => write!(f, "#-"),
379 Token::AtQuestion => write!(f, "@?"),
380 Token::AtAt => write!(f, "@@"),
381 Token::Question => write!(f, "?"),
382 Token::QuestionAnd => write!(f, "?&"),
383 Token::QuestionPipe => write!(f, "?|"),
384 Token::CustomBinaryOperator(s) => f.write_str(s),
385 }
386 }
387}
388
389impl Token {
390 pub fn make_keyword(keyword: &str) -> Self {
391 Token::make_word(keyword, None)
392 }
393
394 pub fn make_word(word: &str, quote_style: Option<char>) -> Self {
395 let word_uppercase = word.to_uppercase();
396 Token::Word(Word {
397 value: word.to_string(),
398 quote_style,
399 keyword: if quote_style.is_none() {
400 let keyword = ALL_KEYWORDS.binary_search(&word_uppercase.as_str());
401 keyword.map_or(Keyword::NoKeyword, |x| ALL_KEYWORDS_INDEX[x])
402 } else {
403 Keyword::NoKeyword
404 },
405 })
406 }
407}
408
409#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
411#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
412#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
413pub struct Word {
414 pub value: String,
417 pub quote_style: Option<char>,
421 pub keyword: Keyword,
424}
425
426impl fmt::Display for Word {
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 match self.quote_style {
429 Some(s) if s == '"' || s == '[' || s == '`' => {
430 write!(f, "{}{}{}", s, self.value, Word::matching_end_quote(s))
431 }
432 None => f.write_str(&self.value),
433 _ => panic!("Unexpected quote_style!"),
434 }
435 }
436}
437
438impl Word {
439 fn matching_end_quote(ch: char) -> char {
440 match ch {
441 '"' => '"', '[' => ']', '`' => '`', _ => panic!("unexpected quoting style!"),
445 }
446 }
447}
448
449#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
450#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
451#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
452pub enum Whitespace {
453 Space,
454 Newline,
455 Tab,
456 SingleLineComment { comment: String, prefix: String },
457 MultiLineComment(String),
458}
459
460impl fmt::Display for Whitespace {
461 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
462 match self {
463 Whitespace::Space => f.write_str(" "),
464 Whitespace::Newline => f.write_str("\n"),
465 Whitespace::Tab => f.write_str("\t"),
466 Whitespace::SingleLineComment { prefix, comment } => write!(f, "{prefix}{comment}"),
467 Whitespace::MultiLineComment(s) => write!(f, "/*{s}*/"),
468 }
469 }
470}
471
472#[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)]
492#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
493#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
494pub struct Location {
495 pub line: u64,
499 pub column: u64,
503}
504
505impl fmt::Display for Location {
506 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507 if self.line == 0 {
508 return Ok(());
509 }
510 write!(f, " at Line: {}, Column: {}", self.line, self.column)
511 }
512}
513
514impl fmt::Debug for Location {
515 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516 write!(f, "Location({},{})", self.line, self.column)
517 }
518}
519
520impl Location {
521 pub fn empty() -> Self {
523 Self { line: 0, column: 0 }
524 }
525
526 pub fn new(line: u64, column: u64) -> Self {
528 Self { line, column }
529 }
530
531 pub fn of(line: u64, column: u64) -> Self {
536 Self::new(line, column)
537 }
538
539 pub fn span_to(self, end: Self) -> Span {
541 Span { start: self, end }
542 }
543}
544
545impl From<(u64, u64)> for Location {
546 fn from((line, column): (u64, u64)) -> Self {
547 Self { line, column }
548 }
549}
550
551#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)]
555#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
556#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
557pub struct Span {
558 pub start: Location,
559 pub end: Location,
560}
561
562impl fmt::Debug for Span {
563 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564 write!(f, "Span({:?}..{:?})", self.start, self.end)
565 }
566}
567
568impl Span {
569 const EMPTY: Span = Self::empty();
572
573 pub fn new(start: Location, end: Location) -> Span {
575 Span { start, end }
576 }
577
578 pub const fn empty() -> Span {
583 Span {
584 start: Location { line: 0, column: 0 },
585 end: Location { line: 0, column: 0 },
586 }
587 }
588
589 pub fn union(&self, other: &Span) -> Span {
605 match (self, other) {
608 (&Span::EMPTY, _) => *other,
609 (_, &Span::EMPTY) => *self,
610 _ => Span {
611 start: cmp::min(self.start, other.start),
612 end: cmp::max(self.end, other.end),
613 },
614 }
615 }
616
617 pub fn union_opt(&self, other: &Option<Span>) -> Span {
621 match other {
622 Some(other) => self.union(other),
623 None => *self,
624 }
625 }
626
627 pub fn union_iter<I: IntoIterator<Item = Span>>(iter: I) -> Span {
645 iter.into_iter()
646 .reduce(|acc, item| acc.union(&item))
647 .unwrap_or(Span::empty())
648 }
649}
650
651#[deprecated(since = "0.53.0", note = "please use `TokenWithSpan` instead")]
653pub type TokenWithLocation = TokenWithSpan;
654
655#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
678#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
679#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
680pub struct TokenWithSpan {
681 pub token: Token,
682 pub span: Span,
683}
684
685impl TokenWithSpan {
686 pub fn new(token: Token, span: Span) -> Self {
688 Self { token, span }
689 }
690
691 pub fn wrap(token: Token) -> Self {
693 Self::new(token, Span::empty())
694 }
695
696 pub fn at(token: Token, start: Location, end: Location) -> Self {
698 Self::new(token, Span::new(start, end))
699 }
700
701 pub fn new_eof() -> Self {
703 Self::wrap(Token::EOF)
704 }
705}
706
707impl PartialEq<Token> for TokenWithSpan {
708 fn eq(&self, other: &Token) -> bool {
709 &self.token == other
710 }
711}
712
713impl PartialEq<TokenWithSpan> for Token {
714 fn eq(&self, other: &TokenWithSpan) -> bool {
715 self == &other.token
716 }
717}
718
719impl fmt::Display for TokenWithSpan {
720 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
721 self.token.fmt(f)
722 }
723}
724
725#[derive(Debug, PartialEq, Eq)]
727pub struct TokenizerError {
728 pub message: String,
729 pub location: Location,
730}
731
732impl fmt::Display for TokenizerError {
733 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734 write!(f, "{}{}", self.message, self.location,)
735 }
736}
737
738#[cfg(feature = "std")]
739impl std::error::Error for TokenizerError {}
740
741struct State<'a> {
742 peekable: Peekable<Chars<'a>>,
743 pub line: u64,
744 pub col: u64,
745}
746
747impl State<'_> {
748 pub fn next(&mut self) -> Option<char> {
750 match self.peekable.next() {
751 None => None,
752 Some(s) => {
753 if s == '\n' {
754 self.line += 1;
755 self.col = 1;
756 } else {
757 self.col += 1;
758 }
759 Some(s)
760 }
761 }
762 }
763
764 pub fn peek(&mut self) -> Option<&char> {
766 self.peekable.peek()
767 }
768
769 pub fn location(&self) -> Location {
770 Location {
771 line: self.line,
772 column: self.col,
773 }
774 }
775}
776
777#[derive(Copy, Clone)]
779enum NumStringQuoteChars {
780 One,
782 Many(NonZeroU8),
784}
785
786struct TokenizeQuotedStringSettings {
788 quote_style: char,
790 num_quote_chars: NumStringQuoteChars,
792 num_opening_quotes_to_consume: u8,
798 backslash_escape: bool,
801}
802
803pub struct Tokenizer<'a> {
805 dialect: &'a dyn Dialect,
806 query: &'a str,
807 unescape: bool,
810}
811
812impl<'a> Tokenizer<'a> {
813 pub fn new(dialect: &'a dyn Dialect, query: &'a str) -> Self {
830 Self {
831 dialect,
832 query,
833 unescape: true,
834 }
835 }
836
837 pub fn with_unescape(mut self, unescape: bool) -> Self {
868 self.unescape = unescape;
869 self
870 }
871
872 pub fn tokenize(&mut self) -> Result<Vec<Token>, TokenizerError> {
874 let twl = self.tokenize_with_location()?;
875 Ok(twl.into_iter().map(|t| t.token).collect())
876 }
877
878 pub fn tokenize_with_location(&mut self) -> Result<Vec<TokenWithSpan>, TokenizerError> {
880 let mut tokens: Vec<TokenWithSpan> = vec![];
881 self.tokenize_with_location_into_buf(&mut tokens)
882 .map(|_| tokens)
883 }
884
885 pub fn tokenize_with_location_into_buf(
888 &mut self,
889 buf: &mut Vec<TokenWithSpan>,
890 ) -> Result<(), TokenizerError> {
891 let mut state = State {
892 peekable: self.query.chars().peekable(),
893 line: 1,
894 col: 1,
895 };
896
897 let mut location = state.location();
898 while let Some(token) = self.next_token(&mut state)? {
899 let span = location.span_to(state.location());
900
901 buf.push(TokenWithSpan { token, span });
902
903 location = state.location();
904 }
905 Ok(())
906 }
907
908 fn tokenize_identifier_or_keyword(
910 &self,
911 ch: impl IntoIterator<Item = char>,
912 chars: &mut State,
913 ) -> Result<Option<Token>, TokenizerError> {
914 chars.next(); let ch: String = ch.into_iter().collect();
916 let word = self.tokenize_word(ch, chars);
917
918 if word.chars().all(|x| x.is_ascii_digit() || x == '.') {
920 let mut inner_state = State {
921 peekable: word.chars().peekable(),
922 line: 0,
923 col: 0,
924 };
925 let mut s = peeking_take_while(&mut inner_state, |ch| matches!(ch, '0'..='9' | '.'));
926 let s2 = peeking_take_while(chars, |ch| matches!(ch, '0'..='9' | '.'));
927 s += s2.as_str();
928 return Ok(Some(Token::Number(s, false)));
929 }
930
931 Ok(Some(Token::make_word(&word, None)))
932 }
933
934 fn next_token(&self, chars: &mut State) -> Result<Option<Token>, TokenizerError> {
936 match chars.peek() {
937 Some(&ch) => match ch {
938 ' ' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Space)),
939 '\t' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Tab)),
940 '\n' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Newline)),
941 '\r' => {
942 chars.next();
944 if let Some('\n') = chars.peek() {
945 chars.next();
946 }
947 Ok(Some(Token::Whitespace(Whitespace::Newline)))
948 }
949 b @ 'B' | b @ 'b' if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | MySqlDialect | GenericDialect) =>
951 {
952 chars.next(); match chars.peek() {
954 Some('\'') => {
955 if self.dialect.supports_triple_quoted_string() {
956 return self
957 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
958 chars,
959 '\'',
960 false,
961 Token::SingleQuotedByteStringLiteral,
962 Token::TripleSingleQuotedByteStringLiteral,
963 );
964 }
965 let s = self.tokenize_single_quoted_string(chars, '\'', false)?;
966 Ok(Some(Token::SingleQuotedByteStringLiteral(s)))
967 }
968 Some('\"') => {
969 if self.dialect.supports_triple_quoted_string() {
970 return self
971 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
972 chars,
973 '"',
974 false,
975 Token::DoubleQuotedByteStringLiteral,
976 Token::TripleDoubleQuotedByteStringLiteral,
977 );
978 }
979 let s = self.tokenize_single_quoted_string(chars, '\"', false)?;
980 Ok(Some(Token::DoubleQuotedByteStringLiteral(s)))
981 }
982 _ => {
983 let s = self.tokenize_word(b, chars);
985 Ok(Some(Token::make_word(&s, None)))
986 }
987 }
988 }
989 b @ 'R' | b @ 'r' if dialect_of!(self is BigQueryDialect | GenericDialect) => {
991 chars.next(); match chars.peek() {
993 Some('\'') => self
994 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
995 chars,
996 '\'',
997 false,
998 Token::SingleQuotedRawStringLiteral,
999 Token::TripleSingleQuotedRawStringLiteral,
1000 ),
1001 Some('\"') => self
1002 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1003 chars,
1004 '"',
1005 false,
1006 Token::DoubleQuotedRawStringLiteral,
1007 Token::TripleDoubleQuotedRawStringLiteral,
1008 ),
1009 _ => {
1010 let s = self.tokenize_word(b, chars);
1012 Ok(Some(Token::make_word(&s, None)))
1013 }
1014 }
1015 }
1016 n @ 'N' | n @ 'n' => {
1018 chars.next(); match chars.peek() {
1020 Some('\'') => {
1021 let backslash_escape =
1023 self.dialect.supports_string_literal_backslash_escape();
1024 let s =
1025 self.tokenize_single_quoted_string(chars, '\'', backslash_escape)?;
1026 Ok(Some(Token::NationalStringLiteral(s)))
1027 }
1028 _ => {
1029 let s = self.tokenize_word(n, chars);
1031 Ok(Some(Token::make_word(&s, None)))
1032 }
1033 }
1034 }
1035 x @ 'e' | x @ 'E' if self.dialect.supports_string_escape_constant() => {
1037 let starting_loc = chars.location();
1038 chars.next(); match chars.peek() {
1040 Some('\'') => {
1041 let s =
1042 self.tokenize_escaped_single_quoted_string(starting_loc, chars)?;
1043 Ok(Some(Token::EscapedStringLiteral(s)))
1044 }
1045 _ => {
1046 let s = self.tokenize_word(x, chars);
1048 Ok(Some(Token::make_word(&s, None)))
1049 }
1050 }
1051 }
1052 x @ 'u' | x @ 'U' if self.dialect.supports_unicode_string_literal() => {
1054 chars.next(); if chars.peek() == Some(&'&') {
1056 let mut chars_clone = chars.peekable.clone();
1058 chars_clone.next(); if chars_clone.peek() == Some(&'\'') {
1060 chars.next(); let s = unescape_unicode_single_quoted_string(chars)?;
1062 return Ok(Some(Token::UnicodeStringLiteral(s)));
1063 }
1064 }
1065 let s = self.tokenize_word(x, chars);
1067 Ok(Some(Token::make_word(&s, None)))
1068 }
1069 x @ 'x' | x @ 'X' => {
1072 chars.next(); match chars.peek() {
1074 Some('\'') => {
1075 let s = self.tokenize_single_quoted_string(chars, '\'', true)?;
1077 Ok(Some(Token::HexStringLiteral(s)))
1078 }
1079 _ => {
1080 let s = self.tokenize_word(x, chars);
1082 Ok(Some(Token::make_word(&s, None)))
1083 }
1084 }
1085 }
1086 '\'' => {
1088 if self.dialect.supports_triple_quoted_string() {
1089 return self
1090 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1091 chars,
1092 '\'',
1093 self.dialect.supports_string_literal_backslash_escape(),
1094 Token::SingleQuotedString,
1095 Token::TripleSingleQuotedString,
1096 );
1097 }
1098 let s = self.tokenize_single_quoted_string(
1099 chars,
1100 '\'',
1101 self.dialect.supports_string_literal_backslash_escape(),
1102 )?;
1103
1104 Ok(Some(Token::SingleQuotedString(s)))
1105 }
1106 '\"' if !self.dialect.is_delimited_identifier_start(ch)
1108 && !self.dialect.is_identifier_start(ch) =>
1109 {
1110 if self.dialect.supports_triple_quoted_string() {
1111 return self
1112 .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1113 chars,
1114 '"',
1115 self.dialect.supports_string_literal_backslash_escape(),
1116 Token::DoubleQuotedString,
1117 Token::TripleDoubleQuotedString,
1118 );
1119 }
1120 let s = self.tokenize_single_quoted_string(
1121 chars,
1122 '"',
1123 self.dialect.supports_string_literal_backslash_escape(),
1124 )?;
1125
1126 Ok(Some(Token::DoubleQuotedString(s)))
1127 }
1128 quote_start if self.dialect.is_delimited_identifier_start(ch) => {
1130 let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1131 Ok(Some(Token::make_word(&word, Some(quote_start))))
1132 }
1133 quote_start
1135 if self
1136 .dialect
1137 .is_nested_delimited_identifier_start(quote_start)
1138 && self
1139 .dialect
1140 .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1141 .is_some() =>
1142 {
1143 let Some((quote_start, nested_quote_start)) = self
1144 .dialect
1145 .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1146 else {
1147 return self.tokenizer_error(
1148 chars.location(),
1149 format!("Expected nested delimiter '{quote_start}' before EOF."),
1150 );
1151 };
1152
1153 let Some(nested_quote_start) = nested_quote_start else {
1154 let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1155 return Ok(Some(Token::make_word(&word, Some(quote_start))));
1156 };
1157
1158 let mut word = vec![];
1159 let quote_end = Word::matching_end_quote(quote_start);
1160 let nested_quote_end = Word::matching_end_quote(nested_quote_start);
1161 let error_loc = chars.location();
1162
1163 chars.next(); peeking_take_while(chars, |ch| ch.is_whitespace());
1165 if chars.peek() != Some(&nested_quote_start) {
1166 return self.tokenizer_error(
1167 error_loc,
1168 format!("Expected nested delimiter '{nested_quote_start}' before EOF."),
1169 );
1170 }
1171 word.push(nested_quote_start.into());
1172 word.push(self.tokenize_quoted_identifier(nested_quote_end, chars)?);
1173 word.push(nested_quote_end.into());
1174 peeking_take_while(chars, |ch| ch.is_whitespace());
1175 if chars.peek() != Some("e_end) {
1176 return self.tokenizer_error(
1177 error_loc,
1178 format!("Expected close delimiter '{quote_end}' before EOF."),
1179 );
1180 }
1181 chars.next(); Ok(Some(Token::make_word(&word.concat(), Some(quote_start))))
1184 }
1185 '0'..='9' | '.' => {
1187 let is_number_separator = |ch: char, next_char: Option<char>| {
1190 self.dialect.supports_numeric_literal_underscores()
1191 && ch == '_'
1192 && next_char.is_some_and(|next_ch| next_ch.is_ascii_hexdigit())
1193 };
1194
1195 let mut s = peeking_next_take_while(chars, |ch, next_ch| {
1196 ch.is_ascii_digit() || is_number_separator(ch, next_ch)
1197 });
1198
1199 if s == "0" && chars.peek() == Some(&'x') {
1201 chars.next();
1202 let s2 = peeking_next_take_while(chars, |ch, next_ch| {
1203 ch.is_ascii_hexdigit() || is_number_separator(ch, next_ch)
1204 });
1205 return Ok(Some(Token::HexStringLiteral(s2)));
1206 }
1207
1208 if let Some('.') = chars.peek() {
1210 s.push('.');
1211 chars.next();
1212 }
1213
1214 s += &peeking_next_take_while(chars, |ch, next_ch| {
1215 ch.is_ascii_digit() || is_number_separator(ch, next_ch)
1216 });
1217
1218 if s == "." {
1220 return Ok(Some(Token::Period));
1221 }
1222
1223 let mut exponent_part = String::new();
1224 if chars.peek() == Some(&'e') || chars.peek() == Some(&'E') {
1226 let mut char_clone = chars.peekable.clone();
1227 exponent_part.push(char_clone.next().unwrap());
1228
1229 match char_clone.peek() {
1231 Some(&c) if matches!(c, '+' | '-') => {
1232 exponent_part.push(c);
1233 char_clone.next();
1234 }
1235 _ => (),
1236 }
1237
1238 match char_clone.peek() {
1239 Some(&c) if c.is_ascii_digit() => {
1241 for _ in 0..exponent_part.len() {
1242 chars.next();
1243 }
1244 exponent_part +=
1245 &peeking_take_while(chars, |ch| ch.is_ascii_digit());
1246 s += exponent_part.as_str();
1247 }
1248 _ => (),
1250 }
1251 }
1252
1253 if self.dialect.supports_numeric_prefix() && exponent_part.is_empty() {
1256 let word =
1257 peeking_take_while(chars, |ch| self.dialect.is_identifier_part(ch));
1258
1259 if !word.is_empty() {
1260 s += word.as_str();
1261 return Ok(Some(Token::make_word(s.as_str(), None)));
1262 }
1263 }
1264
1265 let long = if chars.peek() == Some(&'L') {
1266 chars.next();
1267 true
1268 } else {
1269 false
1270 };
1271 Ok(Some(Token::Number(s, long)))
1272 }
1273 '(' => self.consume_and_return(chars, Token::LParen),
1275 ')' => self.consume_and_return(chars, Token::RParen),
1276 ',' => self.consume_and_return(chars, Token::Comma),
1277 '-' => {
1279 chars.next(); match chars.peek() {
1282 Some('-') => {
1283 let mut is_comment = true;
1284 if self.dialect.requires_single_line_comment_whitespace() {
1285 is_comment = Some(' ') == chars.peekable.clone().nth(1);
1286 }
1287
1288 if is_comment {
1289 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1291 return Ok(Some(Token::Whitespace(
1292 Whitespace::SingleLineComment {
1293 prefix: "--".to_owned(),
1294 comment,
1295 },
1296 )));
1297 }
1298
1299 self.start_binop(chars, "-", Token::Minus)
1300 }
1301 Some('>') => {
1302 chars.next();
1303 match chars.peek() {
1304 Some('>') => self.consume_for_binop(chars, "->>", Token::LongArrow),
1305 _ => self.start_binop(chars, "->", Token::Arrow),
1306 }
1307 }
1308 _ => self.start_binop(chars, "-", Token::Minus),
1310 }
1311 }
1312 '/' => {
1313 chars.next(); match chars.peek() {
1315 Some('*') => {
1316 chars.next(); self.tokenize_multiline_comment(chars)
1318 }
1319 Some('/') if dialect_of!(self is SnowflakeDialect) => {
1320 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1322 Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1323 prefix: "//".to_owned(),
1324 comment,
1325 })))
1326 }
1327 Some('/') if dialect_of!(self is DuckDbDialect | GenericDialect) => {
1328 self.consume_and_return(chars, Token::DuckIntDiv)
1329 }
1330 _ => Ok(Some(Token::Div)),
1332 }
1333 }
1334 '+' => self.consume_and_return(chars, Token::Plus),
1335 '*' => self.consume_and_return(chars, Token::Mul),
1336 '%' => {
1337 chars.next(); match chars.peek() {
1339 Some(s) if s.is_whitespace() => Ok(Some(Token::Mod)),
1340 Some(sch) if self.dialect.is_identifier_start('%') => {
1341 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1342 }
1343 _ => self.start_binop(chars, "%", Token::Mod),
1344 }
1345 }
1346 '|' => {
1347 chars.next(); match chars.peek() {
1349 Some('/') => self.consume_for_binop(chars, "|/", Token::PGSquareRoot),
1350 Some('|') => {
1351 chars.next(); match chars.peek() {
1353 Some('/') => {
1354 self.consume_for_binop(chars, "||/", Token::PGCubeRoot)
1355 }
1356 _ => self.start_binop(chars, "||", Token::StringConcat),
1357 }
1358 }
1359 Some('&') if self.dialect.supports_geometric_types() => {
1360 chars.next(); match chars.peek() {
1362 Some('>') => self.consume_for_binop(
1363 chars,
1364 "|&>",
1365 Token::VerticalBarAmpersandRightAngleBracket,
1366 ),
1367 _ => self.start_binop_opt(chars, "|&", None),
1368 }
1369 }
1370 Some('>') if self.dialect.supports_geometric_types() => {
1371 chars.next(); match chars.peek() {
1373 Some('>') => self.consume_for_binop(
1374 chars,
1375 "|>>",
1376 Token::VerticalBarShiftRight,
1377 ),
1378 _ => self.start_binop_opt(chars, "|>", None),
1379 }
1380 }
1381 _ => self.start_binop(chars, "|", Token::Pipe),
1383 }
1384 }
1385 '=' => {
1386 chars.next(); match chars.peek() {
1388 Some('>') => self.consume_and_return(chars, Token::RArrow),
1389 Some('=') => self.consume_and_return(chars, Token::DoubleEq),
1390 _ => Ok(Some(Token::Eq)),
1391 }
1392 }
1393 '!' => {
1394 chars.next(); match chars.peek() {
1396 Some('=') => self.consume_and_return(chars, Token::Neq),
1397 Some('!') => self.consume_and_return(chars, Token::DoubleExclamationMark),
1398 Some('~') => {
1399 chars.next();
1400 match chars.peek() {
1401 Some('*') => self
1402 .consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
1403 Some('~') => {
1404 chars.next();
1405 match chars.peek() {
1406 Some('*') => self.consume_and_return(
1407 chars,
1408 Token::ExclamationMarkDoubleTildeAsterisk,
1409 ),
1410 _ => Ok(Some(Token::ExclamationMarkDoubleTilde)),
1411 }
1412 }
1413 _ => Ok(Some(Token::ExclamationMarkTilde)),
1414 }
1415 }
1416 _ => Ok(Some(Token::ExclamationMark)),
1417 }
1418 }
1419 '<' => {
1420 chars.next(); match chars.peek() {
1422 Some('=') => {
1423 chars.next();
1424 match chars.peek() {
1425 Some('>') => self.consume_for_binop(chars, "<=>", Token::Spaceship),
1426 _ => self.start_binop(chars, "<=", Token::LtEq),
1427 }
1428 }
1429 Some('|') if self.dialect.supports_geometric_types() => {
1430 self.consume_for_binop(chars, "<<|", Token::ShiftLeftVerticalBar)
1431 }
1432 Some('>') => self.consume_for_binop(chars, "<>", Token::Neq),
1433 Some('<') if self.dialect.supports_geometric_types() => {
1434 chars.next(); match chars.peek() {
1436 Some('|') => self.consume_for_binop(
1437 chars,
1438 "<<|",
1439 Token::ShiftLeftVerticalBar,
1440 ),
1441 _ => self.start_binop(chars, "<<", Token::ShiftLeft),
1442 }
1443 }
1444 Some('<') => self.consume_for_binop(chars, "<<", Token::ShiftLeft),
1445 Some('-') if self.dialect.supports_geometric_types() => {
1446 chars.next(); match chars.peek() {
1448 Some('>') => {
1449 self.consume_for_binop(chars, "<->", Token::TwoWayArrow)
1450 }
1451 _ => self.start_binop_opt(chars, "<-", None),
1452 }
1453 }
1454 Some('^') if self.dialect.supports_geometric_types() => {
1455 self.consume_for_binop(chars, "<^", Token::LeftAngleBracketCaret)
1456 }
1457 Some('@') => self.consume_for_binop(chars, "<@", Token::ArrowAt),
1458 _ => self.start_binop(chars, "<", Token::Lt),
1459 }
1460 }
1461 '>' => {
1462 chars.next(); match chars.peek() {
1464 Some('=') => self.consume_for_binop(chars, ">=", Token::GtEq),
1465 Some('>') => self.consume_for_binop(chars, ">>", Token::ShiftRight),
1466 Some('^') if self.dialect.supports_geometric_types() => {
1467 self.consume_for_binop(chars, ">^", Token::RightAngleBracketCaret)
1468 }
1469 _ => self.start_binop(chars, ">", Token::Gt),
1470 }
1471 }
1472 ':' => {
1473 chars.next();
1474 match chars.peek() {
1475 Some(':') => self.consume_and_return(chars, Token::DoubleColon),
1476 Some('=') => self.consume_and_return(chars, Token::Assignment),
1477 _ => Ok(Some(Token::Colon)),
1478 }
1479 }
1480 ';' => self.consume_and_return(chars, Token::SemiColon),
1481 '\\' => self.consume_and_return(chars, Token::Backslash),
1482 '[' => self.consume_and_return(chars, Token::LBracket),
1483 ']' => self.consume_and_return(chars, Token::RBracket),
1484 '&' => {
1485 chars.next(); match chars.peek() {
1487 Some('>') if self.dialect.supports_geometric_types() => {
1488 chars.next();
1489 self.consume_and_return(chars, Token::AmpersandRightAngleBracket)
1490 }
1491 Some('<') if self.dialect.supports_geometric_types() => {
1492 chars.next(); match chars.peek() {
1494 Some('|') => self.consume_and_return(
1495 chars,
1496 Token::AmpersandLeftAngleBracketVerticalBar,
1497 ),
1498 _ => {
1499 self.start_binop(chars, "&<", Token::AmpersandLeftAngleBracket)
1500 }
1501 }
1502 }
1503 Some('&') => {
1504 chars.next(); self.start_binop(chars, "&&", Token::Overlap)
1506 }
1507 _ => self.start_binop(chars, "&", Token::Ampersand),
1509 }
1510 }
1511 '^' => {
1512 chars.next(); match chars.peek() {
1514 Some('@') => self.consume_and_return(chars, Token::CaretAt),
1515 _ => Ok(Some(Token::Caret)),
1516 }
1517 }
1518 '{' => self.consume_and_return(chars, Token::LBrace),
1519 '}' => self.consume_and_return(chars, Token::RBrace),
1520 '#' if dialect_of!(self is SnowflakeDialect | BigQueryDialect | MySqlDialect | HiveDialect) =>
1521 {
1522 chars.next(); let comment = self.tokenize_single_line_comment(chars);
1524 Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1525 prefix: "#".to_owned(),
1526 comment,
1527 })))
1528 }
1529 '~' => {
1530 chars.next(); match chars.peek() {
1532 Some('*') => self.consume_for_binop(chars, "~*", Token::TildeAsterisk),
1533 Some('=') if self.dialect.supports_geometric_types() => {
1534 self.consume_for_binop(chars, "~=", Token::TildeEqual)
1535 }
1536 Some('~') => {
1537 chars.next();
1538 match chars.peek() {
1539 Some('*') => {
1540 self.consume_for_binop(chars, "~~*", Token::DoubleTildeAsterisk)
1541 }
1542 _ => self.start_binop(chars, "~~", Token::DoubleTilde),
1543 }
1544 }
1545 _ => self.start_binop(chars, "~", Token::Tilde),
1546 }
1547 }
1548 '#' => {
1549 chars.next();
1550 match chars.peek() {
1551 Some('-') => self.consume_for_binop(chars, "#-", Token::HashMinus),
1552 Some('>') => {
1553 chars.next();
1554 match chars.peek() {
1555 Some('>') => {
1556 self.consume_for_binop(chars, "#>>", Token::HashLongArrow)
1557 }
1558 _ => self.start_binop(chars, "#>", Token::HashArrow),
1559 }
1560 }
1561 Some(' ') => Ok(Some(Token::Sharp)),
1562 Some('#') if self.dialect.supports_geometric_types() => {
1563 self.consume_for_binop(chars, "##", Token::DoubleSharp)
1564 }
1565 Some(sch) if self.dialect.is_identifier_start('#') => {
1566 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1567 }
1568 _ => self.start_binop(chars, "#", Token::Sharp),
1569 }
1570 }
1571 '@' => {
1572 chars.next();
1573 match chars.peek() {
1574 Some('@') if self.dialect.supports_geometric_types() => {
1575 self.consume_and_return(chars, Token::AtAt)
1576 }
1577 Some('-') if self.dialect.supports_geometric_types() => {
1578 chars.next();
1579 match chars.peek() {
1580 Some('@') => self.consume_and_return(chars, Token::AtDashAt),
1581 _ => self.start_binop_opt(chars, "@-", None),
1582 }
1583 }
1584 Some('>') => self.consume_and_return(chars, Token::AtArrow),
1585 Some('?') => self.consume_and_return(chars, Token::AtQuestion),
1586 Some('@') => {
1587 chars.next();
1588 match chars.peek() {
1589 Some(' ') => Ok(Some(Token::AtAt)),
1590 Some(tch) if self.dialect.is_identifier_start('@') => {
1591 self.tokenize_identifier_or_keyword([ch, '@', *tch], chars)
1592 }
1593 _ => Ok(Some(Token::AtAt)),
1594 }
1595 }
1596 Some(' ') => Ok(Some(Token::AtSign)),
1597 Some('\'') => Ok(Some(Token::AtSign)),
1607 Some('\"') => Ok(Some(Token::AtSign)),
1608 Some('`') => Ok(Some(Token::AtSign)),
1609 Some(sch) if self.dialect.is_identifier_start('@') => {
1610 self.tokenize_identifier_or_keyword([ch, *sch], chars)
1611 }
1612 _ => Ok(Some(Token::AtSign)),
1613 }
1614 }
1615 '?' if self.dialect.supports_geometric_types() => {
1617 chars.next(); match chars.peek() {
1619 Some('|') => {
1620 chars.next();
1621 match chars.peek() {
1622 Some('|') => self.consume_and_return(
1623 chars,
1624 Token::QuestionMarkDoubleVerticalBar,
1625 ),
1626 _ => Ok(Some(Token::QuestionPipe)),
1627 }
1628 }
1629
1630 Some('&') => self.consume_and_return(chars, Token::QuestionAnd),
1631 Some('-') => {
1632 chars.next(); match chars.peek() {
1634 Some('|') => self
1635 .consume_and_return(chars, Token::QuestionMarkDashVerticalBar),
1636 _ => Ok(Some(Token::QuestionMarkDash)),
1637 }
1638 }
1639 Some('#') => self.consume_and_return(chars, Token::QuestionMarkSharp),
1640 _ => self.consume_and_return(chars, Token::Question),
1641 }
1642 }
1643 '?' => {
1644 chars.next();
1645 let s = peeking_take_while(chars, |ch| ch.is_numeric());
1646 Ok(Some(Token::Placeholder(String::from("?") + &s)))
1647 }
1648
1649 ch if self.dialect.is_identifier_start(ch) => {
1651 self.tokenize_identifier_or_keyword([ch], chars)
1652 }
1653 '$' => Ok(Some(self.tokenize_dollar_preceded_value(chars)?)),
1654
1655 ch if ch.is_whitespace() => {
1657 self.consume_and_return(chars, Token::Whitespace(Whitespace::Space))
1658 }
1659 other => self.consume_and_return(chars, Token::Char(other)),
1660 },
1661 None => Ok(None),
1662 }
1663 }
1664
1665 fn consume_for_binop(
1667 &self,
1668 chars: &mut State,
1669 prefix: &str,
1670 default: Token,
1671 ) -> Result<Option<Token>, TokenizerError> {
1672 chars.next(); self.start_binop_opt(chars, prefix, Some(default))
1674 }
1675
1676 fn start_binop(
1678 &self,
1679 chars: &mut State,
1680 prefix: &str,
1681 default: Token,
1682 ) -> Result<Option<Token>, TokenizerError> {
1683 self.start_binop_opt(chars, prefix, Some(default))
1684 }
1685
1686 fn start_binop_opt(
1688 &self,
1689 chars: &mut State,
1690 prefix: &str,
1691 default: Option<Token>,
1692 ) -> Result<Option<Token>, TokenizerError> {
1693 let mut custom = None;
1694 while let Some(&ch) = chars.peek() {
1695 if !self.dialect.is_custom_operator_part(ch) {
1696 break;
1697 }
1698
1699 custom.get_or_insert_with(|| prefix.to_string()).push(ch);
1700 chars.next();
1701 }
1702 match (custom, default) {
1703 (Some(custom), _) => Ok(Token::CustomBinaryOperator(custom).into()),
1704 (None, Some(tok)) => Ok(Some(tok)),
1705 (None, None) => self.tokenizer_error(
1706 chars.location(),
1707 format!("Expected a valid binary operator after '{}'", prefix),
1708 ),
1709 }
1710 }
1711
1712 fn tokenize_dollar_preceded_value(&self, chars: &mut State) -> Result<Token, TokenizerError> {
1714 let mut s = String::new();
1715 let mut value = String::new();
1716
1717 chars.next();
1718
1719 if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1721 chars.next();
1722
1723 let mut is_terminated = false;
1724 let mut prev: Option<char> = None;
1725
1726 while let Some(&ch) = chars.peek() {
1727 if prev == Some('$') {
1728 if ch == '$' {
1729 chars.next();
1730 is_terminated = true;
1731 break;
1732 } else {
1733 s.push('$');
1734 s.push(ch);
1735 }
1736 } else if ch != '$' {
1737 s.push(ch);
1738 }
1739
1740 prev = Some(ch);
1741 chars.next();
1742 }
1743
1744 return if chars.peek().is_none() && !is_terminated {
1745 self.tokenizer_error(chars.location(), "Unterminated dollar-quoted string")
1746 } else {
1747 Ok(Token::DollarQuotedString(DollarQuotedString {
1748 value: s,
1749 tag: None,
1750 }))
1751 };
1752 } else {
1753 value.push_str(&peeking_take_while(chars, |ch| {
1754 ch.is_alphanumeric()
1755 || ch == '_'
1756 || matches!(ch, '$' if self.dialect.supports_dollar_placeholder())
1758 }));
1759
1760 if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1762 chars.next();
1763
1764 let mut temp = String::new();
1765 let end_delimiter = format!("${}$", value);
1766
1767 loop {
1768 match chars.next() {
1769 Some(ch) => {
1770 temp.push(ch);
1771
1772 if temp.ends_with(&end_delimiter) {
1773 if let Some(temp) = temp.strip_suffix(&end_delimiter) {
1774 s.push_str(temp);
1775 }
1776 break;
1777 }
1778 }
1779 None => {
1780 if temp.ends_with(&end_delimiter) {
1781 if let Some(temp) = temp.strip_suffix(&end_delimiter) {
1782 s.push_str(temp);
1783 }
1784 break;
1785 }
1786
1787 return self.tokenizer_error(
1788 chars.location(),
1789 "Unterminated dollar-quoted, expected $",
1790 );
1791 }
1792 }
1793 }
1794 } else {
1795 return Ok(Token::Placeholder(String::from("$") + &value));
1796 }
1797 }
1798
1799 Ok(Token::DollarQuotedString(DollarQuotedString {
1800 value: s,
1801 tag: if value.is_empty() { None } else { Some(value) },
1802 }))
1803 }
1804
1805 fn tokenizer_error<R>(
1806 &self,
1807 loc: Location,
1808 message: impl Into<String>,
1809 ) -> Result<R, TokenizerError> {
1810 Err(TokenizerError {
1811 message: message.into(),
1812 location: loc,
1813 })
1814 }
1815
1816 fn tokenize_single_line_comment(&self, chars: &mut State) -> String {
1818 let mut comment = peeking_take_while(chars, |ch| match ch {
1819 '\n' => false, '\r' if dialect_of!(self is PostgreSqlDialect) => false, _ => true, });
1823
1824 if let Some(ch) = chars.next() {
1825 assert!(ch == '\n' || ch == '\r');
1826 comment.push(ch);
1827 }
1828
1829 comment
1830 }
1831
1832 fn tokenize_word(&self, first_chars: impl Into<String>, chars: &mut State) -> String {
1834 let mut s = first_chars.into();
1835 s.push_str(&peeking_take_while(chars, |ch| {
1836 self.dialect.is_identifier_part(ch)
1837 }));
1838 s
1839 }
1840
1841 fn tokenize_quoted_identifier(
1843 &self,
1844 quote_start: char,
1845 chars: &mut State,
1846 ) -> Result<String, TokenizerError> {
1847 let error_loc = chars.location();
1848 chars.next(); let quote_end = Word::matching_end_quote(quote_start);
1850 let (s, last_char) = self.parse_quoted_ident(chars, quote_end);
1851
1852 if last_char == Some(quote_end) {
1853 Ok(s)
1854 } else {
1855 self.tokenizer_error(
1856 error_loc,
1857 format!("Expected close delimiter '{quote_end}' before EOF."),
1858 )
1859 }
1860 }
1861
1862 fn tokenize_escaped_single_quoted_string(
1864 &self,
1865 starting_loc: Location,
1866 chars: &mut State,
1867 ) -> Result<String, TokenizerError> {
1868 if let Some(s) = unescape_single_quoted_string(chars) {
1869 return Ok(s);
1870 }
1871
1872 self.tokenizer_error(starting_loc, "Unterminated encoded string literal")
1873 }
1874
1875 fn tokenize_single_or_triple_quoted_string<F>(
1878 &self,
1879 chars: &mut State,
1880 quote_style: char,
1881 backslash_escape: bool,
1882 single_quote_token: F,
1883 triple_quote_token: F,
1884 ) -> Result<Option<Token>, TokenizerError>
1885 where
1886 F: Fn(String) -> Token,
1887 {
1888 let error_loc = chars.location();
1889
1890 let mut num_opening_quotes = 0u8;
1891 for _ in 0..3 {
1892 if Some("e_style) == chars.peek() {
1893 chars.next(); num_opening_quotes += 1;
1895 } else {
1896 break;
1897 }
1898 }
1899
1900 let (token_fn, num_quote_chars) = match num_opening_quotes {
1901 1 => (single_quote_token, NumStringQuoteChars::One),
1902 2 => {
1903 return Ok(Some(single_quote_token("".into())));
1905 }
1906 3 => {
1907 let Some(num_quote_chars) = NonZeroU8::new(3) else {
1908 return self.tokenizer_error(error_loc, "invalid number of opening quotes");
1909 };
1910 (
1911 triple_quote_token,
1912 NumStringQuoteChars::Many(num_quote_chars),
1913 )
1914 }
1915 _ => {
1916 return self.tokenizer_error(error_loc, "invalid string literal opening");
1917 }
1918 };
1919
1920 let settings = TokenizeQuotedStringSettings {
1921 quote_style,
1922 num_quote_chars,
1923 num_opening_quotes_to_consume: 0,
1924 backslash_escape,
1925 };
1926
1927 self.tokenize_quoted_string(chars, settings)
1928 .map(token_fn)
1929 .map(Some)
1930 }
1931
1932 fn tokenize_single_quoted_string(
1934 &self,
1935 chars: &mut State,
1936 quote_style: char,
1937 backslash_escape: bool,
1938 ) -> Result<String, TokenizerError> {
1939 self.tokenize_quoted_string(
1940 chars,
1941 TokenizeQuotedStringSettings {
1942 quote_style,
1943 num_quote_chars: NumStringQuoteChars::One,
1944 num_opening_quotes_to_consume: 1,
1945 backslash_escape,
1946 },
1947 )
1948 }
1949
1950 fn tokenize_quoted_string(
1952 &self,
1953 chars: &mut State,
1954 settings: TokenizeQuotedStringSettings,
1955 ) -> Result<String, TokenizerError> {
1956 let mut s = String::new();
1957 let error_loc = chars.location();
1958
1959 for _ in 0..settings.num_opening_quotes_to_consume {
1961 if Some(settings.quote_style) != chars.next() {
1962 return self.tokenizer_error(error_loc, "invalid string literal opening");
1963 }
1964 }
1965
1966 let mut num_consecutive_quotes = 0;
1967 while let Some(&ch) = chars.peek() {
1968 let pending_final_quote = match settings.num_quote_chars {
1969 NumStringQuoteChars::One => Some(NumStringQuoteChars::One),
1970 n @ NumStringQuoteChars::Many(count)
1971 if num_consecutive_quotes + 1 == count.get() =>
1972 {
1973 Some(n)
1974 }
1975 NumStringQuoteChars::Many(_) => None,
1976 };
1977
1978 match ch {
1979 char if char == settings.quote_style && pending_final_quote.is_some() => {
1980 chars.next(); if let Some(NumStringQuoteChars::Many(count)) = pending_final_quote {
1983 let mut buf = s.chars();
1988 for _ in 1..count.get() {
1989 buf.next_back();
1990 }
1991 return Ok(buf.as_str().to_string());
1992 } else if chars
1993 .peek()
1994 .map(|c| *c == settings.quote_style)
1995 .unwrap_or(false)
1996 {
1997 s.push(ch);
1998 if !self.unescape {
1999 s.push(ch);
2001 }
2002 chars.next();
2003 } else {
2004 return Ok(s);
2005 }
2006 }
2007 '\\' if settings.backslash_escape => {
2008 chars.next();
2010
2011 num_consecutive_quotes = 0;
2012
2013 if let Some(next) = chars.peek() {
2014 if !self.unescape {
2015 s.push(ch);
2017 s.push(*next);
2018 chars.next(); } else {
2020 let n = match next {
2021 '0' => '\0',
2022 'a' => '\u{7}',
2023 'b' => '\u{8}',
2024 'f' => '\u{c}',
2025 'n' => '\n',
2026 'r' => '\r',
2027 't' => '\t',
2028 'Z' => '\u{1a}',
2029 _ => *next,
2030 };
2031 s.push(n);
2032 chars.next(); }
2034 }
2035 }
2036 ch => {
2037 chars.next(); if ch == settings.quote_style {
2040 num_consecutive_quotes += 1;
2041 } else {
2042 num_consecutive_quotes = 0;
2043 }
2044
2045 s.push(ch);
2046 }
2047 }
2048 }
2049 self.tokenizer_error(error_loc, "Unterminated string literal")
2050 }
2051
2052 fn tokenize_multiline_comment(
2053 &self,
2054 chars: &mut State,
2055 ) -> Result<Option<Token>, TokenizerError> {
2056 let mut s = String::new();
2057 let mut nested = 1;
2058 let supports_nested_comments = self.dialect.supports_nested_comments();
2059
2060 loop {
2061 match chars.next() {
2062 Some('/') if matches!(chars.peek(), Some('*')) && supports_nested_comments => {
2063 chars.next(); s.push('/');
2065 s.push('*');
2066 nested += 1;
2067 }
2068 Some('*') if matches!(chars.peek(), Some('/')) => {
2069 chars.next(); nested -= 1;
2071 if nested == 0 {
2072 break Ok(Some(Token::Whitespace(Whitespace::MultiLineComment(s))));
2073 }
2074 s.push('*');
2075 s.push('/');
2076 }
2077 Some(ch) => {
2078 s.push(ch);
2079 }
2080 None => {
2081 break self.tokenizer_error(
2082 chars.location(),
2083 "Unexpected EOF while in a multi-line comment",
2084 );
2085 }
2086 }
2087 }
2088 }
2089
2090 fn parse_quoted_ident(&self, chars: &mut State, quote_end: char) -> (String, Option<char>) {
2091 let mut last_char = None;
2092 let mut s = String::new();
2093 while let Some(ch) = chars.next() {
2094 if ch == quote_end {
2095 if chars.peek() == Some("e_end) {
2096 chars.next();
2097 s.push(ch);
2098 if !self.unescape {
2099 s.push(ch);
2101 }
2102 } else {
2103 last_char = Some(quote_end);
2104 break;
2105 }
2106 } else {
2107 s.push(ch);
2108 }
2109 }
2110 (s, last_char)
2111 }
2112
2113 #[allow(clippy::unnecessary_wraps)]
2114 fn consume_and_return(
2115 &self,
2116 chars: &mut State,
2117 t: Token,
2118 ) -> Result<Option<Token>, TokenizerError> {
2119 chars.next();
2120 Ok(Some(t))
2121 }
2122}
2123
2124fn peeking_take_while(chars: &mut State, mut predicate: impl FnMut(char) -> bool) -> String {
2128 let mut s = String::new();
2129 while let Some(&ch) = chars.peek() {
2130 if predicate(ch) {
2131 chars.next(); s.push(ch);
2133 } else {
2134 break;
2135 }
2136 }
2137 s
2138}
2139
2140fn peeking_next_take_while(
2142 chars: &mut State,
2143 mut predicate: impl FnMut(char, Option<char>) -> bool,
2144) -> String {
2145 let mut s = String::new();
2146 while let Some(&ch) = chars.peek() {
2147 let next_char = chars.peekable.clone().nth(1);
2148 if predicate(ch, next_char) {
2149 chars.next(); s.push(ch);
2151 } else {
2152 break;
2153 }
2154 }
2155 s
2156}
2157
2158fn unescape_single_quoted_string(chars: &mut State<'_>) -> Option<String> {
2159 Unescape::new(chars).unescape()
2160}
2161
2162struct Unescape<'a: 'b, 'b> {
2163 chars: &'b mut State<'a>,
2164}
2165
2166impl<'a: 'b, 'b> Unescape<'a, 'b> {
2167 fn new(chars: &'b mut State<'a>) -> Self {
2168 Self { chars }
2169 }
2170 fn unescape(mut self) -> Option<String> {
2171 let mut unescaped = String::new();
2172
2173 self.chars.next();
2174
2175 while let Some(c) = self.chars.next() {
2176 if c == '\'' {
2177 if self.chars.peek().map(|c| *c == '\'').unwrap_or(false) {
2179 self.chars.next();
2180 unescaped.push('\'');
2181 continue;
2182 }
2183 return Some(unescaped);
2184 }
2185
2186 if c != '\\' {
2187 unescaped.push(c);
2188 continue;
2189 }
2190
2191 let c = match self.chars.next()? {
2192 'b' => '\u{0008}',
2193 'f' => '\u{000C}',
2194 'n' => '\n',
2195 'r' => '\r',
2196 't' => '\t',
2197 'u' => self.unescape_unicode_16()?,
2198 'U' => self.unescape_unicode_32()?,
2199 'x' => self.unescape_hex()?,
2200 c if c.is_digit(8) => self.unescape_octal(c)?,
2201 c => c,
2202 };
2203
2204 unescaped.push(Self::check_null(c)?);
2205 }
2206
2207 None
2208 }
2209
2210 #[inline]
2211 fn check_null(c: char) -> Option<char> {
2212 if c == '\0' {
2213 None
2214 } else {
2215 Some(c)
2216 }
2217 }
2218
2219 #[inline]
2220 fn byte_to_char<const RADIX: u32>(s: &str) -> Option<char> {
2221 match u32::from_str_radix(s, RADIX) {
2223 Err(_) => None,
2224 Ok(n) => {
2225 let n = n & 0xFF;
2226 if n <= 127 {
2227 char::from_u32(n)
2228 } else {
2229 None
2230 }
2231 }
2232 }
2233 }
2234
2235 fn unescape_hex(&mut self) -> Option<char> {
2237 let mut s = String::new();
2238
2239 for _ in 0..2 {
2240 match self.next_hex_digit() {
2241 Some(c) => s.push(c),
2242 None => break,
2243 }
2244 }
2245
2246 if s.is_empty() {
2247 return Some('x');
2248 }
2249
2250 Self::byte_to_char::<16>(&s)
2251 }
2252
2253 #[inline]
2254 fn next_hex_digit(&mut self) -> Option<char> {
2255 match self.chars.peek() {
2256 Some(c) if c.is_ascii_hexdigit() => self.chars.next(),
2257 _ => None,
2258 }
2259 }
2260
2261 fn unescape_octal(&mut self, c: char) -> Option<char> {
2263 let mut s = String::new();
2264
2265 s.push(c);
2266 for _ in 0..2 {
2267 match self.next_octal_digest() {
2268 Some(c) => s.push(c),
2269 None => break,
2270 }
2271 }
2272
2273 Self::byte_to_char::<8>(&s)
2274 }
2275
2276 #[inline]
2277 fn next_octal_digest(&mut self) -> Option<char> {
2278 match self.chars.peek() {
2279 Some(c) if c.is_digit(8) => self.chars.next(),
2280 _ => None,
2281 }
2282 }
2283
2284 fn unescape_unicode_16(&mut self) -> Option<char> {
2286 self.unescape_unicode::<4>()
2287 }
2288
2289 fn unescape_unicode_32(&mut self) -> Option<char> {
2291 self.unescape_unicode::<8>()
2292 }
2293
2294 fn unescape_unicode<const NUM: usize>(&mut self) -> Option<char> {
2295 let mut s = String::new();
2296 for _ in 0..NUM {
2297 s.push(self.chars.next()?);
2298 }
2299 match u32::from_str_radix(&s, 16) {
2300 Err(_) => None,
2301 Ok(n) => char::from_u32(n),
2302 }
2303 }
2304}
2305
2306fn unescape_unicode_single_quoted_string(chars: &mut State<'_>) -> Result<String, TokenizerError> {
2307 let mut unescaped = String::new();
2308 chars.next(); while let Some(c) = chars.next() {
2310 match c {
2311 '\'' => {
2312 if chars.peek() == Some(&'\'') {
2313 chars.next();
2314 unescaped.push('\'');
2315 } else {
2316 return Ok(unescaped);
2317 }
2318 }
2319 '\\' => match chars.peek() {
2320 Some('\\') => {
2321 chars.next();
2322 unescaped.push('\\');
2323 }
2324 Some('+') => {
2325 chars.next();
2326 unescaped.push(take_char_from_hex_digits(chars, 6)?);
2327 }
2328 _ => unescaped.push(take_char_from_hex_digits(chars, 4)?),
2329 },
2330 _ => {
2331 unescaped.push(c);
2332 }
2333 }
2334 }
2335 Err(TokenizerError {
2336 message: "Unterminated unicode encoded string literal".to_string(),
2337 location: chars.location(),
2338 })
2339}
2340
2341fn take_char_from_hex_digits(
2342 chars: &mut State<'_>,
2343 max_digits: usize,
2344) -> Result<char, TokenizerError> {
2345 let mut result = 0u32;
2346 for _ in 0..max_digits {
2347 let next_char = chars.next().ok_or_else(|| TokenizerError {
2348 message: "Unexpected EOF while parsing hex digit in escaped unicode string."
2349 .to_string(),
2350 location: chars.location(),
2351 })?;
2352 let digit = next_char.to_digit(16).ok_or_else(|| TokenizerError {
2353 message: format!("Invalid hex digit in escaped unicode string: {}", next_char),
2354 location: chars.location(),
2355 })?;
2356 result = result * 16 + digit;
2357 }
2358 char::from_u32(result).ok_or_else(|| TokenizerError {
2359 message: format!("Invalid unicode character: {:x}", result),
2360 location: chars.location(),
2361 })
2362}
2363
2364#[cfg(test)]
2365mod tests {
2366 use super::*;
2367 use crate::dialect::{
2368 BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, MySqlDialect, SQLiteDialect,
2369 };
2370 use crate::test_utils::all_dialects_where;
2371 use core::fmt::Debug;
2372
2373 #[test]
2374 fn tokenizer_error_impl() {
2375 let err = TokenizerError {
2376 message: "test".into(),
2377 location: Location { line: 1, column: 1 },
2378 };
2379 #[cfg(feature = "std")]
2380 {
2381 use std::error::Error;
2382 assert!(err.source().is_none());
2383 }
2384 assert_eq!(err.to_string(), "test at Line: 1, Column: 1");
2385 }
2386
2387 #[test]
2388 fn tokenize_select_1() {
2389 let sql = String::from("SELECT 1");
2390 let dialect = GenericDialect {};
2391 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2392
2393 let expected = vec![
2394 Token::make_keyword("SELECT"),
2395 Token::Whitespace(Whitespace::Space),
2396 Token::Number(String::from("1"), false),
2397 ];
2398
2399 compare(expected, tokens);
2400 }
2401
2402 #[test]
2403 fn tokenize_select_float() {
2404 let sql = String::from("SELECT .1");
2405 let dialect = GenericDialect {};
2406 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2407
2408 let expected = vec![
2409 Token::make_keyword("SELECT"),
2410 Token::Whitespace(Whitespace::Space),
2411 Token::Number(String::from(".1"), false),
2412 ];
2413
2414 compare(expected, tokens);
2415 }
2416
2417 #[test]
2418 fn tokenize_clickhouse_double_equal() {
2419 let sql = String::from("SELECT foo=='1'");
2420 let dialect = ClickHouseDialect {};
2421 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2422 let tokens = tokenizer.tokenize().unwrap();
2423
2424 let expected = vec![
2425 Token::make_keyword("SELECT"),
2426 Token::Whitespace(Whitespace::Space),
2427 Token::Word(Word {
2428 value: "foo".to_string(),
2429 quote_style: None,
2430 keyword: Keyword::NoKeyword,
2431 }),
2432 Token::DoubleEq,
2433 Token::SingleQuotedString("1".to_string()),
2434 ];
2435
2436 compare(expected, tokens);
2437 }
2438
2439 #[test]
2440 fn tokenize_numeric_literal_underscore() {
2441 let dialect = GenericDialect {};
2442 let sql = String::from("SELECT 10_000");
2443 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2444 let tokens = tokenizer.tokenize().unwrap();
2445 let expected = vec![
2446 Token::make_keyword("SELECT"),
2447 Token::Whitespace(Whitespace::Space),
2448 Token::Number("10".to_string(), false),
2449 Token::make_word("_000", None),
2450 ];
2451 compare(expected, tokens);
2452
2453 all_dialects_where(|dialect| dialect.supports_numeric_literal_underscores()).tokenizes_to(
2454 "SELECT 10_000, _10_000, 10_00_, 10___0",
2455 vec![
2456 Token::make_keyword("SELECT"),
2457 Token::Whitespace(Whitespace::Space),
2458 Token::Number("10_000".to_string(), false),
2459 Token::Comma,
2460 Token::Whitespace(Whitespace::Space),
2461 Token::make_word("_10_000", None), Token::Comma,
2463 Token::Whitespace(Whitespace::Space),
2464 Token::Number("10_00".to_string(), false),
2465 Token::make_word("_", None), Token::Comma,
2467 Token::Whitespace(Whitespace::Space),
2468 Token::Number("10".to_string(), false),
2469 Token::make_word("___0", None), ],
2471 );
2472 }
2473
2474 #[test]
2475 fn tokenize_select_exponent() {
2476 let sql = String::from("SELECT 1e10, 1e-10, 1e+10, 1ea, 1e-10a, 1e-10-10");
2477 let dialect = GenericDialect {};
2478 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2479
2480 let expected = vec![
2481 Token::make_keyword("SELECT"),
2482 Token::Whitespace(Whitespace::Space),
2483 Token::Number(String::from("1e10"), false),
2484 Token::Comma,
2485 Token::Whitespace(Whitespace::Space),
2486 Token::Number(String::from("1e-10"), false),
2487 Token::Comma,
2488 Token::Whitespace(Whitespace::Space),
2489 Token::Number(String::from("1e+10"), false),
2490 Token::Comma,
2491 Token::Whitespace(Whitespace::Space),
2492 Token::Number(String::from("1"), false),
2493 Token::make_word("ea", None),
2494 Token::Comma,
2495 Token::Whitespace(Whitespace::Space),
2496 Token::Number(String::from("1e-10"), false),
2497 Token::make_word("a", None),
2498 Token::Comma,
2499 Token::Whitespace(Whitespace::Space),
2500 Token::Number(String::from("1e-10"), false),
2501 Token::Minus,
2502 Token::Number(String::from("10"), false),
2503 ];
2504
2505 compare(expected, tokens);
2506 }
2507
2508 #[test]
2509 fn tokenize_scalar_function() {
2510 let sql = String::from("SELECT sqrt(1)");
2511 let dialect = GenericDialect {};
2512 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2513
2514 let expected = vec![
2515 Token::make_keyword("SELECT"),
2516 Token::Whitespace(Whitespace::Space),
2517 Token::make_word("sqrt", None),
2518 Token::LParen,
2519 Token::Number(String::from("1"), false),
2520 Token::RParen,
2521 ];
2522
2523 compare(expected, tokens);
2524 }
2525
2526 #[test]
2527 fn tokenize_string_string_concat() {
2528 let sql = String::from("SELECT 'a' || 'b'");
2529 let dialect = GenericDialect {};
2530 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2531
2532 let expected = vec![
2533 Token::make_keyword("SELECT"),
2534 Token::Whitespace(Whitespace::Space),
2535 Token::SingleQuotedString(String::from("a")),
2536 Token::Whitespace(Whitespace::Space),
2537 Token::StringConcat,
2538 Token::Whitespace(Whitespace::Space),
2539 Token::SingleQuotedString(String::from("b")),
2540 ];
2541
2542 compare(expected, tokens);
2543 }
2544 #[test]
2545 fn tokenize_bitwise_op() {
2546 let sql = String::from("SELECT one | two ^ three");
2547 let dialect = GenericDialect {};
2548 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2549
2550 let expected = vec![
2551 Token::make_keyword("SELECT"),
2552 Token::Whitespace(Whitespace::Space),
2553 Token::make_word("one", None),
2554 Token::Whitespace(Whitespace::Space),
2555 Token::Pipe,
2556 Token::Whitespace(Whitespace::Space),
2557 Token::make_word("two", None),
2558 Token::Whitespace(Whitespace::Space),
2559 Token::Caret,
2560 Token::Whitespace(Whitespace::Space),
2561 Token::make_word("three", None),
2562 ];
2563 compare(expected, tokens);
2564 }
2565
2566 #[test]
2567 fn tokenize_logical_xor() {
2568 let sql =
2569 String::from("SELECT true XOR true, false XOR false, true XOR false, false XOR true");
2570 let dialect = GenericDialect {};
2571 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2572
2573 let expected = vec![
2574 Token::make_keyword("SELECT"),
2575 Token::Whitespace(Whitespace::Space),
2576 Token::make_keyword("true"),
2577 Token::Whitespace(Whitespace::Space),
2578 Token::make_keyword("XOR"),
2579 Token::Whitespace(Whitespace::Space),
2580 Token::make_keyword("true"),
2581 Token::Comma,
2582 Token::Whitespace(Whitespace::Space),
2583 Token::make_keyword("false"),
2584 Token::Whitespace(Whitespace::Space),
2585 Token::make_keyword("XOR"),
2586 Token::Whitespace(Whitespace::Space),
2587 Token::make_keyword("false"),
2588 Token::Comma,
2589 Token::Whitespace(Whitespace::Space),
2590 Token::make_keyword("true"),
2591 Token::Whitespace(Whitespace::Space),
2592 Token::make_keyword("XOR"),
2593 Token::Whitespace(Whitespace::Space),
2594 Token::make_keyword("false"),
2595 Token::Comma,
2596 Token::Whitespace(Whitespace::Space),
2597 Token::make_keyword("false"),
2598 Token::Whitespace(Whitespace::Space),
2599 Token::make_keyword("XOR"),
2600 Token::Whitespace(Whitespace::Space),
2601 Token::make_keyword("true"),
2602 ];
2603 compare(expected, tokens);
2604 }
2605
2606 #[test]
2607 fn tokenize_simple_select() {
2608 let sql = String::from("SELECT * FROM customer WHERE id = 1 LIMIT 5");
2609 let dialect = GenericDialect {};
2610 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2611
2612 let expected = vec![
2613 Token::make_keyword("SELECT"),
2614 Token::Whitespace(Whitespace::Space),
2615 Token::Mul,
2616 Token::Whitespace(Whitespace::Space),
2617 Token::make_keyword("FROM"),
2618 Token::Whitespace(Whitespace::Space),
2619 Token::make_word("customer", None),
2620 Token::Whitespace(Whitespace::Space),
2621 Token::make_keyword("WHERE"),
2622 Token::Whitespace(Whitespace::Space),
2623 Token::make_word("id", None),
2624 Token::Whitespace(Whitespace::Space),
2625 Token::Eq,
2626 Token::Whitespace(Whitespace::Space),
2627 Token::Number(String::from("1"), false),
2628 Token::Whitespace(Whitespace::Space),
2629 Token::make_keyword("LIMIT"),
2630 Token::Whitespace(Whitespace::Space),
2631 Token::Number(String::from("5"), false),
2632 ];
2633
2634 compare(expected, tokens);
2635 }
2636
2637 #[test]
2638 fn tokenize_explain_select() {
2639 let sql = String::from("EXPLAIN SELECT * FROM customer WHERE id = 1");
2640 let dialect = GenericDialect {};
2641 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2642
2643 let expected = vec![
2644 Token::make_keyword("EXPLAIN"),
2645 Token::Whitespace(Whitespace::Space),
2646 Token::make_keyword("SELECT"),
2647 Token::Whitespace(Whitespace::Space),
2648 Token::Mul,
2649 Token::Whitespace(Whitespace::Space),
2650 Token::make_keyword("FROM"),
2651 Token::Whitespace(Whitespace::Space),
2652 Token::make_word("customer", None),
2653 Token::Whitespace(Whitespace::Space),
2654 Token::make_keyword("WHERE"),
2655 Token::Whitespace(Whitespace::Space),
2656 Token::make_word("id", None),
2657 Token::Whitespace(Whitespace::Space),
2658 Token::Eq,
2659 Token::Whitespace(Whitespace::Space),
2660 Token::Number(String::from("1"), false),
2661 ];
2662
2663 compare(expected, tokens);
2664 }
2665
2666 #[test]
2667 fn tokenize_explain_analyze_select() {
2668 let sql = String::from("EXPLAIN ANALYZE SELECT * FROM customer WHERE id = 1");
2669 let dialect = GenericDialect {};
2670 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2671
2672 let expected = vec![
2673 Token::make_keyword("EXPLAIN"),
2674 Token::Whitespace(Whitespace::Space),
2675 Token::make_keyword("ANALYZE"),
2676 Token::Whitespace(Whitespace::Space),
2677 Token::make_keyword("SELECT"),
2678 Token::Whitespace(Whitespace::Space),
2679 Token::Mul,
2680 Token::Whitespace(Whitespace::Space),
2681 Token::make_keyword("FROM"),
2682 Token::Whitespace(Whitespace::Space),
2683 Token::make_word("customer", None),
2684 Token::Whitespace(Whitespace::Space),
2685 Token::make_keyword("WHERE"),
2686 Token::Whitespace(Whitespace::Space),
2687 Token::make_word("id", None),
2688 Token::Whitespace(Whitespace::Space),
2689 Token::Eq,
2690 Token::Whitespace(Whitespace::Space),
2691 Token::Number(String::from("1"), false),
2692 ];
2693
2694 compare(expected, tokens);
2695 }
2696
2697 #[test]
2698 fn tokenize_string_predicate() {
2699 let sql = String::from("SELECT * FROM customer WHERE salary != 'Not Provided'");
2700 let dialect = GenericDialect {};
2701 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2702
2703 let expected = vec![
2704 Token::make_keyword("SELECT"),
2705 Token::Whitespace(Whitespace::Space),
2706 Token::Mul,
2707 Token::Whitespace(Whitespace::Space),
2708 Token::make_keyword("FROM"),
2709 Token::Whitespace(Whitespace::Space),
2710 Token::make_word("customer", None),
2711 Token::Whitespace(Whitespace::Space),
2712 Token::make_keyword("WHERE"),
2713 Token::Whitespace(Whitespace::Space),
2714 Token::make_word("salary", None),
2715 Token::Whitespace(Whitespace::Space),
2716 Token::Neq,
2717 Token::Whitespace(Whitespace::Space),
2718 Token::SingleQuotedString(String::from("Not Provided")),
2719 ];
2720
2721 compare(expected, tokens);
2722 }
2723
2724 #[test]
2725 fn tokenize_invalid_string() {
2726 let sql = String::from("\n💝مصطفىh");
2727
2728 let dialect = GenericDialect {};
2729 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2730 let expected = vec![
2732 Token::Whitespace(Whitespace::Newline),
2733 Token::Char('💝'),
2734 Token::make_word("مصطفىh", None),
2735 ];
2736 compare(expected, tokens);
2737 }
2738
2739 #[test]
2740 fn tokenize_newline_in_string_literal() {
2741 let sql = String::from("'foo\r\nbar\nbaz'");
2742
2743 let dialect = GenericDialect {};
2744 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2745 let expected = vec![Token::SingleQuotedString("foo\r\nbar\nbaz".to_string())];
2746 compare(expected, tokens);
2747 }
2748
2749 #[test]
2750 fn tokenize_unterminated_string_literal() {
2751 let sql = String::from("select 'foo");
2752
2753 let dialect = GenericDialect {};
2754 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2755 assert_eq!(
2756 tokenizer.tokenize(),
2757 Err(TokenizerError {
2758 message: "Unterminated string literal".to_string(),
2759 location: Location { line: 1, column: 8 },
2760 })
2761 );
2762 }
2763
2764 #[test]
2765 fn tokenize_unterminated_string_literal_utf8() {
2766 let sql = String::from("SELECT \"なにか\" FROM Y WHERE \"なにか\" = 'test;");
2767
2768 let dialect = GenericDialect {};
2769 let mut tokenizer = Tokenizer::new(&dialect, &sql);
2770 assert_eq!(
2771 tokenizer.tokenize(),
2772 Err(TokenizerError {
2773 message: "Unterminated string literal".to_string(),
2774 location: Location {
2775 line: 1,
2776 column: 35
2777 }
2778 })
2779 );
2780 }
2781
2782 #[test]
2783 fn tokenize_invalid_string_cols() {
2784 let sql = String::from("\n\nSELECT * FROM table\t💝مصطفىh");
2785
2786 let dialect = GenericDialect {};
2787 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2788 let expected = vec![
2790 Token::Whitespace(Whitespace::Newline),
2791 Token::Whitespace(Whitespace::Newline),
2792 Token::make_keyword("SELECT"),
2793 Token::Whitespace(Whitespace::Space),
2794 Token::Mul,
2795 Token::Whitespace(Whitespace::Space),
2796 Token::make_keyword("FROM"),
2797 Token::Whitespace(Whitespace::Space),
2798 Token::make_keyword("table"),
2799 Token::Whitespace(Whitespace::Tab),
2800 Token::Char('💝'),
2801 Token::make_word("مصطفىh", None),
2802 ];
2803 compare(expected, tokens);
2804 }
2805
2806 #[test]
2807 fn tokenize_dollar_quoted_string_tagged() {
2808 let test_cases = vec![
2809 (
2810 String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$tag$"),
2811 vec![
2812 Token::make_keyword("SELECT"),
2813 Token::Whitespace(Whitespace::Space),
2814 Token::DollarQuotedString(DollarQuotedString {
2815 value: "dollar '$' quoted strings have $tags like this$ or like this $$".into(),
2816 tag: Some("tag".into()),
2817 })
2818 ]
2819 ),
2820 (
2821 String::from("SELECT $abc$x$ab$abc$"),
2822 vec![
2823 Token::make_keyword("SELECT"),
2824 Token::Whitespace(Whitespace::Space),
2825 Token::DollarQuotedString(DollarQuotedString {
2826 value: "x$ab".into(),
2827 tag: Some("abc".into()),
2828 })
2829 ]
2830 ),
2831 (
2832 String::from("SELECT $abc$$abc$"),
2833 vec![
2834 Token::make_keyword("SELECT"),
2835 Token::Whitespace(Whitespace::Space),
2836 Token::DollarQuotedString(DollarQuotedString {
2837 value: "".into(),
2838 tag: Some("abc".into()),
2839 })
2840 ]
2841 ),
2842 (
2843 String::from("0$abc$$abc$1"),
2844 vec![
2845 Token::Number("0".into(), false),
2846 Token::DollarQuotedString(DollarQuotedString {
2847 value: "".into(),
2848 tag: Some("abc".into()),
2849 }),
2850 Token::Number("1".into(), false),
2851 ]
2852 ),
2853 (
2854 String::from("$function$abc$q$data$q$$function$"),
2855 vec![
2856 Token::DollarQuotedString(DollarQuotedString {
2857 value: "abc$q$data$q$".into(),
2858 tag: Some("function".into()),
2859 }),
2860 ]
2861 ),
2862 ];
2863
2864 let dialect = GenericDialect {};
2865 for (sql, expected) in test_cases {
2866 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2867 compare(expected, tokens);
2868 }
2869 }
2870
2871 #[test]
2872 fn tokenize_dollar_quoted_string_tagged_unterminated() {
2873 let sql = String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$different tag$");
2874 let dialect = GenericDialect {};
2875 assert_eq!(
2876 Tokenizer::new(&dialect, &sql).tokenize(),
2877 Err(TokenizerError {
2878 message: "Unterminated dollar-quoted, expected $".into(),
2879 location: Location {
2880 line: 1,
2881 column: 91
2882 }
2883 })
2884 );
2885 }
2886
2887 #[test]
2888 fn tokenize_dollar_quoted_string_tagged_unterminated_mirror() {
2889 let sql = String::from("SELECT $abc$abc$");
2890 let dialect = GenericDialect {};
2891 assert_eq!(
2892 Tokenizer::new(&dialect, &sql).tokenize(),
2893 Err(TokenizerError {
2894 message: "Unterminated dollar-quoted, expected $".into(),
2895 location: Location {
2896 line: 1,
2897 column: 17
2898 }
2899 })
2900 );
2901 }
2902
2903 #[test]
2904 fn tokenize_dollar_placeholder() {
2905 let sql = String::from("SELECT $$, $$ABC$$, $ABC$, $ABC");
2906 let dialect = SQLiteDialect {};
2907 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2908 assert_eq!(
2909 tokens,
2910 vec![
2911 Token::make_keyword("SELECT"),
2912 Token::Whitespace(Whitespace::Space),
2913 Token::Placeholder("$$".into()),
2914 Token::Comma,
2915 Token::Whitespace(Whitespace::Space),
2916 Token::Placeholder("$$ABC$$".into()),
2917 Token::Comma,
2918 Token::Whitespace(Whitespace::Space),
2919 Token::Placeholder("$ABC$".into()),
2920 Token::Comma,
2921 Token::Whitespace(Whitespace::Space),
2922 Token::Placeholder("$ABC".into()),
2923 ]
2924 );
2925 }
2926
2927 #[test]
2928 fn tokenize_nested_dollar_quoted_strings() {
2929 let sql = String::from("SELECT $tag$dollar $nested$ string$tag$");
2930 let dialect = GenericDialect {};
2931 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2932 let expected = vec![
2933 Token::make_keyword("SELECT"),
2934 Token::Whitespace(Whitespace::Space),
2935 Token::DollarQuotedString(DollarQuotedString {
2936 value: "dollar $nested$ string".into(),
2937 tag: Some("tag".into()),
2938 }),
2939 ];
2940 compare(expected, tokens);
2941 }
2942
2943 #[test]
2944 fn tokenize_dollar_quoted_string_untagged_empty() {
2945 let sql = String::from("SELECT $$$$");
2946 let dialect = GenericDialect {};
2947 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2948 let expected = vec![
2949 Token::make_keyword("SELECT"),
2950 Token::Whitespace(Whitespace::Space),
2951 Token::DollarQuotedString(DollarQuotedString {
2952 value: "".into(),
2953 tag: None,
2954 }),
2955 ];
2956 compare(expected, tokens);
2957 }
2958
2959 #[test]
2960 fn tokenize_dollar_quoted_string_untagged() {
2961 let sql =
2962 String::from("SELECT $$within dollar '$' quoted strings have $tags like this$ $$");
2963 let dialect = GenericDialect {};
2964 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2965 let expected = vec![
2966 Token::make_keyword("SELECT"),
2967 Token::Whitespace(Whitespace::Space),
2968 Token::DollarQuotedString(DollarQuotedString {
2969 value: "within dollar '$' quoted strings have $tags like this$ ".into(),
2970 tag: None,
2971 }),
2972 ];
2973 compare(expected, tokens);
2974 }
2975
2976 #[test]
2977 fn tokenize_dollar_quoted_string_untagged_unterminated() {
2978 let sql = String::from(
2979 "SELECT $$dollar '$' quoted strings have $tags like this$ or like this $different tag$",
2980 );
2981 let dialect = GenericDialect {};
2982 assert_eq!(
2983 Tokenizer::new(&dialect, &sql).tokenize(),
2984 Err(TokenizerError {
2985 message: "Unterminated dollar-quoted string".into(),
2986 location: Location {
2987 line: 1,
2988 column: 86
2989 }
2990 })
2991 );
2992 }
2993
2994 #[test]
2995 fn tokenize_right_arrow() {
2996 let sql = String::from("FUNCTION(key=>value)");
2997 let dialect = GenericDialect {};
2998 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2999 let expected = vec![
3000 Token::make_word("FUNCTION", None),
3001 Token::LParen,
3002 Token::make_word("key", None),
3003 Token::RArrow,
3004 Token::make_word("value", None),
3005 Token::RParen,
3006 ];
3007 compare(expected, tokens);
3008 }
3009
3010 #[test]
3011 fn tokenize_is_null() {
3012 let sql = String::from("a IS NULL");
3013 let dialect = GenericDialect {};
3014 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3015
3016 let expected = vec![
3017 Token::make_word("a", None),
3018 Token::Whitespace(Whitespace::Space),
3019 Token::make_keyword("IS"),
3020 Token::Whitespace(Whitespace::Space),
3021 Token::make_keyword("NULL"),
3022 ];
3023
3024 compare(expected, tokens);
3025 }
3026
3027 #[test]
3028 fn tokenize_comment() {
3029 let test_cases = vec![
3030 (
3031 String::from("0--this is a comment\n1"),
3032 vec![
3033 Token::Number("0".to_string(), false),
3034 Token::Whitespace(Whitespace::SingleLineComment {
3035 prefix: "--".to_string(),
3036 comment: "this is a comment\n".to_string(),
3037 }),
3038 Token::Number("1".to_string(), false),
3039 ],
3040 ),
3041 (
3042 String::from("0--this is a comment\r1"),
3043 vec![
3044 Token::Number("0".to_string(), false),
3045 Token::Whitespace(Whitespace::SingleLineComment {
3046 prefix: "--".to_string(),
3047 comment: "this is a comment\r1".to_string(),
3048 }),
3049 ],
3050 ),
3051 (
3052 String::from("0--this is a comment\r\n1"),
3053 vec![
3054 Token::Number("0".to_string(), false),
3055 Token::Whitespace(Whitespace::SingleLineComment {
3056 prefix: "--".to_string(),
3057 comment: "this is a comment\r\n".to_string(),
3058 }),
3059 Token::Number("1".to_string(), false),
3060 ],
3061 ),
3062 ];
3063
3064 let dialect = GenericDialect {};
3065
3066 for (sql, expected) in test_cases {
3067 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3068 compare(expected, tokens);
3069 }
3070 }
3071
3072 #[test]
3073 fn tokenize_comment_postgres() {
3074 let sql = String::from("1--\r0");
3075
3076 let dialect = PostgreSqlDialect {};
3077 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3078 let expected = vec![
3079 Token::Number("1".to_string(), false),
3080 Token::Whitespace(Whitespace::SingleLineComment {
3081 prefix: "--".to_string(),
3082 comment: "\r".to_string(),
3083 }),
3084 Token::Number("0".to_string(), false),
3085 ];
3086 compare(expected, tokens);
3087 }
3088
3089 #[test]
3090 fn tokenize_comment_at_eof() {
3091 let sql = String::from("--this is a comment");
3092
3093 let dialect = GenericDialect {};
3094 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3095 let expected = vec![Token::Whitespace(Whitespace::SingleLineComment {
3096 prefix: "--".to_string(),
3097 comment: "this is a comment".to_string(),
3098 })];
3099 compare(expected, tokens);
3100 }
3101
3102 #[test]
3103 fn tokenize_multiline_comment() {
3104 let sql = String::from("0/*multi-line\n* /comment*/1");
3105
3106 let dialect = GenericDialect {};
3107 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3108 let expected = vec![
3109 Token::Number("0".to_string(), false),
3110 Token::Whitespace(Whitespace::MultiLineComment(
3111 "multi-line\n* /comment".to_string(),
3112 )),
3113 Token::Number("1".to_string(), false),
3114 ];
3115 compare(expected, tokens);
3116 }
3117
3118 #[test]
3119 fn tokenize_nested_multiline_comment() {
3120 let dialect = GenericDialect {};
3121 let test_cases = vec![
3122 (
3123 "0/*multi-line\n* \n/* comment \n /*comment*/*/ */ /comment*/1",
3124 vec![
3125 Token::Number("0".to_string(), false),
3126 Token::Whitespace(Whitespace::MultiLineComment(
3127 "multi-line\n* \n/* comment \n /*comment*/*/ ".into(),
3128 )),
3129 Token::Whitespace(Whitespace::Space),
3130 Token::Div,
3131 Token::Word(Word {
3132 value: "comment".to_string(),
3133 quote_style: None,
3134 keyword: Keyword::COMMENT,
3135 }),
3136 Token::Mul,
3137 Token::Div,
3138 Token::Number("1".to_string(), false),
3139 ],
3140 ),
3141 (
3142 "0/*multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/*/1",
3143 vec![
3144 Token::Number("0".to_string(), false),
3145 Token::Whitespace(Whitespace::MultiLineComment(
3146 "multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/".into(),
3147 )),
3148 Token::Number("1".to_string(), false),
3149 ],
3150 ),
3151 (
3152 "SELECT 1/* a /* b */ c */0",
3153 vec![
3154 Token::make_keyword("SELECT"),
3155 Token::Whitespace(Whitespace::Space),
3156 Token::Number("1".to_string(), false),
3157 Token::Whitespace(Whitespace::MultiLineComment(" a /* b */ c ".to_string())),
3158 Token::Number("0".to_string(), false),
3159 ],
3160 ),
3161 ];
3162
3163 for (sql, expected) in test_cases {
3164 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3165 compare(expected, tokens);
3166 }
3167 }
3168
3169 #[test]
3170 fn tokenize_nested_multiline_comment_empty() {
3171 let sql = "select 1/*/**/*/0";
3172
3173 let dialect = GenericDialect {};
3174 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3175 let expected = vec![
3176 Token::make_keyword("select"),
3177 Token::Whitespace(Whitespace::Space),
3178 Token::Number("1".to_string(), false),
3179 Token::Whitespace(Whitespace::MultiLineComment("/**/".to_string())),
3180 Token::Number("0".to_string(), false),
3181 ];
3182
3183 compare(expected, tokens);
3184 }
3185
3186 #[test]
3187 fn tokenize_nested_comments_if_not_supported() {
3188 let dialect = SQLiteDialect {};
3189 let sql = "SELECT 1/*/* nested comment */*/0";
3190 let tokens = Tokenizer::new(&dialect, sql).tokenize();
3191 let expected = vec![
3192 Token::make_keyword("SELECT"),
3193 Token::Whitespace(Whitespace::Space),
3194 Token::Number("1".to_string(), false),
3195 Token::Whitespace(Whitespace::MultiLineComment(
3196 "/* nested comment ".to_string(),
3197 )),
3198 Token::Mul,
3199 Token::Div,
3200 Token::Number("0".to_string(), false),
3201 ];
3202
3203 compare(expected, tokens.unwrap());
3204 }
3205
3206 #[test]
3207 fn tokenize_multiline_comment_with_even_asterisks() {
3208 let sql = String::from("\n/** Comment **/\n");
3209
3210 let dialect = GenericDialect {};
3211 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3212 let expected = vec![
3213 Token::Whitespace(Whitespace::Newline),
3214 Token::Whitespace(Whitespace::MultiLineComment("* Comment *".to_string())),
3215 Token::Whitespace(Whitespace::Newline),
3216 ];
3217 compare(expected, tokens);
3218 }
3219
3220 #[test]
3221 fn tokenize_unicode_whitespace() {
3222 let sql = String::from(" \u{2003}\n");
3223
3224 let dialect = GenericDialect {};
3225 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3226 let expected = vec![
3227 Token::Whitespace(Whitespace::Space),
3228 Token::Whitespace(Whitespace::Space),
3229 Token::Whitespace(Whitespace::Newline),
3230 ];
3231 compare(expected, tokens);
3232 }
3233
3234 #[test]
3235 fn tokenize_mismatched_quotes() {
3236 let sql = String::from("\"foo");
3237
3238 let dialect = GenericDialect {};
3239 let mut tokenizer = Tokenizer::new(&dialect, &sql);
3240 assert_eq!(
3241 tokenizer.tokenize(),
3242 Err(TokenizerError {
3243 message: "Expected close delimiter '\"' before EOF.".to_string(),
3244 location: Location { line: 1, column: 1 },
3245 })
3246 );
3247 }
3248
3249 #[test]
3250 fn tokenize_newlines() {
3251 let sql = String::from("line1\nline2\rline3\r\nline4\r");
3252
3253 let dialect = GenericDialect {};
3254 let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3255 let expected = vec![
3256 Token::make_word("line1", None),
3257 Token::Whitespace(Whitespace::Newline),
3258 Token::make_word("line2", None),
3259 Token::Whitespace(Whitespace::Newline),
3260 Token::make_word("line3", None),
3261 Token::Whitespace(Whitespace::Newline),
3262 Token::make_word("line4", None),
3263 Token::Whitespace(Whitespace::Newline),
3264 ];
3265 compare(expected, tokens);
3266 }
3267
3268 #[test]
3269 fn tokenize_mssql_top() {
3270 let sql = "SELECT TOP 5 [bar] FROM foo";
3271 let dialect = MsSqlDialect {};
3272 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3273 let expected = vec![
3274 Token::make_keyword("SELECT"),
3275 Token::Whitespace(Whitespace::Space),
3276 Token::make_keyword("TOP"),
3277 Token::Whitespace(Whitespace::Space),
3278 Token::Number(String::from("5"), false),
3279 Token::Whitespace(Whitespace::Space),
3280 Token::make_word("bar", Some('[')),
3281 Token::Whitespace(Whitespace::Space),
3282 Token::make_keyword("FROM"),
3283 Token::Whitespace(Whitespace::Space),
3284 Token::make_word("foo", None),
3285 ];
3286 compare(expected, tokens);
3287 }
3288
3289 #[test]
3290 fn tokenize_pg_regex_match() {
3291 let sql = "SELECT col ~ '^a', col ~* '^a', col !~ '^a', col !~* '^a'";
3292 let dialect = GenericDialect {};
3293 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3294 let expected = vec![
3295 Token::make_keyword("SELECT"),
3296 Token::Whitespace(Whitespace::Space),
3297 Token::make_word("col", None),
3298 Token::Whitespace(Whitespace::Space),
3299 Token::Tilde,
3300 Token::Whitespace(Whitespace::Space),
3301 Token::SingleQuotedString("^a".into()),
3302 Token::Comma,
3303 Token::Whitespace(Whitespace::Space),
3304 Token::make_word("col", None),
3305 Token::Whitespace(Whitespace::Space),
3306 Token::TildeAsterisk,
3307 Token::Whitespace(Whitespace::Space),
3308 Token::SingleQuotedString("^a".into()),
3309 Token::Comma,
3310 Token::Whitespace(Whitespace::Space),
3311 Token::make_word("col", None),
3312 Token::Whitespace(Whitespace::Space),
3313 Token::ExclamationMarkTilde,
3314 Token::Whitespace(Whitespace::Space),
3315 Token::SingleQuotedString("^a".into()),
3316 Token::Comma,
3317 Token::Whitespace(Whitespace::Space),
3318 Token::make_word("col", None),
3319 Token::Whitespace(Whitespace::Space),
3320 Token::ExclamationMarkTildeAsterisk,
3321 Token::Whitespace(Whitespace::Space),
3322 Token::SingleQuotedString("^a".into()),
3323 ];
3324 compare(expected, tokens);
3325 }
3326
3327 #[test]
3328 fn tokenize_pg_like_match() {
3329 let sql = "SELECT col ~~ '_a%', col ~~* '_a%', col !~~ '_a%', col !~~* '_a%'";
3330 let dialect = GenericDialect {};
3331 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3332 let expected = vec![
3333 Token::make_keyword("SELECT"),
3334 Token::Whitespace(Whitespace::Space),
3335 Token::make_word("col", None),
3336 Token::Whitespace(Whitespace::Space),
3337 Token::DoubleTilde,
3338 Token::Whitespace(Whitespace::Space),
3339 Token::SingleQuotedString("_a%".into()),
3340 Token::Comma,
3341 Token::Whitespace(Whitespace::Space),
3342 Token::make_word("col", None),
3343 Token::Whitespace(Whitespace::Space),
3344 Token::DoubleTildeAsterisk,
3345 Token::Whitespace(Whitespace::Space),
3346 Token::SingleQuotedString("_a%".into()),
3347 Token::Comma,
3348 Token::Whitespace(Whitespace::Space),
3349 Token::make_word("col", None),
3350 Token::Whitespace(Whitespace::Space),
3351 Token::ExclamationMarkDoubleTilde,
3352 Token::Whitespace(Whitespace::Space),
3353 Token::SingleQuotedString("_a%".into()),
3354 Token::Comma,
3355 Token::Whitespace(Whitespace::Space),
3356 Token::make_word("col", None),
3357 Token::Whitespace(Whitespace::Space),
3358 Token::ExclamationMarkDoubleTildeAsterisk,
3359 Token::Whitespace(Whitespace::Space),
3360 Token::SingleQuotedString("_a%".into()),
3361 ];
3362 compare(expected, tokens);
3363 }
3364
3365 #[test]
3366 fn tokenize_quoted_identifier() {
3367 let sql = r#" "a "" b" "a """ "c """"" "#;
3368 let dialect = GenericDialect {};
3369 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3370 let expected = vec![
3371 Token::Whitespace(Whitespace::Space),
3372 Token::make_word(r#"a " b"#, Some('"')),
3373 Token::Whitespace(Whitespace::Space),
3374 Token::make_word(r#"a ""#, Some('"')),
3375 Token::Whitespace(Whitespace::Space),
3376 Token::make_word(r#"c """#, Some('"')),
3377 Token::Whitespace(Whitespace::Space),
3378 ];
3379 compare(expected, tokens);
3380 }
3381
3382 #[test]
3383 fn tokenize_snowflake_div() {
3384 let sql = r#"field/1000"#;
3385 let dialect = SnowflakeDialect {};
3386 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3387 let expected = vec![
3388 Token::make_word(r#"field"#, None),
3389 Token::Div,
3390 Token::Number("1000".to_string(), false),
3391 ];
3392 compare(expected, tokens);
3393 }
3394
3395 #[test]
3396 fn tokenize_quoted_identifier_with_no_escape() {
3397 let sql = r#" "a "" b" "a """ "c """"" "#;
3398 let dialect = GenericDialect {};
3399 let tokens = Tokenizer::new(&dialect, sql)
3400 .with_unescape(false)
3401 .tokenize()
3402 .unwrap();
3403 let expected = vec![
3404 Token::Whitespace(Whitespace::Space),
3405 Token::make_word(r#"a "" b"#, Some('"')),
3406 Token::Whitespace(Whitespace::Space),
3407 Token::make_word(r#"a """#, Some('"')),
3408 Token::Whitespace(Whitespace::Space),
3409 Token::make_word(r#"c """""#, Some('"')),
3410 Token::Whitespace(Whitespace::Space),
3411 ];
3412 compare(expected, tokens);
3413 }
3414
3415 #[test]
3416 fn tokenize_with_location() {
3417 let sql = "SELECT a,\n b";
3418 let dialect = GenericDialect {};
3419 let tokens = Tokenizer::new(&dialect, sql)
3420 .tokenize_with_location()
3421 .unwrap();
3422 let expected = vec![
3423 TokenWithSpan::at(Token::make_keyword("SELECT"), (1, 1).into(), (1, 7).into()),
3424 TokenWithSpan::at(
3425 Token::Whitespace(Whitespace::Space),
3426 (1, 7).into(),
3427 (1, 8).into(),
3428 ),
3429 TokenWithSpan::at(Token::make_word("a", None), (1, 8).into(), (1, 9).into()),
3430 TokenWithSpan::at(Token::Comma, (1, 9).into(), (1, 10).into()),
3431 TokenWithSpan::at(
3432 Token::Whitespace(Whitespace::Newline),
3433 (1, 10).into(),
3434 (2, 1).into(),
3435 ),
3436 TokenWithSpan::at(
3437 Token::Whitespace(Whitespace::Space),
3438 (2, 1).into(),
3439 (2, 2).into(),
3440 ),
3441 TokenWithSpan::at(Token::make_word("b", None), (2, 2).into(), (2, 3).into()),
3442 ];
3443 compare(expected, tokens);
3444 }
3445
3446 fn compare<T: PartialEq + fmt::Debug>(expected: Vec<T>, actual: Vec<T>) {
3447 assert_eq!(expected, actual);
3452 }
3453
3454 fn check_unescape(s: &str, expected: Option<&str>) {
3455 let s = format!("'{}'", s);
3456 let mut state = State {
3457 peekable: s.chars().peekable(),
3458 line: 0,
3459 col: 0,
3460 };
3461
3462 assert_eq!(
3463 unescape_single_quoted_string(&mut state),
3464 expected.map(|s| s.to_string())
3465 );
3466 }
3467
3468 #[test]
3469 fn test_unescape() {
3470 check_unescape(r"\b", Some("\u{0008}"));
3471 check_unescape(r"\f", Some("\u{000C}"));
3472 check_unescape(r"\t", Some("\t"));
3473 check_unescape(r"\r\n", Some("\r\n"));
3474 check_unescape(r"\/", Some("/"));
3475 check_unescape(r"/", Some("/"));
3476 check_unescape(r"\\", Some("\\"));
3477
3478 check_unescape(r"\u0001", Some("\u{0001}"));
3480 check_unescape(r"\u4c91", Some("\u{4c91}"));
3481 check_unescape(r"\u4c916", Some("\u{4c91}6"));
3482 check_unescape(r"\u4c", None);
3483 check_unescape(r"\u0000", None);
3484 check_unescape(r"\U0010FFFF", Some("\u{10FFFF}"));
3485 check_unescape(r"\U00110000", None);
3486 check_unescape(r"\U00000000", None);
3487 check_unescape(r"\u", None);
3488 check_unescape(r"\U", None);
3489 check_unescape(r"\U1010FFFF", None);
3490
3491 check_unescape(r"\x4B", Some("\u{004b}"));
3493 check_unescape(r"\x4", Some("\u{0004}"));
3494 check_unescape(r"\x4L", Some("\u{0004}L"));
3495 check_unescape(r"\x", Some("x"));
3496 check_unescape(r"\xP", Some("xP"));
3497 check_unescape(r"\x0", None);
3498 check_unescape(r"\xCAD", None);
3499 check_unescape(r"\xA9", None);
3500
3501 check_unescape(r"\1", Some("\u{0001}"));
3503 check_unescape(r"\12", Some("\u{000a}"));
3504 check_unescape(r"\123", Some("\u{0053}"));
3505 check_unescape(r"\1232", Some("\u{0053}2"));
3506 check_unescape(r"\4", Some("\u{0004}"));
3507 check_unescape(r"\45", Some("\u{0025}"));
3508 check_unescape(r"\450", Some("\u{0028}"));
3509 check_unescape(r"\603", None);
3510 check_unescape(r"\0", None);
3511 check_unescape(r"\080", None);
3512
3513 check_unescape(r"\9", Some("9"));
3515 check_unescape(r"''", Some("'"));
3516 check_unescape(
3517 r"Hello\r\nRust/\u4c91 SQL Parser\U0010ABCD\1232",
3518 Some("Hello\r\nRust/\u{4c91} SQL Parser\u{10abcd}\u{0053}2"),
3519 );
3520 check_unescape(r"Hello\0", None);
3521 check_unescape(r"Hello\xCADRust", None);
3522 }
3523
3524 #[test]
3525 fn tokenize_numeric_prefix_trait() {
3526 #[derive(Debug)]
3527 struct NumericPrefixDialect;
3528
3529 impl Dialect for NumericPrefixDialect {
3530 fn is_identifier_start(&self, ch: char) -> bool {
3531 ch.is_ascii_lowercase()
3532 || ch.is_ascii_uppercase()
3533 || ch.is_ascii_digit()
3534 || ch == '$'
3535 }
3536
3537 fn is_identifier_part(&self, ch: char) -> bool {
3538 ch.is_ascii_lowercase()
3539 || ch.is_ascii_uppercase()
3540 || ch.is_ascii_digit()
3541 || ch == '_'
3542 || ch == '$'
3543 || ch == '{'
3544 || ch == '}'
3545 }
3546
3547 fn supports_numeric_prefix(&self) -> bool {
3548 true
3549 }
3550 }
3551
3552 tokenize_numeric_prefix_inner(&NumericPrefixDialect {});
3553 tokenize_numeric_prefix_inner(&HiveDialect {});
3554 tokenize_numeric_prefix_inner(&MySqlDialect {});
3555 }
3556
3557 fn tokenize_numeric_prefix_inner(dialect: &dyn Dialect) {
3558 let sql = r#"SELECT * FROM 1"#;
3559 let tokens = Tokenizer::new(dialect, sql).tokenize().unwrap();
3560 let expected = vec![
3561 Token::make_keyword("SELECT"),
3562 Token::Whitespace(Whitespace::Space),
3563 Token::Mul,
3564 Token::Whitespace(Whitespace::Space),
3565 Token::make_keyword("FROM"),
3566 Token::Whitespace(Whitespace::Space),
3567 Token::Number(String::from("1"), false),
3568 ];
3569 compare(expected, tokens);
3570 }
3571
3572 #[test]
3573 fn tokenize_quoted_string_escape() {
3574 let dialect = SnowflakeDialect {};
3575 for (sql, expected, expected_unescaped) in [
3576 (r#"'%a\'%b'"#, r#"%a\'%b"#, r#"%a'%b"#),
3577 (r#"'a\'\'b\'c\'d'"#, r#"a\'\'b\'c\'d"#, r#"a''b'c'd"#),
3578 (r#"'\\'"#, r#"\\"#, r#"\"#),
3579 (
3580 r#"'\0\a\b\f\n\r\t\Z'"#,
3581 r#"\0\a\b\f\n\r\t\Z"#,
3582 "\0\u{7}\u{8}\u{c}\n\r\t\u{1a}",
3583 ),
3584 (r#"'\"'"#, r#"\""#, "\""),
3585 (r#"'\\a\\b\'c'"#, r#"\\a\\b\'c"#, r#"\a\b'c"#),
3586 (r#"'\'abcd'"#, r#"\'abcd"#, r#"'abcd"#),
3587 (r#"'''a''b'"#, r#"''a''b"#, r#"'a'b"#),
3588 ] {
3589 let tokens = Tokenizer::new(&dialect, sql)
3590 .with_unescape(false)
3591 .tokenize()
3592 .unwrap();
3593 let expected = vec![Token::SingleQuotedString(expected.to_string())];
3594 compare(expected, tokens);
3595
3596 let tokens = Tokenizer::new(&dialect, sql)
3597 .with_unescape(true)
3598 .tokenize()
3599 .unwrap();
3600 let expected = vec![Token::SingleQuotedString(expected_unescaped.to_string())];
3601 compare(expected, tokens);
3602 }
3603
3604 for sql in [r#"'\'"#, r#"'ab\'"#] {
3605 let mut tokenizer = Tokenizer::new(&dialect, sql);
3606 assert_eq!(
3607 "Unterminated string literal",
3608 tokenizer.tokenize().unwrap_err().message.as_str(),
3609 );
3610 }
3611
3612 for (sql, expected) in [(r#"'\'"#, r#"\"#), (r#"'ab\'"#, r#"ab\"#)] {
3614 let dialect = GenericDialect {};
3615 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3616
3617 let expected = vec![Token::SingleQuotedString(expected.to_string())];
3618
3619 compare(expected, tokens);
3620 }
3621 }
3622
3623 #[test]
3624 fn tokenize_triple_quoted_string() {
3625 fn check<F>(
3626 q: char, r: char, quote_token: F,
3629 ) where
3630 F: Fn(String) -> Token,
3631 {
3632 let dialect = BigQueryDialect {};
3633
3634 for (sql, expected, expected_unescaped) in [
3635 (format!(r#"{q}{q}{q}{q}{q}{q}"#), "".into(), "".into()),
3637 (
3639 format!(r#"{q}{q}{q}ab{q}{q}\{q}{q}cd{q}{q}{q}"#),
3640 format!(r#"ab{q}{q}\{q}{q}cd"#),
3641 format!(r#"ab{q}{q}{q}{q}cd"#),
3642 ),
3643 (
3645 format!(r#"{q}{q}{q}abc{q}{q}{q}"#),
3646 "abc".into(),
3647 "abc".into(),
3648 ),
3649 (
3651 format!(r#"{q}{q}{q}ab{r}{r}{r}c{r}def{r}{r}{r}{q}{q}{q}"#),
3652 format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
3653 format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
3654 ),
3655 (
3657 format!(r#"{q}{q}{q}ab{q}{q}c{q}{q}\{q}de{q}{q}f{q}{q}{q}"#),
3658 format!(r#"ab{q}{q}c{q}{q}\{q}de{q}{q}f"#),
3659 format!(r#"ab{q}{q}c{q}{q}{q}de{q}{q}f"#),
3660 ),
3661 (
3663 format!(r#"{q}{q}{q}a\'\'b\'c\'d{q}{q}{q}"#),
3664 r#"a\'\'b\'c\'d"#.into(),
3665 r#"a''b'c'd"#.into(),
3666 ),
3667 (
3669 format!(r#"{q}{q}{q}abc\0\n\rdef{q}{q}{q}"#),
3670 r#"abc\0\n\rdef"#.into(),
3671 "abc\0\n\rdef".into(),
3672 ),
3673 ] {
3674 let tokens = Tokenizer::new(&dialect, sql.as_str())
3675 .with_unescape(false)
3676 .tokenize()
3677 .unwrap();
3678 let expected = vec![quote_token(expected.to_string())];
3679 compare(expected, tokens);
3680
3681 let tokens = Tokenizer::new(&dialect, sql.as_str())
3682 .with_unescape(true)
3683 .tokenize()
3684 .unwrap();
3685 let expected = vec![quote_token(expected_unescaped.to_string())];
3686 compare(expected, tokens);
3687 }
3688
3689 for sql in [
3690 format!(r#"{q}{q}{q}{q}{q}\{q}"#),
3691 format!(r#"{q}{q}{q}abc{q}{q}\{q}"#),
3692 format!(r#"{q}{q}{q}{q}"#),
3693 format!(r#"{q}{q}{q}{r}{r}"#),
3694 format!(r#"{q}{q}{q}abc{q}"#),
3695 format!(r#"{q}{q}{q}abc{q}{q}"#),
3696 format!(r#"{q}{q}{q}abc"#),
3697 ] {
3698 let dialect = BigQueryDialect {};
3699 let mut tokenizer = Tokenizer::new(&dialect, sql.as_str());
3700 assert_eq!(
3701 "Unterminated string literal",
3702 tokenizer.tokenize().unwrap_err().message.as_str(),
3703 );
3704 }
3705 }
3706
3707 check('"', '\'', Token::TripleDoubleQuotedString);
3708
3709 check('\'', '"', Token::TripleSingleQuotedString);
3710
3711 let dialect = BigQueryDialect {};
3712
3713 let sql = r#"""''"#;
3714 let tokens = Tokenizer::new(&dialect, sql)
3715 .with_unescape(true)
3716 .tokenize()
3717 .unwrap();
3718 let expected = vec![
3719 Token::DoubleQuotedString("".to_string()),
3720 Token::SingleQuotedString("".to_string()),
3721 ];
3722 compare(expected, tokens);
3723
3724 let sql = r#"''"""#;
3725 let tokens = Tokenizer::new(&dialect, sql)
3726 .with_unescape(true)
3727 .tokenize()
3728 .unwrap();
3729 let expected = vec![
3730 Token::SingleQuotedString("".to_string()),
3731 Token::DoubleQuotedString("".to_string()),
3732 ];
3733 compare(expected, tokens);
3734
3735 let dialect = SnowflakeDialect {};
3737 let sql = r#"''''''"#;
3738 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3739 let expected = vec![Token::SingleQuotedString("''".to_string())];
3740 compare(expected, tokens);
3741 }
3742
3743 #[test]
3744 fn test_mysql_users_grantees() {
3745 let dialect = MySqlDialect {};
3746
3747 let sql = "CREATE USER `root`@`%`";
3748 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3749 let expected = vec![
3750 Token::make_keyword("CREATE"),
3751 Token::Whitespace(Whitespace::Space),
3752 Token::make_keyword("USER"),
3753 Token::Whitespace(Whitespace::Space),
3754 Token::make_word("root", Some('`')),
3755 Token::AtSign,
3756 Token::make_word("%", Some('`')),
3757 ];
3758 compare(expected, tokens);
3759 }
3760
3761 #[test]
3762 fn test_postgres_abs_without_space_and_string_literal() {
3763 let dialect = MySqlDialect {};
3764
3765 let sql = "SELECT @'1'";
3766 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3767 let expected = vec![
3768 Token::make_keyword("SELECT"),
3769 Token::Whitespace(Whitespace::Space),
3770 Token::AtSign,
3771 Token::SingleQuotedString("1".to_string()),
3772 ];
3773 compare(expected, tokens);
3774 }
3775
3776 #[test]
3777 fn test_postgres_abs_without_space_and_quoted_column() {
3778 let dialect = MySqlDialect {};
3779
3780 let sql = r#"SELECT @"bar" FROM foo"#;
3781 let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3782 let expected = vec![
3783 Token::make_keyword("SELECT"),
3784 Token::Whitespace(Whitespace::Space),
3785 Token::AtSign,
3786 Token::DoubleQuotedString("bar".to_string()),
3787 Token::Whitespace(Whitespace::Space),
3788 Token::make_keyword("FROM"),
3789 Token::Whitespace(Whitespace::Space),
3790 Token::make_word("foo", None),
3791 ];
3792 compare(expected, tokens);
3793 }
3794
3795 #[test]
3796 fn test_national_strings_backslash_escape_not_supported() {
3797 all_dialects_where(|dialect| !dialect.supports_string_literal_backslash_escape())
3798 .tokenizes_to(
3799 "select n'''''\\'",
3800 vec![
3801 Token::make_keyword("select"),
3802 Token::Whitespace(Whitespace::Space),
3803 Token::NationalStringLiteral("''\\".to_string()),
3804 ],
3805 );
3806 }
3807
3808 #[test]
3809 fn test_national_strings_backslash_escape_supported() {
3810 all_dialects_where(|dialect| dialect.supports_string_literal_backslash_escape())
3811 .tokenizes_to(
3812 "select n'''''\\''",
3813 vec![
3814 Token::make_keyword("select"),
3815 Token::Whitespace(Whitespace::Space),
3816 Token::NationalStringLiteral("'''".to_string()),
3817 ],
3818 );
3819 }
3820
3821 #[test]
3822 fn test_string_escape_constant_not_supported() {
3823 all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
3824 "select e'...'",
3825 vec![
3826 Token::make_keyword("select"),
3827 Token::Whitespace(Whitespace::Space),
3828 Token::make_word("e", None),
3829 Token::SingleQuotedString("...".to_string()),
3830 ],
3831 );
3832
3833 all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
3834 "select E'...'",
3835 vec![
3836 Token::make_keyword("select"),
3837 Token::Whitespace(Whitespace::Space),
3838 Token::make_word("E", None),
3839 Token::SingleQuotedString("...".to_string()),
3840 ],
3841 );
3842 }
3843
3844 #[test]
3845 fn test_string_escape_constant_supported() {
3846 all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
3847 "select e'\\''",
3848 vec![
3849 Token::make_keyword("select"),
3850 Token::Whitespace(Whitespace::Space),
3851 Token::EscapedStringLiteral("'".to_string()),
3852 ],
3853 );
3854
3855 all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
3856 "select E'\\''",
3857 vec![
3858 Token::make_keyword("select"),
3859 Token::Whitespace(Whitespace::Space),
3860 Token::EscapedStringLiteral("'".to_string()),
3861 ],
3862 );
3863 }
3864
3865 #[test]
3866 fn test_whitespace_required_after_single_line_comment() {
3867 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
3868 .tokenizes_to(
3869 "SELECT --'abc'",
3870 vec![
3871 Token::make_keyword("SELECT"),
3872 Token::Whitespace(Whitespace::Space),
3873 Token::Minus,
3874 Token::Minus,
3875 Token::SingleQuotedString("abc".to_string()),
3876 ],
3877 );
3878
3879 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
3880 .tokenizes_to(
3881 "SELECT -- 'abc'",
3882 vec![
3883 Token::make_keyword("SELECT"),
3884 Token::Whitespace(Whitespace::Space),
3885 Token::Whitespace(Whitespace::SingleLineComment {
3886 prefix: "--".to_string(),
3887 comment: " 'abc'".to_string(),
3888 }),
3889 ],
3890 );
3891
3892 all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
3893 .tokenizes_to(
3894 "SELECT --",
3895 vec![
3896 Token::make_keyword("SELECT"),
3897 Token::Whitespace(Whitespace::Space),
3898 Token::Minus,
3899 Token::Minus,
3900 ],
3901 );
3902 }
3903
3904 #[test]
3905 fn test_whitespace_not_required_after_single_line_comment() {
3906 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
3907 .tokenizes_to(
3908 "SELECT --'abc'",
3909 vec![
3910 Token::make_keyword("SELECT"),
3911 Token::Whitespace(Whitespace::Space),
3912 Token::Whitespace(Whitespace::SingleLineComment {
3913 prefix: "--".to_string(),
3914 comment: "'abc'".to_string(),
3915 }),
3916 ],
3917 );
3918
3919 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
3920 .tokenizes_to(
3921 "SELECT -- 'abc'",
3922 vec![
3923 Token::make_keyword("SELECT"),
3924 Token::Whitespace(Whitespace::Space),
3925 Token::Whitespace(Whitespace::SingleLineComment {
3926 prefix: "--".to_string(),
3927 comment: " 'abc'".to_string(),
3928 }),
3929 ],
3930 );
3931
3932 all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
3933 .tokenizes_to(
3934 "SELECT --",
3935 vec![
3936 Token::make_keyword("SELECT"),
3937 Token::Whitespace(Whitespace::Space),
3938 Token::Whitespace(Whitespace::SingleLineComment {
3939 prefix: "--".to_string(),
3940 comment: "".to_string(),
3941 }),
3942 ],
3943 );
3944 }
3945}