Skip to main content

sqlparser/
tokenizer.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! SQL Tokenizer
19//!
20//! The tokenizer (a.k.a. lexer) converts a string into a sequence of tokens.
21//!
22//! The tokens then form the input for the parser, which outputs an Abstract Syntax Tree (AST).
23
24#[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/// SQL Token enumeration
52#[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    /// An end-of-file marker, not a real token
57    EOF,
58    /// A keyword (like SELECT) or an optionally quoted SQL identifier
59    Word(Word),
60    /// An unsigned numeric literal
61    Number(String, bool),
62    /// A character that could not be tokenized
63    Char(char),
64    /// Single quoted string: i.e: 'string'
65    SingleQuotedString(String),
66    /// Double quoted string: i.e: "string"
67    DoubleQuotedString(String),
68    /// Triple single quoted strings: Example '''abc'''
69    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
70    TripleSingleQuotedString(String),
71    /// Triple double quoted strings: Example """abc"""
72    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
73    TripleDoubleQuotedString(String),
74    /// Dollar quoted string: i.e: $$string$$ or $tag_name$string$tag_name$
75    DollarQuotedString(DollarQuotedString),
76    /// Byte string literal: i.e: b'string' or B'string' (note that some backends, such as
77    /// PostgreSQL, may treat this syntax as a bit string literal instead, i.e: b'10010101')
78    SingleQuotedByteStringLiteral(String),
79    /// Byte string literal: i.e: b"string" or B"string"
80    DoubleQuotedByteStringLiteral(String),
81    /// Triple single quoted literal with byte string prefix. Example `B'''abc'''`
82    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
83    TripleSingleQuotedByteStringLiteral(String),
84    /// Triple double quoted literal with byte string prefix. Example `B"""abc"""`
85    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
86    TripleDoubleQuotedByteStringLiteral(String),
87    /// Single quoted literal with raw string prefix. Example `R'abc'`
88    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
89    SingleQuotedRawStringLiteral(String),
90    /// Double quoted literal with raw string prefix. Example `R"abc"`
91    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
92    DoubleQuotedRawStringLiteral(String),
93    /// Triple single quoted literal with raw string prefix. Example `R'''abc'''`
94    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
95    TripleSingleQuotedRawStringLiteral(String),
96    /// Triple double quoted literal with raw string prefix. Example `R"""abc"""`
97    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
98    TripleDoubleQuotedRawStringLiteral(String),
99    /// "National" string literal: i.e: N'string'
100    NationalStringLiteral(String),
101    /// "escaped" string literal, which are an extension to the SQL standard: i.e: e'first \n second' or E 'first \n second'
102    EscapedStringLiteral(String),
103    /// Unicode string literal: i.e: U&'first \000A second'
104    UnicodeStringLiteral(String),
105    /// Hexadecimal string literal: i.e.: X'deadbeef'
106    HexStringLiteral(String),
107    /// Comma
108    Comma,
109    /// Whitespace (space, tab, etc)
110    Whitespace(Whitespace),
111    /// Double equals sign `==`
112    DoubleEq,
113    /// Equality operator `=`
114    Eq,
115    /// Not Equals operator `<>` (or `!=` in some dialects)
116    Neq,
117    /// Less Than operator `<`
118    Lt,
119    /// Greater Than operator `>`
120    Gt,
121    /// Less Than Or Equals operator `<=`
122    LtEq,
123    /// Greater Than Or Equals operator `>=`
124    GtEq,
125    /// Spaceship operator <=>
126    Spaceship,
127    /// Plus operator `+`
128    Plus,
129    /// Minus operator `-`
130    Minus,
131    /// Multiplication operator `*`
132    Mul,
133    /// Division operator `/`
134    Div,
135    /// Integer division operator `//` in DuckDB
136    DuckIntDiv,
137    /// Modulo Operator `%`
138    Mod,
139    /// String concatenation `||`
140    StringConcat,
141    /// Left parenthesis `(`
142    LParen,
143    /// Right parenthesis `)`
144    RParen,
145    /// Period (used for compound identifiers or projections into nested types)
146    Period,
147    /// Colon `:`
148    Colon,
149    /// DoubleColon `::` (used for casting in PostgreSQL)
150    DoubleColon,
151    /// Assignment `:=` (used for keyword argument in DuckDB macros and some functions, and for variable declarations in DuckDB and Snowflake)
152    Assignment,
153    /// SemiColon `;` used as separator for COPY and payload
154    SemiColon,
155    /// Backslash `\` used in terminating the COPY payload with `\.`
156    Backslash,
157    /// Left bracket `[`
158    LBracket,
159    /// Right bracket `]`
160    RBracket,
161    /// Ampersand `&`
162    Ampersand,
163    /// Pipe `|`
164    Pipe,
165    /// Caret `^`
166    Caret,
167    /// Left brace `{`
168    LBrace,
169    /// Right brace `}`
170    RBrace,
171    /// Right Arrow `=>`
172    RArrow,
173    /// Sharp `#` used for PostgreSQL Bitwise XOR operator, also PostgreSQL/Redshift geometrical unary/binary operator (Number of points in path or polygon/Intersection)
174    Sharp,
175    /// `##` PostgreSQL/Redshift geometrical binary operator (Point of closest proximity)
176    DoubleSharp,
177    /// Tilde `~` used for PostgreSQL Bitwise NOT operator or case sensitive match regular expression operator
178    Tilde,
179    /// `~*` , a case insensitive match regular expression operator in PostgreSQL
180    TildeAsterisk,
181    /// `!~` , a case sensitive not match regular expression operator in PostgreSQL
182    ExclamationMarkTilde,
183    /// `!~*` , a case insensitive not match regular expression operator in PostgreSQL
184    ExclamationMarkTildeAsterisk,
185    /// `~~`, a case sensitive match pattern operator in PostgreSQL
186    DoubleTilde,
187    /// `~~*`, a case insensitive match pattern operator in PostgreSQL
188    DoubleTildeAsterisk,
189    /// `!~~`, a case sensitive not match pattern operator in PostgreSQL
190    ExclamationMarkDoubleTilde,
191    /// `!~~*`, a case insensitive not match pattern operator in PostgreSQL
192    ExclamationMarkDoubleTildeAsterisk,
193    /// `<<`, a bitwise shift left operator in PostgreSQL
194    ShiftLeft,
195    /// `>>`, a bitwise shift right operator in PostgreSQL
196    ShiftRight,
197    /// `&&`, an overlap operator in PostgreSQL
198    Overlap,
199    /// Exclamation Mark `!` used for PostgreSQL factorial operator
200    ExclamationMark,
201    /// Double Exclamation Mark `!!` used for PostgreSQL prefix factorial operator
202    DoubleExclamationMark,
203    /// AtSign `@` used for PostgreSQL abs operator, also PostgreSQL/Redshift geometrical unary/binary operator (Center, Contained or on)
204    AtSign,
205    /// `^@`, a "starts with" string operator in PostgreSQL
206    CaretAt,
207    /// `|/`, a square root math operator in PostgreSQL
208    PGSquareRoot,
209    /// `||/`, a cube root math operator in PostgreSQL
210    PGCubeRoot,
211    /// `?` or `$` , a prepared statement arg placeholder
212    Placeholder(String),
213    /// `->`, used as a operator to extract json field in PostgreSQL
214    Arrow,
215    /// `->>`, used as a operator to extract json field as text in PostgreSQL
216    LongArrow,
217    /// `#>`, extracts JSON sub-object at the specified path
218    HashArrow,
219    /// `@-@` PostgreSQL/Redshift geometrical unary operator (Length or circumference)
220    AtDashAt,
221    /// `?-` PostgreSQL/Redshift geometrical unary/binary operator (Is horizontal?/Are horizontally aligned?)
222    QuestionMarkDash,
223    /// `&<` PostgreSQL/Redshift geometrical binary operator (Overlaps to left?)
224    AmpersandLeftAngleBracket,
225    /// `&>` PostgreSQL/Redshift geometrical binary operator (Overlaps to right?)`
226    AmpersandRightAngleBracket,
227    /// `&<|` PostgreSQL/Redshift geometrical binary operator (Does not extend above?)`
228    AmpersandLeftAngleBracketVerticalBar,
229    /// `|&>` PostgreSQL/Redshift geometrical binary operator (Does not extend below?)`
230    VerticalBarAmpersandRightAngleBracket,
231    /// `<->` PostgreSQL/Redshift geometrical binary operator (Distance between)
232    TwoWayArrow,
233    /// `<^` PostgreSQL/Redshift geometrical binary operator (Is below?)
234    LeftAngleBracketCaret,
235    /// `>^` PostgreSQL/Redshift geometrical binary operator (Is above?)
236    RightAngleBracketCaret,
237    /// `?#` PostgreSQL/Redshift geometrical binary operator (Intersects or overlaps)
238    QuestionMarkSharp,
239    /// `?-|` PostgreSQL/Redshift geometrical binary operator (Is perpendicular?)
240    QuestionMarkDashVerticalBar,
241    /// `?||` PostgreSQL/Redshift geometrical binary operator (Are parallel?)
242    QuestionMarkDoubleVerticalBar,
243    /// `~=` PostgreSQL/Redshift geometrical binary operator (Same as)
244    TildeEqual,
245    /// `<<| PostgreSQL/Redshift geometrical binary operator (Is strictly below?)
246    ShiftLeftVerticalBar,
247    /// `|>> PostgreSQL/Redshift geometrical binary operator (Is strictly above?)
248    VerticalBarShiftRight,
249    /// `#>>`, extracts JSON sub-object at the specified path as text
250    HashLongArrow,
251    /// jsonb @> jsonb -> boolean: Test whether left json contains the right json
252    AtArrow,
253    /// jsonb <@ jsonb -> boolean: Test whether right json contains the left json
254    ArrowAt,
255    /// jsonb #- text[] -> jsonb: Deletes the field or array element at the specified
256    /// path, where path elements can be either field keys or array indexes.
257    HashMinus,
258    /// jsonb @? jsonpath -> boolean: Does JSON path return any item for the specified
259    /// JSON value?
260    AtQuestion,
261    /// jsonb @@ jsonpath → boolean: Returns the result of a JSON path predicate check
262    /// for the specified JSON value. Only the first item of the result is taken into
263    /// account. If the result is not Boolean, then NULL is returned.
264    AtAt,
265    /// jsonb ? text -> boolean: Checks whether the string exists as a top-level key within the
266    /// jsonb object
267    Question,
268    /// jsonb ?& text[] -> boolean: Check whether all members of the text array exist as top-level
269    /// keys within the jsonb object
270    QuestionAnd,
271    /// jsonb ?| text[] -> boolean: Check whether any member of the text array exists as top-level
272    /// keys within the jsonb object
273    QuestionPipe,
274    /// Custom binary operator
275    /// This is used to represent any custom binary operator that is not part of the SQL standard.
276    /// PostgreSQL allows defining custom binary operators using CREATE OPERATOR.
277    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/// A keyword (like SELECT) or an optionally quoted SQL identifier
410#[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    /// The value of the token, without the enclosing quotes, and with the
415    /// escape sequences (if any) processed (TODO: escapes are not handled)
416    pub value: String,
417    /// An identifier can be "quoted" (&lt;delimited identifier> in ANSI parlance).
418    /// The standard and most implementations allow using double quotes for this,
419    /// but some implementations support other quoting styles as well (e.g. \[MS SQL])
420    pub quote_style: Option<char>,
421    /// If the word was not quoted and it matched one of the known keywords,
422    /// this will have one of the values from dialect::keywords, otherwise empty
423    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            '"' => '"', // ANSI and most dialects
442            '[' => ']', // MS SQL
443            '`' => '`', // MySQL
444            _ => 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/// Location in input string
473///
474/// # Create an "empty" (unknown) `Location`
475/// ```
476/// # use sqlparser::tokenizer::Location;
477/// let location = Location::empty();
478/// ```
479///
480/// # Create a `Location` from a line and column
481/// ```
482/// # use sqlparser::tokenizer::Location;
483/// let location = Location::new(1, 1);
484/// ```
485///
486/// # Create a `Location` from a pair
487/// ```
488/// # use sqlparser::tokenizer::Location;
489/// let location = Location::from((1, 1));
490/// ```
491#[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    /// Line number, starting from 1.
496    ///
497    /// Note: Line 0 is used for empty spans
498    pub line: u64,
499    /// Line column, starting from 1.
500    ///
501    /// Note: Column 0 is used for empty spans
502    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    /// Return an "empty" / unknown location
522    pub fn empty() -> Self {
523        Self { line: 0, column: 0 }
524    }
525
526    /// Create a new `Location` for a given line and column
527    pub fn new(line: u64, column: u64) -> Self {
528        Self { line, column }
529    }
530
531    /// Create a new location for a given line and column
532    ///
533    /// Alias for [`Self::new`]
534    // TODO: remove / deprecate in favor of` `new` for consistency?
535    pub fn of(line: u64, column: u64) -> Self {
536        Self::new(line, column)
537    }
538
539    /// Combine self and `end` into a new `Span`
540    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/// A span represents a linear portion of the input string (start, end)
552///
553/// See [Spanned](crate::ast::Spanned) for more information.
554#[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    // An empty span (0, 0) -> (0, 0)
570    // We need a const instance for pattern matching
571    const EMPTY: Span = Self::empty();
572
573    /// Create a new span from a start and end [`Location`]
574    pub fn new(start: Location, end: Location) -> Span {
575        Span { start, end }
576    }
577
578    /// Returns an empty span `(0, 0) -> (0, 0)`
579    ///
580    /// Empty spans represent no knowledge of source location
581    /// See [Spanned](crate::ast::Spanned) for more information.
582    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    /// Returns the smallest Span that contains both `self` and `other`
590    /// If either span is [Span::empty], the other span is returned
591    ///
592    /// # Examples
593    /// ```
594    /// # use sqlparser::tokenizer::{Span, Location};
595    /// // line 1, column1 -> line 2, column 5
596    /// let span1 = Span::new(Location::new(1, 1), Location::new(2, 5));
597    /// // line 2, column 3 -> line 3, column 7
598    /// let span2 = Span::new(Location::new(2, 3), Location::new(3, 7));
599    /// // Union of the two is the min/max of the two spans
600    /// // line 1, column 1 -> line 3, column 7
601    /// let union = span1.union(&span2);
602    /// assert_eq!(union, Span::new(Location::new(1, 1), Location::new(3, 7)));
603    /// ```
604    pub fn union(&self, other: &Span) -> Span {
605        // If either span is empty, return the other
606        // this prevents propagating (0, 0) through the tree
607        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    /// Same as [Span::union] for `Option<Span>`
618    ///
619    /// If `other` is `None`, `self` is returned
620    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    /// Return the [Span::union] of all spans in the iterator
628    ///
629    /// If the iterator is empty, an empty span is returned
630    ///
631    /// # Example
632    /// ```
633    /// # use sqlparser::tokenizer::{Span, Location};
634    /// let spans = vec![
635    ///     Span::new(Location::new(1, 1), Location::new(2, 5)),
636    ///     Span::new(Location::new(2, 3), Location::new(3, 7)),
637    ///     Span::new(Location::new(3, 1), Location::new(4, 2)),
638    /// ];
639    /// // line 1, column 1 -> line 4, column 2
640    /// assert_eq!(
641    ///   Span::union_iter(spans),
642    ///   Span::new(Location::new(1, 1), Location::new(4, 2))
643    /// );
644    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/// Backwards compatibility struct for [`TokenWithSpan`]
652#[deprecated(since = "0.53.0", note = "please use `TokenWithSpan` instead")]
653pub type TokenWithLocation = TokenWithSpan;
654
655/// A [Token] with [Span] attached to it
656///
657/// This is used to track the location of a token in the input string
658///
659/// # Examples
660/// ```
661/// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithSpan};
662/// // commas @ line 1, column 10
663/// let tok1 = TokenWithSpan::new(
664///   Token::Comma,
665///   Span::new(Location::new(1, 10), Location::new(1, 11)),
666/// );
667/// assert_eq!(tok1, Token::Comma); // can compare the token
668///
669/// // commas @ line 2, column 20
670/// let tok2 = TokenWithSpan::new(
671///   Token::Comma,
672///   Span::new(Location::new(2, 20), Location::new(2, 21)),
673/// );
674/// // same token but different locations are not equal
675/// assert_ne!(tok1, tok2);
676/// ```
677#[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    /// Create a new [`TokenWithSpan`] from a [`Token`] and a [`Span`]
687    pub fn new(token: Token, span: Span) -> Self {
688        Self { token, span }
689    }
690
691    /// Wrap a token with an empty span
692    pub fn wrap(token: Token) -> Self {
693        Self::new(token, Span::empty())
694    }
695
696    /// Wrap a token with a location from `start` to `end`
697    pub fn at(token: Token, start: Location, end: Location) -> Self {
698        Self::new(token, Span::new(start, end))
699    }
700
701    /// Return an EOF token with no location
702    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/// Tokenizer error
726#[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    /// return the next character and advance the stream
749    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    /// return the next character but do not advance the stream
765    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/// Represents how many quote characters enclose a string literal.
778#[derive(Copy, Clone)]
779enum NumStringQuoteChars {
780    /// e.g. `"abc"`, `'abc'`, `r'abc'`
781    One,
782    /// e.g. `"""abc"""`, `'''abc'''`, `r'''abc'''`
783    Many(NonZeroU8),
784}
785
786/// Settings for tokenizing a quoted string literal.
787struct TokenizeQuotedStringSettings {
788    /// The character used to quote the string.
789    quote_style: char,
790    /// Represents how many quotes characters enclose the string literal.
791    num_quote_chars: NumStringQuoteChars,
792    /// The number of opening quotes left to consume, before parsing
793    /// the remaining string literal.
794    /// For example: given initial string `"""abc"""`. If the caller has
795    /// already parsed the first quote for some reason, then this value
796    /// is set to 1, flagging to look to consume only 2 leading quotes.
797    num_opening_quotes_to_consume: u8,
798    /// True if the string uses backslash escaping of special characters
799    /// e.g `'abc\ndef\'ghi'
800    backslash_escape: bool,
801}
802
803/// SQL Tokenizer
804pub struct Tokenizer<'a> {
805    dialect: &'a dyn Dialect,
806    query: &'a str,
807    /// If true (the default), the tokenizer will un-escape literal
808    /// SQL strings See [`Tokenizer::with_unescape`] for more details.
809    unescape: bool,
810}
811
812impl<'a> Tokenizer<'a> {
813    /// Create a new SQL tokenizer for the specified SQL statement
814    ///
815    /// ```
816    /// # use sqlparser::tokenizer::{Token, Whitespace, Tokenizer};
817    /// # use sqlparser::dialect::GenericDialect;
818    /// # let dialect = GenericDialect{};
819    /// let query = r#"SELECT 'foo'"#;
820    ///
821    /// // Parsing the query
822    /// let tokens = Tokenizer::new(&dialect, &query).tokenize().unwrap();
823    ///
824    /// assert_eq!(tokens, vec![
825    ///   Token::make_word("SELECT", None),
826    ///   Token::Whitespace(Whitespace::Space),
827    ///   Token::SingleQuotedString("foo".to_string()),
828    /// ]);
829    pub fn new(dialect: &'a dyn Dialect, query: &'a str) -> Self {
830        Self {
831            dialect,
832            query,
833            unescape: true,
834        }
835    }
836
837    /// Set unescape mode
838    ///
839    /// When true (default) the tokenizer unescapes literal values
840    /// (for example, `""` in SQL is unescaped to the literal `"`).
841    ///
842    /// When false, the tokenizer provides the raw strings as provided
843    /// in the query.  This can be helpful for programs that wish to
844    /// recover the *exact* original query text without normalizing
845    /// the escaping
846    ///
847    /// # Example
848    ///
849    /// ```
850    /// # use sqlparser::tokenizer::{Token, Tokenizer};
851    /// # use sqlparser::dialect::GenericDialect;
852    /// # let dialect = GenericDialect{};
853    /// let query = r#""Foo "" Bar""#;
854    /// let unescaped = Token::make_word(r#"Foo " Bar"#, Some('"'));
855    /// let original  = Token::make_word(r#"Foo "" Bar"#, Some('"'));
856    ///
857    /// // Parsing with unescaping (default)
858    /// let tokens = Tokenizer::new(&dialect, &query).tokenize().unwrap();
859    /// assert_eq!(tokens, vec![unescaped]);
860    ///
861    /// // Parsing with unescape = false
862    /// let tokens = Tokenizer::new(&dialect, &query)
863    ///    .with_unescape(false)
864    ///    .tokenize().unwrap();
865    /// assert_eq!(tokens, vec![original]);
866    /// ```
867    pub fn with_unescape(mut self, unescape: bool) -> Self {
868        self.unescape = unescape;
869        self
870    }
871
872    /// Tokenize the statement and produce a vector of tokens
873    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    /// Tokenize the statement and produce a vector of tokens with location information
879    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    /// Tokenize the statement and append tokens with location information into the provided buffer.
886    /// If an error is thrown, the buffer will contain all tokens that were successfully parsed before the error.
887    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    // Tokenize the identifier or keywords in `ch`
909    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(); // consume the first char
915        let ch: String = ch.into_iter().collect();
916        let word = self.tokenize_word(ch, chars);
917
918        // TODO: implement parsing of exponent here
919        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    /// Get the next token or return None
935    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                    // Emit a single Whitespace::Newline token for \r and \r\n
943                    chars.next();
944                    if let Some('\n') = chars.peek() {
945                        chars.next();
946                    }
947                    Ok(Some(Token::Whitespace(Whitespace::Newline)))
948                }
949                // BigQuery and MySQL use b or B for byte string literal, Postgres for bit strings
950                b @ 'B' | b @ 'b' if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | MySqlDialect | GenericDialect) =>
951                {
952                    chars.next(); // consume
953                    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                            // regular identifier starting with an "b" or "B"
984                            let s = self.tokenize_word(b, chars);
985                            Ok(Some(Token::make_word(&s, None)))
986                        }
987                    }
988                }
989                // BigQuery uses r or R for raw string literal
990                b @ 'R' | b @ 'r' if dialect_of!(self is BigQueryDialect | GenericDialect) => {
991                    chars.next(); // consume
992                    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                            // regular identifier starting with an "r" or "R"
1011                            let s = self.tokenize_word(b, chars);
1012                            Ok(Some(Token::make_word(&s, None)))
1013                        }
1014                    }
1015                }
1016                // Redshift uses lower case n for national string literal
1017                n @ 'N' | n @ 'n' => {
1018                    chars.next(); // consume, to check the next char
1019                    match chars.peek() {
1020                        Some('\'') => {
1021                            // N'...' - a <national character string literal>
1022                            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                            // regular identifier starting with an "N"
1030                            let s = self.tokenize_word(n, chars);
1031                            Ok(Some(Token::make_word(&s, None)))
1032                        }
1033                    }
1034                }
1035                // PostgreSQL accepts "escape" string constants, which are an extension to the SQL standard.
1036                x @ 'e' | x @ 'E' if self.dialect.supports_string_escape_constant() => {
1037                    let starting_loc = chars.location();
1038                    chars.next(); // consume, to check the next char
1039                    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                            // regular identifier starting with an "E" or "e"
1047                            let s = self.tokenize_word(x, chars);
1048                            Ok(Some(Token::make_word(&s, None)))
1049                        }
1050                    }
1051                }
1052                // Unicode string literals like U&'first \000A second' are supported in some dialects, including PostgreSQL
1053                x @ 'u' | x @ 'U' if self.dialect.supports_unicode_string_literal() => {
1054                    chars.next(); // consume, to check the next char
1055                    if chars.peek() == Some(&'&') {
1056                        // we cannot advance the iterator here, as we need to consume the '&' later if the 'u' was an identifier
1057                        let mut chars_clone = chars.peekable.clone();
1058                        chars_clone.next(); // consume the '&' in the clone
1059                        if chars_clone.peek() == Some(&'\'') {
1060                            chars.next(); // consume the '&' in the original iterator
1061                            let s = unescape_unicode_single_quoted_string(chars)?;
1062                            return Ok(Some(Token::UnicodeStringLiteral(s)));
1063                        }
1064                    }
1065                    // regular identifier starting with an "U" or "u"
1066                    let s = self.tokenize_word(x, chars);
1067                    Ok(Some(Token::make_word(&s, None)))
1068                }
1069                // The spec only allows an uppercase 'X' to introduce a hex
1070                // string, but PostgreSQL, at least, allows a lowercase 'x' too.
1071                x @ 'x' | x @ 'X' => {
1072                    chars.next(); // consume, to check the next char
1073                    match chars.peek() {
1074                        Some('\'') => {
1075                            // X'...' - a <binary string literal>
1076                            let s = self.tokenize_single_quoted_string(chars, '\'', true)?;
1077                            Ok(Some(Token::HexStringLiteral(s)))
1078                        }
1079                        _ => {
1080                            // regular identifier starting with an "X"
1081                            let s = self.tokenize_word(x, chars);
1082                            Ok(Some(Token::make_word(&s, None)))
1083                        }
1084                    }
1085                }
1086                // single quoted string
1087                '\'' => {
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                // double quoted string
1107                '\"' 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                // delimited (quoted) identifier
1129                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                // Potentially nested delimited (quoted) identifier
1134                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(); // skip the first delimiter
1164                    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(&quote_end) {
1176                        return self.tokenizer_error(
1177                            error_loc,
1178                            format!("Expected close delimiter '{quote_end}' before EOF."),
1179                        );
1180                    }
1181                    chars.next(); // skip close delimiter
1182
1183                    Ok(Some(Token::make_word(&word.concat(), Some(quote_start))))
1184                }
1185                // numbers and period
1186                '0'..='9' | '.' => {
1187                    // Some dialects support underscore as number separator
1188                    // There can only be one at a time and it must be followed by another digit
1189                    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                    // match binary literal that starts with 0x
1200                    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                    // match one period
1209                    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                    // No number -> Token::Period
1219                    if s == "." {
1220                        return Ok(Some(Token::Period));
1221                    }
1222
1223                    let mut exponent_part = String::new();
1224                    // Parse exponent as number
1225                    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                        // Optional sign
1230                        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                            // Definitely an exponent, get original iterator up to speed and use it
1240                            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                            // Not an exponent, discard the work done
1249                            _ => (),
1250                        }
1251                    }
1252
1253                    // mysql dialect supports identifiers that start with a numeric prefix,
1254                    // as long as they aren't an exponent number.
1255                    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                // punctuation
1274                '(' => self.consume_and_return(chars, Token::LParen),
1275                ')' => self.consume_and_return(chars, Token::RParen),
1276                ',' => self.consume_and_return(chars, Token::Comma),
1277                // operators
1278                '-' => {
1279                    chars.next(); // consume the '-'
1280
1281                    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(); // consume second '-'
1290                                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                        // a regular '-' operator
1309                        _ => self.start_binop(chars, "-", Token::Minus),
1310                    }
1311                }
1312                '/' => {
1313                    chars.next(); // consume the '/'
1314                    match chars.peek() {
1315                        Some('*') => {
1316                            chars.next(); // consume the '*', starting a multi-line comment
1317                            self.tokenize_multiline_comment(chars)
1318                        }
1319                        Some('/') if dialect_of!(self is SnowflakeDialect) => {
1320                            chars.next(); // consume the second '/', starting a snowflake single-line comment
1321                            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                        // a regular '/' operator
1331                        _ => 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(); // advance past '%'
1338                    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(); // consume the '|'
1348                    match chars.peek() {
1349                        Some('/') => self.consume_for_binop(chars, "|/", Token::PGSquareRoot),
1350                        Some('|') => {
1351                            chars.next(); // consume the second '|'
1352                            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(); // consume
1361                            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(); // consume
1372                            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                        // Bitshift '|' operator
1382                        _ => self.start_binop(chars, "|", Token::Pipe),
1383                    }
1384                }
1385                '=' => {
1386                    chars.next(); // consume
1387                    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(); // consume
1395                    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(); // consume
1421                    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(); // consume
1435                            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(); // consume
1447                            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(); // consume
1463                    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(); // consume the '&'
1486                    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(); // consume
1493                            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(); // consume the second '&'
1505                            self.start_binop(chars, "&&", Token::Overlap)
1506                        }
1507                        // Bitshift '&' operator
1508                        _ => self.start_binop(chars, "&", Token::Ampersand),
1509                    }
1510                }
1511                '^' => {
1512                    chars.next(); // consume the '^'
1513                    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(); // consume the '#', starting a snowflake single-line comment
1523                    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(); // consume
1531                    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                        // We break on quotes here, because no dialect allows identifiers starting
1598                        // with @ and containing quotation marks (e.g. `@'foo'`) unless they are
1599                        // quoted, which is tokenized as a quoted string, not here (e.g.
1600                        // `"@'foo'"`). Further, at least two dialects parse `@` followed by a
1601                        // quoted string as two separate tokens, which this allows. For example,
1602                        // Postgres parses `@'1'` as the absolute value of '1' which is implicitly
1603                        // cast to a numeric type. And when parsing MySQL-style grantees (e.g.
1604                        // `GRANT ALL ON *.* to 'root'@'localhost'`), we also want separate tokens
1605                        // for the user, the `@`, and the host.
1606                        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                // Postgres uses ? for jsonb operators, not prepared statements
1616                '?' if self.dialect.supports_geometric_types() => {
1617                    chars.next(); // consume
1618                    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(); // consume
1633                            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                // identifier or keyword
1650                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                // whitespace check (including unicode chars) should be last as it covers some of the chars above
1656                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    /// Consume the next character, then parse a custom binary operator. The next character should be included in the prefix
1666    fn consume_for_binop(
1667        &self,
1668        chars: &mut State,
1669        prefix: &str,
1670        default: Token,
1671    ) -> Result<Option<Token>, TokenizerError> {
1672        chars.next(); // consume the first char
1673        self.start_binop_opt(chars, prefix, Some(default))
1674    }
1675
1676    /// parse a custom binary operator
1677    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    /// parse a custom binary operator
1687    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    /// Tokenize dollar preceded value (i.e: a string/placeholder)
1713    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 the dialect does not support dollar-quoted strings, then `$$` is rather a placeholder.
1720        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                    // Allow $ as a placeholder character if the dialect supports it
1757                    || matches!(ch, '$' if self.dialect.supports_dollar_placeholder())
1758            }));
1759
1760            // If the dialect does not support dollar-quoted strings, don't look for the end delimiter.
1761            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    // Consume characters until newline
1817    fn tokenize_single_line_comment(&self, chars: &mut State) -> String {
1818        let mut comment = peeking_take_while(chars, |ch| match ch {
1819            '\n' => false,                                           // Always stop at \n
1820            '\r' if dialect_of!(self is PostgreSqlDialect) => false, // Stop at \r for Postgres
1821            _ => true, // Keep consuming for other characters
1822        });
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    /// Tokenize an identifier or keyword, after the first char is already consumed.
1833    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    /// Read a quoted identifier
1842    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(); // consume the opening quote
1849        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    /// Read a single quoted string, starting with the opening quote.
1863    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    /// Reads a string literal quoted by a single or triple quote characters.
1876    /// Examples: `'abc'`, `'''abc'''`, `"""abc"""`.
1877    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(&quote_style) == chars.peek() {
1893                chars.next(); // Consume quote.
1894                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                // If we matched double quotes, then this is an empty string.
1904                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    /// Reads a string literal quoted by a single quote character.
1933    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    /// Read a quoted string.
1951    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        // Consume any opening quotes.
1960        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(); // consume
1981
1982                    if let Some(NumStringQuoteChars::Many(count)) = pending_final_quote {
1983                        // For an initial string like `"""abc"""`, at this point we have
1984                        // `abc""` in the buffer and have now matched the final `"`.
1985                        // However, the string to return is simply `abc`, so we strip off
1986                        // the trailing quotes before returning.
1987                        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                            // In no-escape mode, the given query has to be saved completely
2000                            s.push(ch);
2001                        }
2002                        chars.next();
2003                    } else {
2004                        return Ok(s);
2005                    }
2006                }
2007                '\\' if settings.backslash_escape => {
2008                    // consume backslash
2009                    chars.next();
2010
2011                    num_consecutive_quotes = 0;
2012
2013                    if let Some(next) = chars.peek() {
2014                        if !self.unescape {
2015                            // In no-escape mode, the given query has to be saved completely including backslashes.
2016                            s.push(ch);
2017                            s.push(*next);
2018                            chars.next(); // consume next
2019                        } 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(); // consume next
2033                        }
2034                    }
2035                }
2036                ch => {
2037                    chars.next(); // consume ch
2038
2039                    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(); // consume the '*'
2064                    s.push('/');
2065                    s.push('*');
2066                    nested += 1;
2067                }
2068                Some('*') if matches!(chars.peek(), Some('/')) => {
2069                    chars.next(); // consume the '/'
2070                    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(&quote_end) {
2096                    chars.next();
2097                    s.push(ch);
2098                    if !self.unescape {
2099                        // In no-escape mode, the given query has to be saved completely
2100                        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
2124/// Read from `chars` until `predicate` returns `false` or EOF is hit.
2125/// Return the characters read as String, and keep the first non-matching
2126/// char available as `chars.next()`.
2127fn 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(); // consume
2132            s.push(ch);
2133        } else {
2134            break;
2135        }
2136    }
2137    s
2138}
2139
2140/// Same as peeking_take_while, but also passes the next character to the predicate.
2141fn 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(); // consume
2150            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                // case: ''''
2178                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        // u32 is used here because Pg has an overflow operation rather than throwing an exception directly.
2222        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    // Hexadecimal byte value. \xh, \xhh (h = 0–9, A–F)
2236    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    // Octal byte value. \o, \oo, \ooo (o = 0–7)
2262    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    // 16-bit hexadecimal Unicode character value. \uxxxx (x = 0–9, A–F)
2285    fn unescape_unicode_16(&mut self) -> Option<char> {
2286        self.unescape_unicode::<4>()
2287    }
2288
2289    // 32-bit hexadecimal Unicode character value. \Uxxxxxxxx (x = 0–9, A–F)
2290    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(); // consume the opening quote
2309    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), // leading underscore tokenizes as a word (parsed as column identifier)
2462                Token::Comma,
2463                Token::Whitespace(Whitespace::Space),
2464                Token::Number("10_00".to_string(), false),
2465                Token::make_word("_", None), // trailing underscores tokenizes as a word (syntax error in some dialects)
2466                Token::Comma,
2467                Token::Whitespace(Whitespace::Space),
2468                Token::Number("10".to_string(), false),
2469                Token::make_word("___0", None), // multiple underscores tokenizes as a word (syntax error in some dialects)
2470            ],
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        // println!("tokens: {:#?}", tokens);
2731        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        // println!("tokens: {:#?}", tokens);
2789        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        //println!("------------------------------");
3448        //println!("tokens   = {:?}", actual);
3449        //println!("expected = {:?}", expected);
3450        //println!("------------------------------");
3451        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        // 16 and 32-bit hexadecimal Unicode character value
3479        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        // hexadecimal byte value
3492        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        // octal byte value
3502        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        // others
3514        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        // Non-escape dialect
3613        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, // The quote character to test
3627            r: char, // An alternate quote character.
3628            quote_token: F,
3629        ) where
3630            F: Fn(String) -> Token,
3631        {
3632            let dialect = BigQueryDialect {};
3633
3634            for (sql, expected, expected_unescaped) in [
3635                // Empty string
3636                (format!(r#"{q}{q}{q}{q}{q}{q}"#), "".into(), "".into()),
3637                // Should not count escaped quote as end of string.
3638                (
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                // Simple string
3644                (
3645                    format!(r#"{q}{q}{q}abc{q}{q}{q}"#),
3646                    "abc".into(),
3647                    "abc".into(),
3648                ),
3649                // Mix single-double quotes unescaped.
3650                (
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                // Escaped quote.
3656                (
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                // backslash-escaped quote characters.
3662                (
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                // backslash-escaped characters
3668                (
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        // Non-triple quoted string dialect
3736        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}