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::num::NonZeroU8;
33use core::str::Chars;
34use core::{cmp, fmt};
35use core::{iter::Peekable, str};
36
37#[cfg(feature = "serde")]
38use serde::{Deserialize, Serialize};
39
40#[cfg(feature = "visitor")]
41use sqlparser_derive::{Visit, VisitMut};
42
43use crate::dialect::Dialect;
44use crate::dialect::{
45    BigQueryDialect, DuckDbDialect, GenericDialect, MySqlDialect, PostgreSqlDialect,
46    SnowflakeDialect,
47};
48use crate::keywords::{Keyword, ALL_KEYWORDS, ALL_KEYWORDS_INDEX};
49use crate::{
50    ast::{DollarQuotedString, QuoteDelimitedString},
51    dialect::HiveDialect,
52};
53
54/// SQL Token enumeration
55#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
58pub enum Token {
59    /// An end-of-file marker, not a real token
60    EOF,
61    /// A keyword (like SELECT) or an optionally quoted SQL identifier
62    Word(Word),
63    /// An unsigned numeric literal
64    Number(String, bool),
65    /// A character that could not be tokenized
66    Char(char),
67    /// Single quoted string: i.e: 'string'
68    SingleQuotedString(String),
69    /// Double quoted string: i.e: "string"
70    DoubleQuotedString(String),
71    /// Triple single quoted strings: Example '''abc'''
72    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
73    TripleSingleQuotedString(String),
74    /// Triple double quoted strings: Example """abc"""
75    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
76    TripleDoubleQuotedString(String),
77    /// Dollar quoted string: i.e: $$string$$ or $tag_name$string$tag_name$
78    DollarQuotedString(DollarQuotedString),
79    /// Byte string literal: i.e: b'string' or B'string' (note that some backends, such as
80    /// PostgreSQL, may treat this syntax as a bit string literal instead, i.e: b'10010101')
81    SingleQuotedByteStringLiteral(String),
82    /// Byte string literal: i.e: b"string" or B"string"
83    DoubleQuotedByteStringLiteral(String),
84    /// Triple single quoted literal with byte string prefix. Example `B'''abc'''`
85    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
86    TripleSingleQuotedByteStringLiteral(String),
87    /// Triple double quoted literal with byte string prefix. Example `B"""abc"""`
88    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
89    TripleDoubleQuotedByteStringLiteral(String),
90    /// Single quoted literal with raw string prefix. Example `R'abc'`
91    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
92    SingleQuotedRawStringLiteral(String),
93    /// Double quoted literal with raw string prefix. Example `R"abc"`
94    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
95    DoubleQuotedRawStringLiteral(String),
96    /// Triple single quoted literal with raw string prefix. Example `R'''abc'''`
97    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
98    TripleSingleQuotedRawStringLiteral(String),
99    /// Triple double quoted literal with raw string prefix. Example `R"""abc"""`
100    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
101    TripleDoubleQuotedRawStringLiteral(String),
102    /// "National" string literal: i.e: N'string'
103    NationalStringLiteral(String),
104    /// Quote delimited literal. Examples `Q'{ab'c}'`, `Q'|ab'c|'`, `Q'|ab|c|'`
105    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
106    QuoteDelimitedStringLiteral(QuoteDelimitedString),
107    /// "Nationa" quote delimited literal. Examples `NQ'{ab'c}'`, `NQ'|ab'c|'`, `NQ'|ab|c|'`
108    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
109    NationalQuoteDelimitedStringLiteral(QuoteDelimitedString),
110    /// "escaped" string literal, which are an extension to the SQL standard: i.e: e'first \n second' or E 'first \n second'
111    EscapedStringLiteral(String),
112    /// Unicode string literal: i.e: U&'first \000A second'
113    UnicodeStringLiteral(String),
114    /// Hexadecimal string literal: i.e.: X'deadbeef'
115    HexStringLiteral(String),
116    /// Comma
117    Comma,
118    /// Whitespace (space, tab, etc)
119    Whitespace(Whitespace),
120    /// Double equals sign `==`
121    DoubleEq,
122    /// Equality operator `=`
123    Eq,
124    /// Not Equals operator `<>` (or `!=` in some dialects)
125    Neq,
126    /// Less Than operator `<`
127    Lt,
128    /// Greater Than operator `>`
129    Gt,
130    /// Less Than Or Equals operator `<=`
131    LtEq,
132    /// Greater Than Or Equals operator `>=`
133    GtEq,
134    /// Spaceship operator <=>
135    Spaceship,
136    /// Plus operator `+`
137    Plus,
138    /// Minus operator `-`
139    Minus,
140    /// Multiplication operator `*`
141    Mul,
142    /// Division operator `/`
143    Div,
144    /// Integer division operator `//` in DuckDB
145    DuckIntDiv,
146    /// Modulo Operator `%`
147    Mod,
148    /// String concatenation `||`
149    StringConcat,
150    /// Left parenthesis `(`
151    LParen,
152    /// Right parenthesis `)`
153    RParen,
154    /// Period (used for compound identifiers or projections into nested types)
155    Period,
156    /// Colon `:`
157    Colon,
158    /// DoubleColon `::` (used for casting in PostgreSQL)
159    DoubleColon,
160    /// Assignment `:=` (used for keyword argument in DuckDB macros and some functions, and for variable declarations in DuckDB and Snowflake)
161    Assignment,
162    /// SemiColon `;` used as separator for COPY and payload
163    SemiColon,
164    /// Backslash `\` used in terminating the COPY payload with `\.`
165    Backslash,
166    /// Left bracket `[`
167    LBracket,
168    /// Right bracket `]`
169    RBracket,
170    /// Ampersand `&`
171    Ampersand,
172    /// Pipe `|`
173    Pipe,
174    /// Caret `^`
175    Caret,
176    /// Left brace `{`
177    LBrace,
178    /// Right brace `}`
179    RBrace,
180    /// Right Arrow `=>`
181    RArrow,
182    /// Sharp `#` used for PostgreSQL Bitwise XOR operator, also PostgreSQL/Redshift geometrical unary/binary operator (Number of points in path or polygon/Intersection)
183    Sharp,
184    /// `##` PostgreSQL/Redshift geometrical binary operator (Point of closest proximity)
185    DoubleSharp,
186    /// Tilde `~` used for PostgreSQL Bitwise NOT operator or case sensitive match regular expression operator
187    Tilde,
188    /// `~*` , a case insensitive match regular expression operator in PostgreSQL
189    TildeAsterisk,
190    /// `!~` , a case sensitive not match regular expression operator in PostgreSQL
191    ExclamationMarkTilde,
192    /// `!~*` , a case insensitive not match regular expression operator in PostgreSQL
193    ExclamationMarkTildeAsterisk,
194    /// `~~`, a case sensitive match pattern operator in PostgreSQL
195    DoubleTilde,
196    /// `~~*`, a case insensitive match pattern operator in PostgreSQL
197    DoubleTildeAsterisk,
198    /// `!~~`, a case sensitive not match pattern operator in PostgreSQL
199    ExclamationMarkDoubleTilde,
200    /// `!~~*`, a case insensitive not match pattern operator in PostgreSQL
201    ExclamationMarkDoubleTildeAsterisk,
202    /// `<<`, a bitwise shift left operator in PostgreSQL
203    ShiftLeft,
204    /// `>>`, a bitwise shift right operator in PostgreSQL
205    ShiftRight,
206    /// `&&`, an overlap operator in PostgreSQL
207    Overlap,
208    /// Exclamation Mark `!` used for PostgreSQL factorial operator
209    ExclamationMark,
210    /// Double Exclamation Mark `!!` used for PostgreSQL prefix factorial operator
211    DoubleExclamationMark,
212    /// AtSign `@` used for PostgreSQL abs operator, also PostgreSQL/Redshift geometrical unary/binary operator (Center, Contained or on)
213    AtSign,
214    /// `^@`, a "starts with" string operator in PostgreSQL
215    CaretAt,
216    /// `|/`, a square root math operator in PostgreSQL
217    PGSquareRoot,
218    /// `||/`, a cube root math operator in PostgreSQL
219    PGCubeRoot,
220    /// `?` or `$` , a prepared statement arg placeholder
221    Placeholder(String),
222    /// `->`, used as a operator to extract json field in PostgreSQL
223    Arrow,
224    /// `->>`, used as a operator to extract json field as text in PostgreSQL
225    LongArrow,
226    /// `#>`, extracts JSON sub-object at the specified path
227    HashArrow,
228    /// `@-@` PostgreSQL/Redshift geometrical unary operator (Length or circumference)
229    AtDashAt,
230    /// `?-` PostgreSQL/Redshift geometrical unary/binary operator (Is horizontal?/Are horizontally aligned?)
231    QuestionMarkDash,
232    /// `&<` PostgreSQL/Redshift geometrical binary operator (Overlaps to left?)
233    AmpersandLeftAngleBracket,
234    /// `&>` PostgreSQL/Redshift geometrical binary operator (Overlaps to right?)`
235    AmpersandRightAngleBracket,
236    /// `&<|` PostgreSQL/Redshift geometrical binary operator (Does not extend above?)`
237    AmpersandLeftAngleBracketVerticalBar,
238    /// `|&>` PostgreSQL/Redshift geometrical binary operator (Does not extend below?)`
239    VerticalBarAmpersandRightAngleBracket,
240    /// `<->` PostgreSQL/Redshift geometrical binary operator (Distance between)
241    TwoWayArrow,
242    /// `<^` PostgreSQL/Redshift geometrical binary operator (Is below?)
243    LeftAngleBracketCaret,
244    /// `>^` PostgreSQL/Redshift geometrical binary operator (Is above?)
245    RightAngleBracketCaret,
246    /// `?#` PostgreSQL/Redshift geometrical binary operator (Intersects or overlaps)
247    QuestionMarkSharp,
248    /// `?-|` PostgreSQL/Redshift geometrical binary operator (Is perpendicular?)
249    QuestionMarkDashVerticalBar,
250    /// `?||` PostgreSQL/Redshift geometrical binary operator (Are parallel?)
251    QuestionMarkDoubleVerticalBar,
252    /// `~=` PostgreSQL/Redshift geometrical binary operator (Same as)
253    TildeEqual,
254    /// `<<| PostgreSQL/Redshift geometrical binary operator (Is strictly below?)
255    ShiftLeftVerticalBar,
256    /// `|>> PostgreSQL/Redshift geometrical binary operator (Is strictly above?)
257    VerticalBarShiftRight,
258    /// `|> BigQuery pipe operator
259    VerticalBarRightAngleBracket,
260    /// `#>>`, extracts JSON sub-object at the specified path as text
261    HashLongArrow,
262    /// jsonb @> jsonb -> boolean: Test whether left json contains the right json
263    AtArrow,
264    /// jsonb <@ jsonb -> boolean: Test whether right json contains the left json
265    ArrowAt,
266    /// jsonb #- text[] -> jsonb: Deletes the field or array element at the specified
267    /// path, where path elements can be either field keys or array indexes.
268    HashMinus,
269    /// jsonb @? jsonpath -> boolean: Does JSON path return any item for the specified
270    /// JSON value?
271    AtQuestion,
272    /// jsonb @@ jsonpath → boolean: Returns the result of a JSON path predicate check
273    /// for the specified JSON value. Only the first item of the result is taken into
274    /// account. If the result is not Boolean, then NULL is returned.
275    AtAt,
276    /// jsonb ? text -> boolean: Checks whether the string exists as a top-level key within the
277    /// jsonb object
278    Question,
279    /// jsonb ?& text[] -> boolean: Check whether all members of the text array exist as top-level
280    /// keys within the jsonb object
281    QuestionAnd,
282    /// jsonb ?| text[] -> boolean: Check whether any member of the text array exists as top-level
283    /// keys within the jsonb object
284    QuestionPipe,
285    /// Custom binary operator
286    /// This is used to represent any custom binary operator that is not part of the SQL standard.
287    /// PostgreSQL allows defining custom binary operators using CREATE OPERATOR.
288    CustomBinaryOperator(String),
289}
290
291impl fmt::Display for Token {
292    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293        match self {
294            Token::EOF => f.write_str("EOF"),
295            Token::Word(ref w) => write!(f, "{w}"),
296            Token::Number(ref n, l) => write!(f, "{}{long}", n, long = if *l { "L" } else { "" }),
297            Token::Char(ref c) => write!(f, "{c}"),
298            Token::SingleQuotedString(ref s) => write!(f, "'{s}'"),
299            Token::TripleSingleQuotedString(ref s) => write!(f, "'''{s}'''"),
300            Token::DoubleQuotedString(ref s) => write!(f, "\"{s}\""),
301            Token::TripleDoubleQuotedString(ref s) => write!(f, "\"\"\"{s}\"\"\""),
302            Token::DollarQuotedString(ref s) => write!(f, "{s}"),
303            Token::NationalStringLiteral(ref s) => write!(f, "N'{s}'"),
304            Token::QuoteDelimitedStringLiteral(ref s) => s.fmt(f),
305            Token::NationalQuoteDelimitedStringLiteral(ref s) => write!(f, "N{s}"),
306            Token::EscapedStringLiteral(ref s) => write!(f, "E'{s}'"),
307            Token::UnicodeStringLiteral(ref s) => write!(f, "U&'{s}'"),
308            Token::HexStringLiteral(ref s) => write!(f, "X'{s}'"),
309            Token::SingleQuotedByteStringLiteral(ref s) => write!(f, "B'{s}'"),
310            Token::TripleSingleQuotedByteStringLiteral(ref s) => write!(f, "B'''{s}'''"),
311            Token::DoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"{s}\""),
312            Token::TripleDoubleQuotedByteStringLiteral(ref s) => write!(f, "B\"\"\"{s}\"\"\""),
313            Token::SingleQuotedRawStringLiteral(ref s) => write!(f, "R'{s}'"),
314            Token::DoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"{s}\""),
315            Token::TripleSingleQuotedRawStringLiteral(ref s) => write!(f, "R'''{s}'''"),
316            Token::TripleDoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"\"\"{s}\"\"\""),
317            Token::Comma => f.write_str(","),
318            Token::Whitespace(ws) => write!(f, "{ws}"),
319            Token::DoubleEq => f.write_str("=="),
320            Token::Spaceship => f.write_str("<=>"),
321            Token::Eq => f.write_str("="),
322            Token::Neq => f.write_str("<>"),
323            Token::Lt => f.write_str("<"),
324            Token::Gt => f.write_str(">"),
325            Token::LtEq => f.write_str("<="),
326            Token::GtEq => f.write_str(">="),
327            Token::Plus => f.write_str("+"),
328            Token::Minus => f.write_str("-"),
329            Token::Mul => f.write_str("*"),
330            Token::Div => f.write_str("/"),
331            Token::DuckIntDiv => f.write_str("//"),
332            Token::StringConcat => f.write_str("||"),
333            Token::Mod => f.write_str("%"),
334            Token::LParen => f.write_str("("),
335            Token::RParen => f.write_str(")"),
336            Token::Period => f.write_str("."),
337            Token::Colon => f.write_str(":"),
338            Token::DoubleColon => f.write_str("::"),
339            Token::Assignment => f.write_str(":="),
340            Token::SemiColon => f.write_str(";"),
341            Token::Backslash => f.write_str("\\"),
342            Token::LBracket => f.write_str("["),
343            Token::RBracket => f.write_str("]"),
344            Token::Ampersand => f.write_str("&"),
345            Token::Caret => f.write_str("^"),
346            Token::Pipe => f.write_str("|"),
347            Token::LBrace => f.write_str("{"),
348            Token::RBrace => f.write_str("}"),
349            Token::RArrow => f.write_str("=>"),
350            Token::Sharp => f.write_str("#"),
351            Token::DoubleSharp => f.write_str("##"),
352            Token::ExclamationMark => f.write_str("!"),
353            Token::DoubleExclamationMark => f.write_str("!!"),
354            Token::Tilde => f.write_str("~"),
355            Token::TildeAsterisk => f.write_str("~*"),
356            Token::ExclamationMarkTilde => f.write_str("!~"),
357            Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
358            Token::DoubleTilde => f.write_str("~~"),
359            Token::DoubleTildeAsterisk => f.write_str("~~*"),
360            Token::ExclamationMarkDoubleTilde => f.write_str("!~~"),
361            Token::ExclamationMarkDoubleTildeAsterisk => f.write_str("!~~*"),
362            Token::AtSign => f.write_str("@"),
363            Token::CaretAt => f.write_str("^@"),
364            Token::ShiftLeft => f.write_str("<<"),
365            Token::ShiftRight => f.write_str(">>"),
366            Token::Overlap => f.write_str("&&"),
367            Token::PGSquareRoot => f.write_str("|/"),
368            Token::PGCubeRoot => f.write_str("||/"),
369            Token::AtDashAt => f.write_str("@-@"),
370            Token::QuestionMarkDash => f.write_str("?-"),
371            Token::AmpersandLeftAngleBracket => f.write_str("&<"),
372            Token::AmpersandRightAngleBracket => f.write_str("&>"),
373            Token::AmpersandLeftAngleBracketVerticalBar => f.write_str("&<|"),
374            Token::VerticalBarAmpersandRightAngleBracket => f.write_str("|&>"),
375            Token::VerticalBarRightAngleBracket => f.write_str("|>"),
376            Token::TwoWayArrow => f.write_str("<->"),
377            Token::LeftAngleBracketCaret => f.write_str("<^"),
378            Token::RightAngleBracketCaret => f.write_str(">^"),
379            Token::QuestionMarkSharp => f.write_str("?#"),
380            Token::QuestionMarkDashVerticalBar => f.write_str("?-|"),
381            Token::QuestionMarkDoubleVerticalBar => f.write_str("?||"),
382            Token::TildeEqual => f.write_str("~="),
383            Token::ShiftLeftVerticalBar => f.write_str("<<|"),
384            Token::VerticalBarShiftRight => f.write_str("|>>"),
385            Token::Placeholder(ref s) => write!(f, "{s}"),
386            Token::Arrow => write!(f, "->"),
387            Token::LongArrow => write!(f, "->>"),
388            Token::HashArrow => write!(f, "#>"),
389            Token::HashLongArrow => write!(f, "#>>"),
390            Token::AtArrow => write!(f, "@>"),
391            Token::ArrowAt => write!(f, "<@"),
392            Token::HashMinus => write!(f, "#-"),
393            Token::AtQuestion => write!(f, "@?"),
394            Token::AtAt => write!(f, "@@"),
395            Token::Question => write!(f, "?"),
396            Token::QuestionAnd => write!(f, "?&"),
397            Token::QuestionPipe => write!(f, "?|"),
398            Token::CustomBinaryOperator(s) => f.write_str(s),
399        }
400    }
401}
402
403impl Token {
404    /// Create a `Token::Word` from an unquoted `keyword`.
405    ///
406    /// The lookup is case-insensitive; unknown values become `Keyword::NoKeyword`.
407    pub fn make_keyword(keyword: &str) -> Self {
408        Token::make_word(keyword, None)
409    }
410
411    /// Create a `Token::Word` from `word` with an optional `quote_style`.
412    ///
413    /// When `quote_style` is `None`, the parser attempts a case-insensitive keyword
414    /// lookup and sets the `Word::keyword` accordingly.
415    pub fn make_word(word: &str, quote_style: Option<char>) -> Self {
416        Token::Word(Word {
417            keyword: keyword_lookup(word, quote_style),
418            value: word.to_string(),
419            quote_style,
420        })
421    }
422
423    /// Like [`Self::make_word`] but takes ownership of the word `String`,
424    /// avoiding an extra allocation when the caller already has an owned value.
425    fn make_word_owned(word: String, quote_style: Option<char>) -> Self {
426        Token::Word(Word {
427            keyword: keyword_lookup(&word, quote_style),
428            value: word,
429            quote_style,
430        })
431    }
432}
433
434/// Case-insensitive keyword lookup using binary search over [`ALL_KEYWORDS`].
435fn keyword_lookup(word: &str, quote_style: Option<char>) -> Keyword {
436    if quote_style.is_some() {
437        return Keyword::NoKeyword;
438    }
439    ALL_KEYWORDS
440        .binary_search_by(|probe| {
441            let probe = probe.as_bytes();
442            let word = word.as_bytes();
443            for (p, w) in probe.iter().zip(word.iter()) {
444                let cmp = p.cmp(&w.to_ascii_uppercase());
445                if cmp != core::cmp::Ordering::Equal {
446                    return cmp;
447                }
448            }
449            probe.len().cmp(&word.len())
450        })
451        .map_or(Keyword::NoKeyword, |x| ALL_KEYWORDS_INDEX[x])
452}
453
454/// A keyword (like SELECT) or an optionally quoted SQL identifier
455#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
456#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
457#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
458pub struct Word {
459    /// The value of the token, without the enclosing quotes, and with the
460    /// escape sequences (if any) processed (TODO: escapes are not handled)
461    pub value: String,
462    /// An identifier can be "quoted" (&lt;delimited identifier> in ANSI parlance).
463    /// The standard and most implementations allow using double quotes for this,
464    /// but some implementations support other quoting styles as well (e.g. \[MS SQL])
465    pub quote_style: Option<char>,
466    /// If the word was not quoted and it matched one of the known keywords,
467    /// this will have one of the values from dialect::keywords, otherwise empty
468    pub keyword: Keyword,
469}
470
471impl fmt::Display for Word {
472    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473        crate::ast::fmt_ident(f, &self.value, self.quote_style)
474    }
475}
476
477impl Word {
478    fn matching_end_quote(ch: char) -> char {
479        match ch {
480            '"' => '"', // ANSI and most dialects
481            '[' => ']', // MS SQL
482            '`' => '`', // MySQL
483            _ => panic!("unexpected quoting style!"),
484        }
485    }
486}
487
488/// Represents whitespace in the input: spaces, newlines, tabs and comments.
489#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
490#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
491#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
492pub enum Whitespace {
493    /// A single space character.
494    Space,
495    /// A newline character.
496    Newline,
497    /// A tab character.
498    Tab,
499    /// A single-line comment (e.g. `-- comment` or `# comment`).
500    /// The `comment` field contains the text, and `prefix` contains the comment prefix.
501    SingleLineComment {
502        /// The content of the comment (without the prefix).
503        comment: String,
504        /// The prefix used for the comment (for example `--` or `#`).
505        prefix: String,
506    },
507
508    /// A multi-line comment (without the `/* ... */` delimiters).
509    MultiLineComment(String),
510}
511
512impl fmt::Display for Whitespace {
513    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514        match self {
515            Whitespace::Space => f.write_str(" "),
516            Whitespace::Newline => f.write_str("\n"),
517            Whitespace::Tab => f.write_str("\t"),
518            Whitespace::SingleLineComment { prefix, comment } => writeln!(f, "{prefix}{comment}"),
519            Whitespace::MultiLineComment(s) => write!(f, "/*{s}*/"),
520        }
521    }
522}
523
524/// Location in input string
525///
526/// # Create an "empty" (unknown) `Location`
527/// ```
528/// # use sqlparser::tokenizer::Location;
529/// let location = Location::empty();
530/// ```
531///
532/// # Create a `Location` from a line and column
533/// ```
534/// # use sqlparser::tokenizer::Location;
535/// let location = Location::new(1, 1);
536/// ```
537///
538/// # Create a `Location` from a pair
539/// ```
540/// # use sqlparser::tokenizer::Location;
541/// let location = Location::from((1, 1));
542/// ```
543#[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)]
544#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
545#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
546pub struct Location {
547    /// Line number, starting from 1.
548    ///
549    /// Note: Line 0 is used for empty spans
550    pub line: u64,
551    /// Line column, starting from 1.
552    ///
553    /// Note: Column 0 is used for empty spans
554    pub column: u64,
555}
556
557impl fmt::Display for Location {
558    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
559        if self.line == 0 {
560            return Ok(());
561        }
562        write!(f, " at Line: {}, Column: {}", self.line, self.column)
563    }
564}
565
566impl fmt::Debug for Location {
567    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568        write!(f, "Location({},{})", self.line, self.column)
569    }
570}
571
572impl Location {
573    /// Return an "empty" / unknown location
574    pub fn empty() -> Self {
575        Self { line: 0, column: 0 }
576    }
577
578    /// Create a new `Location` for a given line and column
579    pub fn new(line: u64, column: u64) -> Self {
580        Self { line, column }
581    }
582
583    /// Create a new location for a given line and column
584    ///
585    /// Alias for [`Self::new`]
586    // TODO: remove / deprecate in favor of` `new` for consistency?
587    pub fn of(line: u64, column: u64) -> Self {
588        Self::new(line, column)
589    }
590
591    /// Combine self and `end` into a new `Span`
592    pub fn span_to(self, end: Self) -> Span {
593        Span { start: self, end }
594    }
595}
596
597impl From<(u64, u64)> for Location {
598    fn from((line, column): (u64, u64)) -> Self {
599        Self { line, column }
600    }
601}
602
603/// A span represents a linear portion of the input string (start, end)
604///
605/// See [Spanned](crate::ast::Spanned) for more information.
606#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)]
607#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
608#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
609pub struct Span {
610    /// Start `Location` (inclusive).
611    pub start: Location,
612    /// End `Location` (inclusive).
613    pub end: Location,
614}
615
616impl fmt::Debug for Span {
617    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618        write!(f, "Span({:?}..{:?})", self.start, self.end)
619    }
620}
621
622impl Span {
623    // An empty span (0, 0) -> (0, 0)
624    // We need a const instance for pattern matching
625    const EMPTY: Span = Self::empty();
626
627    /// Create a new span from a start and end [`Location`]
628    pub fn new(start: Location, end: Location) -> Span {
629        Span { start, end }
630    }
631
632    /// Returns an empty span `(0, 0) -> (0, 0)`
633    ///
634    /// Empty spans represent no knowledge of source location
635    /// See [Spanned](crate::ast::Spanned) for more information.
636    pub const fn empty() -> Span {
637        Span {
638            start: Location { line: 0, column: 0 },
639            end: Location { line: 0, column: 0 },
640        }
641    }
642
643    /// Returns the smallest Span that contains both `self` and `other`
644    /// If either span is [Span::empty], the other span is returned
645    ///
646    /// # Examples
647    /// ```
648    /// # use sqlparser::tokenizer::{Span, Location};
649    /// // line 1, column1 -> line 2, column 5
650    /// let span1 = Span::new(Location::new(1, 1), Location::new(2, 5));
651    /// // line 2, column 3 -> line 3, column 7
652    /// let span2 = Span::new(Location::new(2, 3), Location::new(3, 7));
653    /// // Union of the two is the min/max of the two spans
654    /// // line 1, column 1 -> line 3, column 7
655    /// let union = span1.union(&span2);
656    /// assert_eq!(union, Span::new(Location::new(1, 1), Location::new(3, 7)));
657    /// ```
658    pub fn union(&self, other: &Span) -> Span {
659        // If either span is empty, return the other
660        // this prevents propagating (0, 0) through the tree
661        match (self, other) {
662            (&Span::EMPTY, _) => *other,
663            (_, &Span::EMPTY) => *self,
664            _ => Span {
665                start: cmp::min(self.start, other.start),
666                end: cmp::max(self.end, other.end),
667            },
668        }
669    }
670
671    /// Same as [Span::union] for `Option<Span>`
672    ///
673    /// If `other` is `None`, `self` is returned
674    pub fn union_opt(&self, other: &Option<Span>) -> Span {
675        match other {
676            Some(other) => self.union(other),
677            None => *self,
678        }
679    }
680
681    /// Return the [Span::union] of all spans in the iterator
682    ///
683    /// If the iterator is empty, an empty span is returned
684    ///
685    /// # Example
686    /// ```
687    /// # use sqlparser::tokenizer::{Span, Location};
688    /// let spans = vec![
689    ///     Span::new(Location::new(1, 1), Location::new(2, 5)),
690    ///     Span::new(Location::new(2, 3), Location::new(3, 7)),
691    ///     Span::new(Location::new(3, 1), Location::new(4, 2)),
692    /// ];
693    /// // line 1, column 1 -> line 4, column 2
694    /// assert_eq!(
695    ///   Span::union_iter(spans),
696    ///   Span::new(Location::new(1, 1), Location::new(4, 2))
697    /// );
698    pub fn union_iter<I: IntoIterator<Item = Span>>(iter: I) -> Span {
699        iter.into_iter()
700            .reduce(|acc, item| acc.union(&item))
701            .unwrap_or(Span::empty())
702    }
703}
704
705/// Backwards compatibility struct for [`TokenWithSpan`]
706#[deprecated(since = "0.53.0", note = "please use `TokenWithSpan` instead")]
707pub type TokenWithLocation = TokenWithSpan;
708
709/// A [Token] with [Span] attached to it
710///
711/// This is used to track the location of a token in the input string
712///
713/// # Examples
714/// ```
715/// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithSpan};
716/// // commas @ line 1, column 10
717/// let tok1 = TokenWithSpan::new(
718///   Token::Comma,
719///   Span::new(Location::new(1, 10), Location::new(1, 11)),
720/// );
721/// assert_eq!(tok1, Token::Comma); // can compare the token
722///
723/// // commas @ line 2, column 20
724/// let tok2 = TokenWithSpan::new(
725///   Token::Comma,
726///   Span::new(Location::new(2, 20), Location::new(2, 21)),
727/// );
728/// // same token but different locations are not equal
729/// assert_ne!(tok1, tok2);
730/// ```
731#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
732#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
733#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
734/// A `Token` together with its `Span` (location in the source).
735pub struct TokenWithSpan {
736    /// The token value.
737    pub token: Token,
738    /// The span covering the token in the input.
739    pub span: Span,
740}
741
742impl TokenWithSpan {
743    /// Create a new [`TokenWithSpan`] from a [`Token`] and a [`Span`]
744    pub fn new(token: Token, span: Span) -> Self {
745        Self { token, span }
746    }
747
748    /// Wrap a token with an empty span
749    pub fn wrap(token: Token) -> Self {
750        Self::new(token, Span::empty())
751    }
752
753    /// Wrap a token with a location from `start` to `end`
754    pub fn at(token: Token, start: Location, end: Location) -> Self {
755        Self::new(token, Span::new(start, end))
756    }
757
758    /// Return an EOF token with no location
759    pub fn new_eof() -> Self {
760        Self::wrap(Token::EOF)
761    }
762}
763
764impl PartialEq<Token> for TokenWithSpan {
765    fn eq(&self, other: &Token) -> bool {
766        &self.token == other
767    }
768}
769
770impl PartialEq<TokenWithSpan> for Token {
771    fn eq(&self, other: &TokenWithSpan) -> bool {
772        self == &other.token
773    }
774}
775
776impl fmt::Display for TokenWithSpan {
777    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
778        self.token.fmt(f)
779    }
780}
781
782/// An error reported by the tokenizer, with a human-readable `message` and a `location`.
783#[derive(Debug, PartialEq, Eq)]
784pub struct TokenizerError {
785    /// A descriptive error message.
786    pub message: String,
787    /// The `Location` where the error was detected.
788    pub location: Location,
789}
790
791impl fmt::Display for TokenizerError {
792    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
793        write!(f, "{}{}", self.message, self.location,)
794    }
795}
796
797impl core::error::Error for TokenizerError {}
798
799struct State<'a> {
800    peekable: Peekable<Chars<'a>>,
801    line: u64,
802    col: u64,
803}
804
805impl State<'_> {
806    /// return the next character and advance the stream
807    pub fn next(&mut self) -> Option<char> {
808        match self.peekable.next() {
809            None => None,
810            Some(s) => {
811                if s == '\n' {
812                    self.line += 1;
813                    self.col = 1;
814                } else {
815                    self.col += 1;
816                }
817                Some(s)
818            }
819        }
820    }
821
822    /// return the next character but do not advance the stream
823    pub fn peek(&mut self) -> Option<&char> {
824        self.peekable.peek()
825    }
826
827    /// Return the current `Location` (line and column)
828    pub fn location(&self) -> Location {
829        Location {
830            line: self.line,
831            column: self.col,
832        }
833    }
834}
835
836/// Represents how many quote characters enclose a string literal.
837#[derive(Copy, Clone)]
838enum NumStringQuoteChars {
839    /// e.g. `"abc"`, `'abc'`, `r'abc'`
840    One,
841    /// e.g. `"""abc"""`, `'''abc'''`, `r'''abc'''`
842    Many(NonZeroU8),
843}
844
845/// Settings for tokenizing a quoted string literal.
846struct TokenizeQuotedStringSettings {
847    /// The character used to quote the string.
848    quote_style: char,
849    /// Represents how many quotes characters enclose the string literal.
850    num_quote_chars: NumStringQuoteChars,
851    /// The number of opening quotes left to consume, before parsing
852    /// the remaining string literal.
853    /// For example: given initial string `"""abc"""`. If the caller has
854    /// already parsed the first quote for some reason, then this value
855    /// is set to 1, flagging to look to consume only 2 leading quotes.
856    num_opening_quotes_to_consume: u8,
857    /// True if the string uses backslash escaping of special characters
858    /// e.g `'abc\ndef\'ghi'
859    backslash_escape: bool,
860}
861
862/// SQL Tokenizer
863pub struct Tokenizer<'a> {
864    dialect: &'a dyn Dialect,
865    query: &'a str,
866    /// If true (the default), the tokenizer will un-escape literal
867    /// SQL strings See [`Tokenizer::with_unescape`] for more details.
868    unescape: bool,
869}
870
871impl<'a> Tokenizer<'a> {
872    /// Create a new SQL tokenizer for the specified SQL statement
873    ///
874    /// ```
875    /// # use sqlparser::tokenizer::{Token, Whitespace, Tokenizer};
876    /// # use sqlparser::dialect::GenericDialect;
877    /// # let dialect = GenericDialect{};
878    /// let query = r#"SELECT 'foo'"#;
879    ///
880    /// // Parsing the query
881    /// let tokens = Tokenizer::new(&dialect, &query).tokenize().unwrap();
882    ///
883    /// assert_eq!(tokens, vec![
884    ///   Token::make_word("SELECT", None),
885    ///   Token::Whitespace(Whitespace::Space),
886    ///   Token::SingleQuotedString("foo".to_string()),
887    /// ]);
888    pub fn new(dialect: &'a dyn Dialect, query: &'a str) -> Self {
889        Self {
890            dialect,
891            query,
892            unescape: true,
893        }
894    }
895
896    /// Set unescape mode
897    ///
898    /// When true (default) the tokenizer unescapes literal values
899    /// (for example, `""` in SQL is unescaped to the literal `"`).
900    ///
901    /// When false, the tokenizer provides the raw strings as provided
902    /// in the query.  This can be helpful for programs that wish to
903    /// recover the *exact* original query text without normalizing
904    /// the escaping
905    ///
906    /// # Example
907    ///
908    /// ```
909    /// # use sqlparser::tokenizer::{Token, Tokenizer};
910    /// # use sqlparser::dialect::GenericDialect;
911    /// # let dialect = GenericDialect{};
912    /// let query = r#""Foo "" Bar""#;
913    /// let unescaped = Token::make_word(r#"Foo " Bar"#, Some('"'));
914    /// let original  = Token::make_word(r#"Foo "" Bar"#, Some('"'));
915    ///
916    /// // Parsing with unescaping (default)
917    /// let tokens = Tokenizer::new(&dialect, &query).tokenize().unwrap();
918    /// assert_eq!(tokens, vec![unescaped]);
919    ///
920    /// // Parsing with unescape = false
921    /// let tokens = Tokenizer::new(&dialect, &query)
922    ///    .with_unescape(false)
923    ///    .tokenize().unwrap();
924    /// assert_eq!(tokens, vec![original]);
925    /// ```
926    pub fn with_unescape(mut self, unescape: bool) -> Self {
927        self.unescape = unescape;
928        self
929    }
930
931    /// Tokenize the statement and produce a vector of tokens
932    pub fn tokenize(&mut self) -> Result<Vec<Token>, TokenizerError> {
933        let twl = self.tokenize_with_location()?;
934        Ok(twl.into_iter().map(|t| t.token).collect())
935    }
936
937    /// Tokenize the statement and produce a vector of tokens with location information
938    pub fn tokenize_with_location(&mut self) -> Result<Vec<TokenWithSpan>, TokenizerError> {
939        let mut tokens: Vec<TokenWithSpan> = vec![];
940        self.tokenize_with_location_into_buf(&mut tokens)
941            .map(|_| tokens)
942    }
943
944    /// Tokenize the statement and append tokens with location information into the provided buffer.
945    /// If an error is thrown, the buffer will contain all tokens that were successfully parsed before the error.
946    pub fn tokenize_with_location_into_buf(
947        &mut self,
948        buf: &mut Vec<TokenWithSpan>,
949    ) -> Result<(), TokenizerError> {
950        self.tokenize_with_location_into_buf_with_mapper(buf, |token| token)
951    }
952
953    /// Tokenize the statement and produce a vector of tokens, mapping each token
954    /// with provided `mapper`
955    pub fn tokenize_with_location_into_buf_with_mapper(
956        &mut self,
957        buf: &mut Vec<TokenWithSpan>,
958        mut mapper: impl FnMut(TokenWithSpan) -> TokenWithSpan,
959    ) -> Result<(), TokenizerError> {
960        let mut state = State {
961            peekable: self.query.chars().peekable(),
962            line: 1,
963            col: 1,
964        };
965
966        let mut location = state.location();
967        while let Some(token) = self.next_token(&mut state, buf.last().map(|t| &t.token))? {
968            let span = location.span_to(state.location());
969
970            // Check if this is a multiline comment hint that should be expanded
971            match &token {
972                Token::Whitespace(Whitespace::MultiLineComment(comment))
973                    if self.dialect.supports_multiline_comment_hints()
974                        && comment.starts_with('!') =>
975                {
976                    // Re-tokenize the hints and add them to the buffer
977                    self.tokenize_comment_hints(comment, span, buf, &mut mapper)?;
978                }
979                _ => {
980                    buf.push(mapper(TokenWithSpan { token, span }));
981                }
982            }
983
984            location = state.location();
985        }
986        Ok(())
987    }
988
989    /// Re-tokenize optimizer hints from a multiline comment and add them to the buffer.
990    /// For example, `/*!50110 KEY_BLOCK_SIZE = 1024*/` becomes tokens for `KEY_BLOCK_SIZE = 1024`
991    fn tokenize_comment_hints(
992        &self,
993        comment: &str,
994        span: Span,
995        buf: &mut Vec<TokenWithSpan>,
996        mut mapper: impl FnMut(TokenWithSpan) -> TokenWithSpan,
997    ) -> Result<(), TokenizerError> {
998        // Strip the leading '!' and any version digits (e.g., "50110")
999        let hint_content = comment
1000            .strip_prefix('!')
1001            .unwrap_or(comment)
1002            .trim_start_matches(|c: char| c.is_ascii_digit());
1003
1004        // If there's no content after stripping, nothing to tokenize
1005        if hint_content.is_empty() {
1006            return Ok(());
1007        }
1008
1009        // Create a new tokenizer for the hint content
1010        let inner = Tokenizer::new(self.dialect, hint_content).with_unescape(self.unescape);
1011
1012        // Create a state for tracking position within the hint
1013        let mut state = State {
1014            peekable: hint_content.chars().peekable(),
1015            line: span.start.line,
1016            col: span.start.column,
1017        };
1018
1019        // Tokenize the hint content and add tokens to the buffer
1020        let mut location = state.location();
1021        while let Some(token) = inner.next_token(&mut state, buf.last().map(|t| &t.token))? {
1022            let token_span = location.span_to(state.location());
1023            buf.push(mapper(TokenWithSpan {
1024                token,
1025                span: token_span,
1026            }));
1027            location = state.location();
1028        }
1029
1030        Ok(())
1031    }
1032
1033    // Tokenize the identifier or keywords in `ch`
1034    fn tokenize_identifier_or_keyword(
1035        &self,
1036        ch: impl IntoIterator<Item = char>,
1037        chars: &mut State,
1038    ) -> Result<Option<Token>, TokenizerError> {
1039        chars.next(); // consume the first char
1040        let ch: String = ch.into_iter().collect();
1041        let word = self.tokenize_word(ch, chars);
1042
1043        // TODO: implement parsing of exponent here
1044        if word.chars().all(|x| x.is_ascii_digit() || x == '.') {
1045            let mut inner_state = State {
1046                peekable: word.chars().peekable(),
1047                line: 0,
1048                col: 0,
1049            };
1050            let mut s = peeking_take_while(&mut inner_state, |ch| matches!(ch, '0'..='9' | '.'));
1051            let s2 = peeking_take_while(chars, |ch| matches!(ch, '0'..='9' | '.'));
1052            s += s2.as_str();
1053            return Ok(Some(Token::Number(s, false)));
1054        }
1055
1056        Ok(Some(Token::make_word_owned(word, None)))
1057    }
1058
1059    /// Get the next token or return None
1060    fn next_token(
1061        &self,
1062        chars: &mut State,
1063        prev_token: Option<&Token>,
1064    ) -> Result<Option<Token>, TokenizerError> {
1065        match chars.peek() {
1066            Some(&ch) => match ch {
1067                ' ' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Space)),
1068                '\t' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Tab)),
1069                '\n' => self.consume_and_return(chars, Token::Whitespace(Whitespace::Newline)),
1070                '\r' => {
1071                    // Emit a single Whitespace::Newline token for \r and \r\n
1072                    chars.next();
1073                    if let Some('\n') = chars.peek() {
1074                        chars.next();
1075                    }
1076                    Ok(Some(Token::Whitespace(Whitespace::Newline)))
1077                }
1078                // BigQuery and MySQL use b or B for byte string literal, Postgres for bit strings
1079                b @ 'B' | b @ 'b' if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | MySqlDialect | GenericDialect) =>
1080                {
1081                    chars.next(); // consume
1082                    match chars.peek() {
1083                        Some('\'') => {
1084                            if self.dialect.supports_triple_quoted_string() {
1085                                return self
1086                                    .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1087                                        chars,
1088                                        '\'',
1089                                        false,
1090                                        Token::SingleQuotedByteStringLiteral,
1091                                        Token::TripleSingleQuotedByteStringLiteral,
1092                                    );
1093                            }
1094                            let s = self.tokenize_single_quoted_string(chars, '\'', false)?;
1095                            Ok(Some(Token::SingleQuotedByteStringLiteral(s)))
1096                        }
1097                        Some('\"') => {
1098                            if self.dialect.supports_triple_quoted_string() {
1099                                return self
1100                                    .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1101                                        chars,
1102                                        '"',
1103                                        false,
1104                                        Token::DoubleQuotedByteStringLiteral,
1105                                        Token::TripleDoubleQuotedByteStringLiteral,
1106                                    );
1107                            }
1108                            let s = self.tokenize_single_quoted_string(chars, '\"', false)?;
1109                            Ok(Some(Token::DoubleQuotedByteStringLiteral(s)))
1110                        }
1111                        _ => {
1112                            // regular identifier starting with an "b" or "B"
1113                            let s = self.tokenize_word(b, chars);
1114                            Ok(Some(Token::make_word_owned(s, None)))
1115                        }
1116                    }
1117                }
1118                // BigQuery uses r or R for raw string literal
1119                b @ 'R' | b @ 'r' if dialect_of!(self is BigQueryDialect | GenericDialect) => {
1120                    chars.next(); // consume
1121                    match chars.peek() {
1122                        Some('\'') => self
1123                            .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1124                                chars,
1125                                '\'',
1126                                false,
1127                                Token::SingleQuotedRawStringLiteral,
1128                                Token::TripleSingleQuotedRawStringLiteral,
1129                            ),
1130                        Some('\"') => self
1131                            .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1132                                chars,
1133                                '"',
1134                                false,
1135                                Token::DoubleQuotedRawStringLiteral,
1136                                Token::TripleDoubleQuotedRawStringLiteral,
1137                            ),
1138                        _ => {
1139                            // regular identifier starting with an "r" or "R"
1140                            let s = self.tokenize_word(b, chars);
1141                            Ok(Some(Token::make_word_owned(s, None)))
1142                        }
1143                    }
1144                }
1145                // Redshift uses lower case n for national string literal
1146                n @ 'N' | n @ 'n' => {
1147                    chars.next(); // consume, to check the next char
1148                    match chars.peek() {
1149                        Some('\'') if self.dialect.supports_national_string_literal() => {
1150                            // N'...' - a <national character string literal>
1151                            let backslash_escape =
1152                                self.dialect.supports_string_literal_backslash_escape();
1153                            let s =
1154                                self.tokenize_single_quoted_string(chars, '\'', backslash_escape)?;
1155                            Ok(Some(Token::NationalStringLiteral(s)))
1156                        }
1157                        Some(&q @ 'q') | Some(&q @ 'Q')
1158                            if self.dialect.supports_quote_delimited_string() =>
1159                        {
1160                            chars.next(); // consume and check the next char
1161                            if let Some('\'') = chars.peek() {
1162                                self.tokenize_quote_delimited_string(chars, &[n, q])
1163                                    .map(|s| Some(Token::NationalQuoteDelimitedStringLiteral(s)))
1164                            } else {
1165                                let s = self.tokenize_word(String::from_iter([n, q]), chars);
1166                                Ok(Some(Token::make_word_owned(s, None)))
1167                            }
1168                        }
1169                        _ => {
1170                            // regular identifier starting with an "N"
1171                            let s = self.tokenize_word(n, chars);
1172                            Ok(Some(Token::make_word_owned(s, None)))
1173                        }
1174                    }
1175                }
1176                q @ 'Q' | q @ 'q' if self.dialect.supports_quote_delimited_string() => {
1177                    chars.next(); // consume and check the next char
1178                    if let Some('\'') = chars.peek() {
1179                        self.tokenize_quote_delimited_string(chars, &[q])
1180                            .map(|s| Some(Token::QuoteDelimitedStringLiteral(s)))
1181                    } else {
1182                        let s = self.tokenize_word(q, chars);
1183                        Ok(Some(Token::make_word_owned(s, None)))
1184                    }
1185                }
1186                // PostgreSQL accepts "escape" string constants, which are an extension to the SQL standard.
1187                x @ 'e' | x @ 'E' if self.dialect.supports_string_escape_constant() => {
1188                    let starting_loc = chars.location();
1189                    chars.next(); // consume, to check the next char
1190                    match chars.peek() {
1191                        Some('\'') => {
1192                            let s =
1193                                self.tokenize_escaped_single_quoted_string(starting_loc, chars)?;
1194                            Ok(Some(Token::EscapedStringLiteral(s)))
1195                        }
1196                        _ => {
1197                            // regular identifier starting with an "E" or "e"
1198                            let s = self.tokenize_word(x, chars);
1199                            Ok(Some(Token::make_word_owned(s, None)))
1200                        }
1201                    }
1202                }
1203                // Unicode string literals like U&'first \000A second' are supported in some dialects, including PostgreSQL
1204                x @ 'u' | x @ 'U' if self.dialect.supports_unicode_string_literal() => {
1205                    chars.next(); // consume, to check the next char
1206                    if chars.peek() == Some(&'&') {
1207                        // we cannot advance the iterator here, as we need to consume the '&' later if the 'u' was an identifier
1208                        let mut chars_clone = chars.peekable.clone();
1209                        chars_clone.next(); // consume the '&' in the clone
1210                        if chars_clone.peek() == Some(&'\'') {
1211                            chars.next(); // consume the '&' in the original iterator
1212                            let s = unescape_unicode_single_quoted_string(chars)?;
1213                            return Ok(Some(Token::UnicodeStringLiteral(s)));
1214                        }
1215                    }
1216                    // regular identifier starting with an "U" or "u"
1217                    let s = self.tokenize_word(x, chars);
1218                    Ok(Some(Token::make_word_owned(s, None)))
1219                }
1220                // The spec only allows an uppercase 'X' to introduce a hex
1221                // string, but PostgreSQL, at least, allows a lowercase 'x' too.
1222                x @ 'x' | x @ 'X' => {
1223                    chars.next(); // consume, to check the next char
1224                    match chars.peek() {
1225                        Some('\'') => {
1226                            // X'...' - a <binary string literal>
1227                            let s = self.tokenize_single_quoted_string(chars, '\'', true)?;
1228                            Ok(Some(Token::HexStringLiteral(s)))
1229                        }
1230                        _ => {
1231                            // regular identifier starting with an "X"
1232                            let s = self.tokenize_word(x, chars);
1233                            Ok(Some(Token::make_word_owned(s, None)))
1234                        }
1235                    }
1236                }
1237                // single quoted string
1238                '\'' => {
1239                    if self.dialect.supports_triple_quoted_string() {
1240                        return self
1241                            .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1242                                chars,
1243                                '\'',
1244                                self.dialect.supports_string_literal_backslash_escape(),
1245                                Token::SingleQuotedString,
1246                                Token::TripleSingleQuotedString,
1247                            );
1248                    }
1249                    let s = self.tokenize_single_quoted_string(
1250                        chars,
1251                        '\'',
1252                        self.dialect.supports_string_literal_backslash_escape(),
1253                    )?;
1254
1255                    Ok(Some(Token::SingleQuotedString(s)))
1256                }
1257                // double quoted string
1258                '\"' if !self.dialect.is_delimited_identifier_start(ch)
1259                    && !self.dialect.is_identifier_start(ch) =>
1260                {
1261                    if self.dialect.supports_triple_quoted_string() {
1262                        return self
1263                            .tokenize_single_or_triple_quoted_string::<fn(String) -> Token>(
1264                                chars,
1265                                '"',
1266                                self.dialect.supports_string_literal_backslash_escape(),
1267                                Token::DoubleQuotedString,
1268                                Token::TripleDoubleQuotedString,
1269                            );
1270                    }
1271                    let s = self.tokenize_single_quoted_string(
1272                        chars,
1273                        '"',
1274                        self.dialect.supports_string_literal_backslash_escape(),
1275                    )?;
1276
1277                    Ok(Some(Token::DoubleQuotedString(s)))
1278                }
1279                // delimited (quoted) identifier
1280                quote_start if self.dialect.is_delimited_identifier_start(ch) => {
1281                    let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1282                    Ok(Some(Token::make_word_owned(word, Some(quote_start))))
1283                }
1284                // Potentially nested delimited (quoted) identifier
1285                quote_start
1286                    if self
1287                        .dialect
1288                        .is_nested_delimited_identifier_start(quote_start)
1289                        && self
1290                            .dialect
1291                            .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1292                            .is_some() =>
1293                {
1294                    let Some((quote_start, nested_quote_start)) = self
1295                        .dialect
1296                        .peek_nested_delimited_identifier_quotes(chars.peekable.clone())
1297                    else {
1298                        return self.tokenizer_error(
1299                            chars.location(),
1300                            format!("Expected nested delimiter '{quote_start}' before EOF."),
1301                        );
1302                    };
1303
1304                    let Some(nested_quote_start) = nested_quote_start else {
1305                        let word = self.tokenize_quoted_identifier(quote_start, chars)?;
1306                        return Ok(Some(Token::make_word_owned(word, Some(quote_start))));
1307                    };
1308
1309                    let mut word = vec![];
1310                    let quote_end = Word::matching_end_quote(quote_start);
1311                    let nested_quote_end = Word::matching_end_quote(nested_quote_start);
1312                    let error_loc = chars.location();
1313
1314                    chars.next(); // skip the first delimiter
1315                    peeking_take_while(chars, |ch| ch.is_whitespace());
1316                    if chars.peek() != Some(&nested_quote_start) {
1317                        return self.tokenizer_error(
1318                            error_loc,
1319                            format!("Expected nested delimiter '{nested_quote_start}' before EOF."),
1320                        );
1321                    }
1322                    word.push(nested_quote_start.into());
1323                    word.push(self.tokenize_quoted_identifier(nested_quote_end, chars)?);
1324                    word.push(nested_quote_end.into());
1325                    peeking_take_while(chars, |ch| ch.is_whitespace());
1326                    if chars.peek() != Some(&quote_end) {
1327                        return self.tokenizer_error(
1328                            error_loc,
1329                            format!("Expected close delimiter '{quote_end}' before EOF."),
1330                        );
1331                    }
1332                    chars.next(); // skip close delimiter
1333
1334                    Ok(Some(Token::make_word_owned(
1335                        word.concat(),
1336                        Some(quote_start),
1337                    )))
1338                }
1339                // numbers and period
1340                '0'..='9' | '.' => {
1341                    // special case where if ._ is encountered after a word then that word
1342                    // is a table and the _ is the start of the col name.
1343                    // if the prev token is not a word, then this is not a valid sql
1344                    // word or number.
1345                    if ch == '.' && chars.peekable.clone().nth(1) == Some('_') {
1346                        if let Some(Token::Word(_)) = prev_token {
1347                            chars.next();
1348                            return Ok(Some(Token::Period));
1349                        }
1350
1351                        return self.tokenizer_error(
1352                            chars.location(),
1353                            "Unexpected character '_'".to_string(),
1354                        );
1355                    }
1356
1357                    // A period directly after an identifier, `)`, `]` or a tuple index
1358                    // starts a field access such as `t.1`, `(1, 2).1` or `t.1.2`, never a float.
1359                    if ch == '.'
1360                        && self.dialect.supports_tuple_element_access()
1361                        && matches!(
1362                            prev_token,
1363                            Some(
1364                                Token::Word(_)
1365                                    | Token::RParen
1366                                    | Token::RBracket
1367                                    | Token::Number(..)
1368                            )
1369                        )
1370                    {
1371                        chars.next();
1372                        return Ok(Some(Token::Period));
1373                    }
1374
1375                    let mut s = self.tokenize_number_part(chars, |ch| ch.is_ascii_digit())?;
1376
1377                    // A tuple index is an integer, so `t.1.2` is `(t.1).2`.
1378                    if !s.is_empty()
1379                        && self.dialect.supports_tuple_element_access()
1380                        && prev_token == Some(&Token::Period)
1381                    {
1382                        return Ok(Some(Token::Number(s, false)));
1383                    }
1384
1385                    // match binary literal that starts with 0x
1386                    if s == "0" && chars.peek() == Some(&'x') {
1387                        chars.next();
1388                        let s2 = self.tokenize_number_part(chars, |ch| ch.is_ascii_hexdigit())?;
1389                        return Ok(Some(Token::HexStringLiteral(s2)));
1390                    }
1391
1392                    // match one period
1393                    if let Some('.') = chars.peek() {
1394                        s.push('.');
1395                        chars.next();
1396                    }
1397
1398                    // If the dialect supports identifiers that start with a numeric prefix
1399                    // and we have now consumed a dot, check if the previous token was a Word.
1400                    // If so, what follows is definitely not part of a decimal number and
1401                    // we should yield the dot as a dedicated token so compound identifiers
1402                    // starting with digits can be parsed correctly.
1403                    if s == "." && self.dialect.supports_numeric_prefix() {
1404                        if let Some(Token::Word(_)) = prev_token {
1405                            return Ok(Some(Token::Period));
1406                        }
1407                    }
1408
1409                    // Consume fractional digits.
1410                    s += &self.tokenize_number_part(chars, |ch| ch.is_ascii_digit())?;
1411
1412                    // No fraction -> Token::Period
1413                    if s == "." {
1414                        return Ok(Some(Token::Period));
1415                    }
1416
1417                    // Parse exponent as number
1418                    let mut exponent_part = String::new();
1419                    if chars.peek() == Some(&'e') || chars.peek() == Some(&'E') {
1420                        let mut char_clone = chars.peekable.clone();
1421                        exponent_part.push(char_clone.next().unwrap());
1422
1423                        // Optional sign
1424                        match char_clone.peek() {
1425                            Some(&c) if matches!(c, '+' | '-') => {
1426                                exponent_part.push(c);
1427                                char_clone.next();
1428                            }
1429                            _ => (),
1430                        }
1431
1432                        match char_clone.peek() {
1433                            // Definitely an exponent, get original iterator up to speed and use it
1434                            Some(&c)
1435                                if c.is_ascii_digit()
1436                                    || (c == '_'
1437                                        && self.dialect.supports_numeric_literal_underscores()) =>
1438                            {
1439                                for _ in 0..exponent_part.len() {
1440                                    chars.next();
1441                                }
1442                                exponent_part +=
1443                                    &self.tokenize_number_part(chars, |ch| ch.is_ascii_digit())?;
1444                                s += exponent_part.as_str();
1445                            }
1446                            // Not an exponent, discard the work done
1447                            _ => (),
1448                        }
1449                    }
1450
1451                    // If the dialect supports identifiers that start with a numeric prefix,
1452                    // we need to check if the value is in fact an identifier and must thus
1453                    // be tokenized as a word.
1454                    if self.dialect.supports_numeric_prefix() {
1455                        if exponent_part.is_empty() {
1456                            // If it is not a number with an exponent, it may be
1457                            // an identifier starting with digits.
1458                            let word =
1459                                peeking_take_while(chars, |ch| self.dialect.is_identifier_part(ch));
1460
1461                            if !word.is_empty() {
1462                                s += word.as_str();
1463                                return Ok(Some(Token::make_word_owned(s, None)));
1464                            }
1465                        } else if prev_token == Some(&Token::Period) {
1466                            // If the previous token was a period, thus not belonging to a number,
1467                            // the value we have is part of an identifier.
1468                            return Ok(Some(Token::make_word_owned(s, None)));
1469                        }
1470                    }
1471
1472                    let long = if chars.peek() == Some(&'L') {
1473                        chars.next();
1474                        true
1475                    } else {
1476                        false
1477                    };
1478                    Ok(Some(Token::Number(s, long)))
1479                }
1480                // punctuation
1481                '(' => self.consume_and_return(chars, Token::LParen),
1482                ')' => self.consume_and_return(chars, Token::RParen),
1483                ',' => self.consume_and_return(chars, Token::Comma),
1484                // operators
1485                '-' => {
1486                    chars.next(); // consume the '-'
1487
1488                    match chars.peek() {
1489                        Some('-') => {
1490                            let mut is_comment = true;
1491                            if self.dialect.requires_single_line_comment_whitespace() {
1492                                is_comment = chars
1493                                    .peekable
1494                                    .clone()
1495                                    .nth(1)
1496                                    .is_some_and(char::is_whitespace);
1497                            }
1498
1499                            if is_comment {
1500                                chars.next(); // consume second '-'
1501                                let comment = self.tokenize_single_line_comment(chars);
1502                                return Ok(Some(Token::Whitespace(
1503                                    Whitespace::SingleLineComment {
1504                                        prefix: "--".to_owned(),
1505                                        comment,
1506                                    },
1507                                )));
1508                            }
1509
1510                            self.start_binop(chars, "-", Token::Minus)
1511                        }
1512                        Some('>') => {
1513                            chars.next();
1514                            match chars.peek() {
1515                                Some('>') => self.consume_for_binop(chars, "->>", Token::LongArrow),
1516                                _ => self.start_binop(chars, "->", Token::Arrow),
1517                            }
1518                        }
1519                        // a regular '-' operator
1520                        _ => self.start_binop(chars, "-", Token::Minus),
1521                    }
1522                }
1523                '/' => {
1524                    chars.next(); // consume the '/'
1525                    match chars.peek() {
1526                        Some('*') => {
1527                            chars.next(); // consume the '*', starting a multi-line comment
1528                            self.tokenize_multiline_comment(chars)
1529                        }
1530                        Some('/') if dialect_of!(self is SnowflakeDialect) => {
1531                            chars.next(); // consume the second '/', starting a snowflake single-line comment
1532                            let comment = self.tokenize_single_line_comment(chars);
1533                            Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1534                                prefix: "//".to_owned(),
1535                                comment,
1536                            })))
1537                        }
1538                        Some('/') if dialect_of!(self is DuckDbDialect | GenericDialect) => {
1539                            self.consume_and_return(chars, Token::DuckIntDiv)
1540                        }
1541                        // a regular '/' operator
1542                        _ => Ok(Some(Token::Div)),
1543                    }
1544                }
1545                '+' => self.consume_and_return(chars, Token::Plus),
1546                '*' => self.consume_and_return(chars, Token::Mul),
1547                '%' => {
1548                    chars.next(); // advance past '%'
1549                    match chars.peek() {
1550                        Some(s) if s.is_whitespace() => Ok(Some(Token::Mod)),
1551                        Some(sch) if self.dialect.is_identifier_start('%') => {
1552                            self.tokenize_identifier_or_keyword([ch, *sch], chars)
1553                        }
1554                        _ => self.start_binop(chars, "%", Token::Mod),
1555                    }
1556                }
1557                '|' => {
1558                    chars.next(); // consume the '|'
1559                    match chars.peek() {
1560                        Some('/') => self.consume_for_binop(chars, "|/", Token::PGSquareRoot),
1561                        Some('|') => {
1562                            chars.next(); // consume the second '|'
1563                            match chars.peek() {
1564                                Some('/') => {
1565                                    self.consume_for_binop(chars, "||/", Token::PGCubeRoot)
1566                                }
1567                                _ => self.start_binop(chars, "||", Token::StringConcat),
1568                            }
1569                        }
1570                        Some('&') if self.dialect.supports_geometric_types() => {
1571                            chars.next(); // consume
1572                            match chars.peek() {
1573                                Some('>') => self.consume_for_binop(
1574                                    chars,
1575                                    "|&>",
1576                                    Token::VerticalBarAmpersandRightAngleBracket,
1577                                ),
1578                                _ => self.start_binop_opt(chars, "|&", None),
1579                            }
1580                        }
1581                        Some('>') if self.dialect.supports_geometric_types() => {
1582                            chars.next(); // consume
1583                            match chars.peek() {
1584                                Some('>') => self.consume_for_binop(
1585                                    chars,
1586                                    "|>>",
1587                                    Token::VerticalBarShiftRight,
1588                                ),
1589                                _ => self.start_binop_opt(chars, "|>", None),
1590                            }
1591                        }
1592                        Some('>') if self.dialect.supports_pipe_operator() => {
1593                            self.consume_for_binop(chars, "|>", Token::VerticalBarRightAngleBracket)
1594                        }
1595                        // Bitshift '|' operator
1596                        _ => self.start_binop(chars, "|", Token::Pipe),
1597                    }
1598                }
1599                '=' => {
1600                    chars.next(); // consume
1601                    match chars.peek() {
1602                        Some('>') => self.consume_and_return(chars, Token::RArrow),
1603                        Some('=') => self.consume_and_return(chars, Token::DoubleEq),
1604                        _ => Ok(Some(Token::Eq)),
1605                    }
1606                }
1607                '!' => {
1608                    chars.next(); // consume
1609                    match chars.peek() {
1610                        Some('=') => self.consume_and_return(chars, Token::Neq),
1611                        Some('!') => self.consume_and_return(chars, Token::DoubleExclamationMark),
1612                        Some('~') => {
1613                            chars.next();
1614                            match chars.peek() {
1615                                Some('*') => self
1616                                    .consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
1617                                Some('~') => {
1618                                    chars.next();
1619                                    match chars.peek() {
1620                                        Some('*') => self.consume_and_return(
1621                                            chars,
1622                                            Token::ExclamationMarkDoubleTildeAsterisk,
1623                                        ),
1624                                        _ => Ok(Some(Token::ExclamationMarkDoubleTilde)),
1625                                    }
1626                                }
1627                                _ => Ok(Some(Token::ExclamationMarkTilde)),
1628                            }
1629                        }
1630                        _ => Ok(Some(Token::ExclamationMark)),
1631                    }
1632                }
1633                '<' => {
1634                    chars.next(); // consume
1635                    match chars.peek() {
1636                        Some('=') => {
1637                            chars.next();
1638                            match chars.peek() {
1639                                Some('>') => self.consume_for_binop(chars, "<=>", Token::Spaceship),
1640                                // `<=+` and `<=-` are not valid combined operators; treat `<=` as
1641                                // the operator and leave `+`/`-` to be tokenized separately.
1642                                Some('+') | Some('-') => Ok(Some(Token::LtEq)),
1643                                _ => self.start_binop(chars, "<=", Token::LtEq),
1644                            }
1645                        }
1646                        Some('|') if self.dialect.supports_geometric_types() => {
1647                            self.consume_for_binop(chars, "<<|", Token::ShiftLeftVerticalBar)
1648                        }
1649                        Some('>') => self.consume_for_binop(chars, "<>", Token::Neq),
1650                        Some('<') if self.dialect.supports_geometric_types() => {
1651                            chars.next(); // consume
1652                            match chars.peek() {
1653                                Some('|') => self.consume_for_binop(
1654                                    chars,
1655                                    "<<|",
1656                                    Token::ShiftLeftVerticalBar,
1657                                ),
1658                                _ => self.start_binop(chars, "<<", Token::ShiftLeft),
1659                            }
1660                        }
1661                        Some('<') => self.consume_for_binop(chars, "<<", Token::ShiftLeft),
1662                        // `<+` is not a valid combined operator; treat `<` as the operator
1663                        // and leave `+` to be tokenized separately.
1664                        Some('+') => Ok(Some(Token::Lt)),
1665                        Some('-') if self.dialect.supports_geometric_types() => {
1666                            if chars.peekable.clone().nth(1) == Some('>') {
1667                                chars.next(); // consume `-`
1668                                self.consume_for_binop(chars, "<->", Token::TwoWayArrow)
1669                            } else {
1670                                Ok(Some(Token::Lt))
1671                            }
1672                        }
1673                        Some('^') if self.dialect.supports_geometric_types() => {
1674                            self.consume_for_binop(chars, "<^", Token::LeftAngleBracketCaret)
1675                        }
1676                        Some('@') => self.consume_for_binop(chars, "<@", Token::ArrowAt),
1677                        _ => self.start_binop(chars, "<", Token::Lt),
1678                    }
1679                }
1680                '>' => {
1681                    chars.next(); // consume
1682                    match chars.peek() {
1683                        Some('=') => self.consume_for_binop(chars, ">=", Token::GtEq),
1684                        Some('>') => self.consume_for_binop(chars, ">>", Token::ShiftRight),
1685                        Some('^') if self.dialect.supports_geometric_types() => {
1686                            self.consume_for_binop(chars, ">^", Token::RightAngleBracketCaret)
1687                        }
1688                        _ => self.start_binop(chars, ">", Token::Gt),
1689                    }
1690                }
1691                ':' => {
1692                    chars.next();
1693                    match chars.peek() {
1694                        Some(':') => self.consume_and_return(chars, Token::DoubleColon),
1695                        Some('=') => self.consume_and_return(chars, Token::Assignment),
1696                        _ => Ok(Some(Token::Colon)),
1697                    }
1698                }
1699                ';' => self.consume_and_return(chars, Token::SemiColon),
1700                '\\' => self.consume_and_return(chars, Token::Backslash),
1701                '[' => self.consume_and_return(chars, Token::LBracket),
1702                ']' => self.consume_and_return(chars, Token::RBracket),
1703                '&' => {
1704                    chars.next(); // consume the '&'
1705                    match chars.peek() {
1706                        Some('>') if self.dialect.supports_geometric_types() => {
1707                            chars.next();
1708                            self.consume_and_return(chars, Token::AmpersandRightAngleBracket)
1709                        }
1710                        Some('<') if self.dialect.supports_geometric_types() => {
1711                            chars.next(); // consume
1712                            match chars.peek() {
1713                                Some('|') => self.consume_and_return(
1714                                    chars,
1715                                    Token::AmpersandLeftAngleBracketVerticalBar,
1716                                ),
1717                                _ => {
1718                                    self.start_binop(chars, "&<", Token::AmpersandLeftAngleBracket)
1719                                }
1720                            }
1721                        }
1722                        Some('&') => {
1723                            chars.next(); // consume the second '&'
1724                            self.start_binop(chars, "&&", Token::Overlap)
1725                        }
1726                        // Bitshift '&' operator
1727                        _ => self.start_binop(chars, "&", Token::Ampersand),
1728                    }
1729                }
1730                '^' => {
1731                    chars.next(); // consume the '^'
1732                    match chars.peek() {
1733                        Some('@') => self.consume_and_return(chars, Token::CaretAt),
1734                        _ => Ok(Some(Token::Caret)),
1735                    }
1736                }
1737                '{' => self.consume_and_return(chars, Token::LBrace),
1738                '}' => self.consume_and_return(chars, Token::RBrace),
1739                '#' if dialect_of!(self is SnowflakeDialect | BigQueryDialect | MySqlDialect | HiveDialect) =>
1740                {
1741                    chars.next(); // consume the '#', starting a snowflake single-line comment
1742                    let comment = self.tokenize_single_line_comment(chars);
1743                    Ok(Some(Token::Whitespace(Whitespace::SingleLineComment {
1744                        prefix: "#".to_owned(),
1745                        comment,
1746                    })))
1747                }
1748                '~' => {
1749                    chars.next(); // consume
1750                    match chars.peek() {
1751                        Some('*') => self.consume_for_binop(chars, "~*", Token::TildeAsterisk),
1752                        Some('=') if self.dialect.supports_geometric_types() => {
1753                            self.consume_for_binop(chars, "~=", Token::TildeEqual)
1754                        }
1755                        Some('~') => {
1756                            chars.next();
1757                            match chars.peek() {
1758                                Some('*') => {
1759                                    self.consume_for_binop(chars, "~~*", Token::DoubleTildeAsterisk)
1760                                }
1761                                _ => self.start_binop(chars, "~~", Token::DoubleTilde),
1762                            }
1763                        }
1764                        _ => self.start_binop(chars, "~", Token::Tilde),
1765                    }
1766                }
1767                '#' => {
1768                    chars.next();
1769                    match chars.peek() {
1770                        Some('-') => self.consume_for_binop(chars, "#-", Token::HashMinus),
1771                        Some('>') => {
1772                            chars.next();
1773                            match chars.peek() {
1774                                Some('>') => {
1775                                    self.consume_for_binop(chars, "#>>", Token::HashLongArrow)
1776                                }
1777                                _ => self.start_binop(chars, "#>", Token::HashArrow),
1778                            }
1779                        }
1780                        Some(' ') => Ok(Some(Token::Sharp)),
1781                        Some('#') if self.dialect.supports_geometric_types() => {
1782                            self.consume_for_binop(chars, "##", Token::DoubleSharp)
1783                        }
1784                        Some(sch) if self.dialect.is_identifier_start('#') => {
1785                            self.tokenize_identifier_or_keyword([ch, *sch], chars)
1786                        }
1787                        _ => self.start_binop(chars, "#", Token::Sharp),
1788                    }
1789                }
1790                '@' => {
1791                    chars.next();
1792                    match chars.peek() {
1793                        Some('@') if self.dialect.supports_geometric_types() => {
1794                            self.consume_and_return(chars, Token::AtAt)
1795                        }
1796                        Some('-') if self.dialect.supports_geometric_types() => {
1797                            chars.next();
1798                            match chars.peek() {
1799                                Some('@') => self.consume_and_return(chars, Token::AtDashAt),
1800                                _ => self.start_binop_opt(chars, "@-", None),
1801                            }
1802                        }
1803                        Some('>') => self.consume_and_return(chars, Token::AtArrow),
1804                        Some('?') => self.consume_and_return(chars, Token::AtQuestion),
1805                        Some('@') => {
1806                            chars.next();
1807                            match chars.peek() {
1808                                Some(' ') => Ok(Some(Token::AtAt)),
1809                                Some(tch) if self.dialect.is_identifier_start('@') => {
1810                                    self.tokenize_identifier_or_keyword([ch, '@', *tch], chars)
1811                                }
1812                                _ => Ok(Some(Token::AtAt)),
1813                            }
1814                        }
1815                        Some(' ') => Ok(Some(Token::AtSign)),
1816                        // We break on quotes here, because no dialect allows identifiers starting
1817                        // with @ and containing quotation marks (e.g. `@'foo'`) unless they are
1818                        // quoted, which is tokenized as a quoted string, not here (e.g.
1819                        // `"@'foo'"`). Further, at least two dialects parse `@` followed by a
1820                        // quoted string as two separate tokens, which this allows. For example,
1821                        // Postgres parses `@'1'` as the absolute value of '1' which is implicitly
1822                        // cast to a numeric type. And when parsing MySQL-style grantees (e.g.
1823                        // `GRANT ALL ON *.* to 'root'@'localhost'`), we also want separate tokens
1824                        // for the user, the `@`, and the host.
1825                        Some('\'') => Ok(Some(Token::AtSign)),
1826                        Some('\"') => Ok(Some(Token::AtSign)),
1827                        Some('`') => Ok(Some(Token::AtSign)),
1828                        Some(sch) if self.dialect.is_identifier_start('@') => {
1829                            self.tokenize_identifier_or_keyword([ch, *sch], chars)
1830                        }
1831                        _ => Ok(Some(Token::AtSign)),
1832                    }
1833                }
1834                // Postgres uses ? for jsonb operators, not prepared statements
1835                '?' if self.dialect.supports_geometric_types() => {
1836                    chars.next(); // consume
1837                    match chars.peek() {
1838                        Some('|') => {
1839                            chars.next();
1840                            match chars.peek() {
1841                                Some('|') => self.consume_and_return(
1842                                    chars,
1843                                    Token::QuestionMarkDoubleVerticalBar,
1844                                ),
1845                                _ => Ok(Some(Token::QuestionPipe)),
1846                            }
1847                        }
1848
1849                        Some('&') => self.consume_and_return(chars, Token::QuestionAnd),
1850                        Some('-') => {
1851                            chars.next(); // consume
1852                            match chars.peek() {
1853                                Some('|') => self
1854                                    .consume_and_return(chars, Token::QuestionMarkDashVerticalBar),
1855                                _ => Ok(Some(Token::QuestionMarkDash)),
1856                            }
1857                        }
1858                        Some('#') => self.consume_and_return(chars, Token::QuestionMarkSharp),
1859                        _ => Ok(Some(Token::Question)),
1860                    }
1861                }
1862                '?' => {
1863                    chars.next();
1864                    let s = peeking_take_while(chars, |ch| ch.is_numeric());
1865                    Ok(Some(Token::Placeholder(format!("?{s}"))))
1866                }
1867
1868                // identifier or keyword
1869                ch if self.dialect.is_identifier_start(ch) => {
1870                    self.tokenize_identifier_or_keyword([ch], chars)
1871                }
1872                '$' => Ok(Some(self.tokenize_dollar_preceded_value(chars)?)),
1873
1874                // whitespace check (including unicode chars) should be last as it covers some of the chars above
1875                ch if ch.is_whitespace() => {
1876                    self.consume_and_return(chars, Token::Whitespace(Whitespace::Space))
1877                }
1878                other => self.consume_and_return(chars, Token::Char(other)),
1879            },
1880            None => Ok(None),
1881        }
1882    }
1883
1884    /// Consume the next character, then parse a custom binary operator. The next character should be included in the prefix
1885    fn consume_for_binop(
1886        &self,
1887        chars: &mut State,
1888        prefix: &str,
1889        default: Token,
1890    ) -> Result<Option<Token>, TokenizerError> {
1891        chars.next(); // consume the first char
1892        self.start_binop_opt(chars, prefix, Some(default))
1893    }
1894
1895    /// parse a custom binary operator
1896    fn start_binop(
1897        &self,
1898        chars: &mut State,
1899        prefix: &str,
1900        default: Token,
1901    ) -> Result<Option<Token>, TokenizerError> {
1902        self.start_binop_opt(chars, prefix, Some(default))
1903    }
1904
1905    /// parse a custom binary operator
1906    fn start_binop_opt(
1907        &self,
1908        chars: &mut State,
1909        prefix: &str,
1910        default: Option<Token>,
1911    ) -> Result<Option<Token>, TokenizerError> {
1912        let mut custom = None;
1913        while let Some(&ch) = chars.peek() {
1914            if !self.dialect.is_custom_operator_part(ch) {
1915                break;
1916            }
1917
1918            custom.get_or_insert_with(|| prefix.to_string()).push(ch);
1919            chars.next();
1920        }
1921        match (custom, default) {
1922            (Some(custom), _) => Ok(Token::CustomBinaryOperator(custom).into()),
1923            (None, Some(tok)) => Ok(Some(tok)),
1924            (None, None) => self.tokenizer_error(
1925                chars.location(),
1926                format!("Expected a valid binary operator after '{prefix}'"),
1927            ),
1928        }
1929    }
1930
1931    /// Tokenize dollar preceded value (i.e: a string/placeholder)
1932    fn tokenize_dollar_preceded_value(&self, chars: &mut State) -> Result<Token, TokenizerError> {
1933        let mut s = String::new();
1934        let mut value = String::new();
1935
1936        chars.next();
1937
1938        // If the dialect does not support dollar-quoted strings, then `$$` is rather a placeholder.
1939        if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1940            chars.next();
1941
1942            let mut is_terminated = false;
1943            let mut prev: Option<char> = None;
1944
1945            while let Some(&ch) = chars.peek() {
1946                if prev == Some('$') {
1947                    if ch == '$' {
1948                        chars.next();
1949                        is_terminated = true;
1950                        break;
1951                    } else {
1952                        s.push('$');
1953                        s.push(ch);
1954                    }
1955                } else if ch != '$' {
1956                    s.push(ch);
1957                }
1958
1959                prev = Some(ch);
1960                chars.next();
1961            }
1962
1963            return if chars.peek().is_none() && !is_terminated {
1964                self.tokenizer_error(chars.location(), "Unterminated dollar-quoted string")
1965            } else {
1966                Ok(Token::DollarQuotedString(DollarQuotedString {
1967                    value: s,
1968                    tag: None,
1969                }))
1970            };
1971        } else {
1972            value.push_str(&peeking_take_while(chars, |ch| {
1973                ch.is_alphanumeric()
1974                    || ch == '_'
1975                    // Allow $ as a placeholder character if the dialect supports it
1976                    || matches!(ch, '$' if self.dialect.supports_dollar_placeholder())
1977            }));
1978
1979            // If the dialect supports a dollar sign as a money prefix (e.g. SQL Server),
1980            // and the value so far is all digits, check for a decimal part, e.g. `$123.45`
1981            if matches!(chars.peek(), Some('.'))
1982                && self.dialect.supports_dollar_as_money_prefix()
1983                && !value.is_empty()
1984                && value.chars().all(|c| c.is_ascii_digit())
1985            {
1986                value.push('.');
1987                chars.next();
1988                value.push_str(&peeking_take_while(chars, |ch| ch.is_ascii_digit()));
1989                return Ok(Token::Placeholder(format!("${value}")));
1990            }
1991
1992            // If the dialect does not support dollar-quoted strings, don't look for the end delimiter.
1993            if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
1994                chars.next();
1995
1996                let mut temp = String::new();
1997                let end_delimiter = format!("${value}$");
1998
1999                loop {
2000                    match chars.next() {
2001                        Some(ch) => {
2002                            temp.push(ch);
2003
2004                            if temp.ends_with(&end_delimiter) {
2005                                if let Some(temp) = temp.strip_suffix(&end_delimiter) {
2006                                    s.push_str(temp);
2007                                }
2008                                break;
2009                            }
2010                        }
2011                        None => {
2012                            if temp.ends_with(&end_delimiter) {
2013                                if let Some(temp) = temp.strip_suffix(&end_delimiter) {
2014                                    s.push_str(temp);
2015                                }
2016                                break;
2017                            }
2018
2019                            return self.tokenizer_error(
2020                                chars.location(),
2021                                "Unterminated dollar-quoted, expected $",
2022                            );
2023                        }
2024                    }
2025                }
2026            } else {
2027                return Ok(Token::Placeholder(format!("${value}")));
2028            }
2029        }
2030
2031        Ok(Token::DollarQuotedString(DollarQuotedString {
2032            value: s,
2033            tag: if value.is_empty() { None } else { Some(value) },
2034        }))
2035    }
2036
2037    fn tokenizer_error<R>(
2038        &self,
2039        loc: Location,
2040        message: impl Into<String>,
2041    ) -> Result<R, TokenizerError> {
2042        Err(TokenizerError {
2043            message: message.into(),
2044            location: loc,
2045        })
2046    }
2047
2048    fn tokenize_number_part(
2049        &self,
2050        chars: &mut State,
2051        is_digit: impl Fn(char) -> bool,
2052    ) -> Result<String, TokenizerError> {
2053        let supports_separator = self.dialect.supports_numeric_literal_underscores();
2054        let mut s = String::new();
2055
2056        while let Some(&ch) = chars.peek() {
2057            if is_digit(ch) {
2058                chars.next();
2059                s.push(ch);
2060            } else if supports_separator && ch == '_' {
2061                let next_char = chars.peekable.clone().nth(1);
2062                if s.is_empty() || !next_char.is_some_and(&is_digit) {
2063                    return self.tokenizer_error(chars.location(), "Unexpected character '_'");
2064                }
2065                chars.next();
2066                s.push(ch);
2067            } else {
2068                break;
2069            }
2070        }
2071
2072        Ok(s)
2073    }
2074
2075    // Consume characters until newline
2076    fn tokenize_single_line_comment(&self, chars: &mut State) -> String {
2077        peeking_take_while(chars, |ch| match ch {
2078            '\n' => false,                                           // Always stop at \n
2079            '\r' if dialect_of!(self is PostgreSqlDialect) => false, // Stop at \r for Postgres
2080            _ => true, // Keep consuming for other characters
2081        })
2082    }
2083
2084    /// Tokenize an identifier or keyword, after the first char is already consumed.
2085    fn tokenize_word(&self, first_chars: impl Into<String>, chars: &mut State) -> String {
2086        let mut s = first_chars.into();
2087        s.push_str(&peeking_take_while(chars, |ch| {
2088            self.dialect.is_identifier_part(ch)
2089        }));
2090        s
2091    }
2092
2093    /// Read a quoted identifier
2094    fn tokenize_quoted_identifier(
2095        &self,
2096        quote_start: char,
2097        chars: &mut State,
2098    ) -> Result<String, TokenizerError> {
2099        let error_loc = chars.location();
2100        chars.next(); // consume the opening quote
2101        let quote_end = Word::matching_end_quote(quote_start);
2102        let (s, last_char) = self.parse_quoted_ident(chars, quote_end);
2103
2104        if last_char == Some(quote_end) {
2105            Ok(s)
2106        } else {
2107            self.tokenizer_error(
2108                error_loc,
2109                format!("Expected close delimiter '{quote_end}' before EOF."),
2110            )
2111        }
2112    }
2113
2114    /// Read a single quoted string, starting with the opening quote.
2115    fn tokenize_escaped_single_quoted_string(
2116        &self,
2117        starting_loc: Location,
2118        chars: &mut State,
2119    ) -> Result<String, TokenizerError> {
2120        if let Some(s) = unescape_single_quoted_string(chars) {
2121            return Ok(s);
2122        }
2123
2124        self.tokenizer_error(starting_loc, "Unterminated encoded string literal")
2125    }
2126
2127    /// Reads a string literal quoted by a single or triple quote characters.
2128    /// Examples: `'abc'`, `'''abc'''`, `"""abc"""`.
2129    fn tokenize_single_or_triple_quoted_string<F>(
2130        &self,
2131        chars: &mut State,
2132        quote_style: char,
2133        backslash_escape: bool,
2134        single_quote_token: F,
2135        triple_quote_token: F,
2136    ) -> Result<Option<Token>, TokenizerError>
2137    where
2138        F: Fn(String) -> Token,
2139    {
2140        let error_loc = chars.location();
2141
2142        let mut num_opening_quotes = 0u8;
2143        for _ in 0..3 {
2144            if Some(&quote_style) == chars.peek() {
2145                chars.next(); // Consume quote.
2146                num_opening_quotes += 1;
2147            } else {
2148                break;
2149            }
2150        }
2151
2152        let (token_fn, num_quote_chars) = match num_opening_quotes {
2153            1 => (single_quote_token, NumStringQuoteChars::One),
2154            2 => {
2155                // If we matched double quotes, then this is an empty string.
2156                return Ok(Some(single_quote_token("".into())));
2157            }
2158            3 => {
2159                let Some(num_quote_chars) = NonZeroU8::new(3) else {
2160                    return self.tokenizer_error(error_loc, "invalid number of opening quotes");
2161                };
2162                (
2163                    triple_quote_token,
2164                    NumStringQuoteChars::Many(num_quote_chars),
2165                )
2166            }
2167            _ => {
2168                return self.tokenizer_error(error_loc, "invalid string literal opening");
2169            }
2170        };
2171
2172        let settings = TokenizeQuotedStringSettings {
2173            quote_style,
2174            num_quote_chars,
2175            num_opening_quotes_to_consume: 0,
2176            backslash_escape,
2177        };
2178
2179        self.tokenize_quoted_string(chars, settings)
2180            .map(token_fn)
2181            .map(Some)
2182    }
2183
2184    /// Reads a string literal quoted by a single quote character.
2185    fn tokenize_single_quoted_string(
2186        &self,
2187        chars: &mut State,
2188        quote_style: char,
2189        backslash_escape: bool,
2190    ) -> Result<String, TokenizerError> {
2191        self.tokenize_quoted_string(
2192            chars,
2193            TokenizeQuotedStringSettings {
2194                quote_style,
2195                num_quote_chars: NumStringQuoteChars::One,
2196                num_opening_quotes_to_consume: 1,
2197                backslash_escape,
2198            },
2199        )
2200    }
2201
2202    /// Reads a quote delimited string expecting `chars.next()` to deliver a quote.
2203    ///
2204    /// See <https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA>
2205    fn tokenize_quote_delimited_string(
2206        &self,
2207        chars: &mut State,
2208        // the prefix that introduced the possible literal or word,
2209        // e.g. "Q" or "nq"
2210        literal_prefix: &[char],
2211    ) -> Result<QuoteDelimitedString, TokenizerError> {
2212        let literal_start_loc = chars.location();
2213        chars.next();
2214
2215        let start_quote_loc = chars.location();
2216        let (start_quote, end_quote) = match chars.next() {
2217            None | Some(' ') | Some('\t') | Some('\r') | Some('\n') => {
2218                return self.tokenizer_error(
2219                    start_quote_loc,
2220                    format!(
2221                        "Invalid space, tab, newline, or EOF after '{}''",
2222                        String::from_iter(literal_prefix)
2223                    ),
2224                );
2225            }
2226            Some(c) => (
2227                c,
2228                match c {
2229                    '[' => ']',
2230                    '{' => '}',
2231                    '<' => '>',
2232                    '(' => ')',
2233                    c => c,
2234                },
2235            ),
2236        };
2237
2238        // read the string literal until the "quote character" following a by literal quote
2239        let mut value = String::new();
2240        while let Some(ch) = chars.next() {
2241            if ch == end_quote {
2242                if let Some('\'') = chars.peek() {
2243                    chars.next(); // ~ consume the quote
2244                    return Ok(QuoteDelimitedString {
2245                        start_quote,
2246                        value,
2247                        end_quote,
2248                    });
2249                }
2250            }
2251            value.push(ch);
2252        }
2253
2254        self.tokenizer_error(literal_start_loc, "Unterminated string literal")
2255    }
2256
2257    /// Read a quoted string.
2258    fn tokenize_quoted_string(
2259        &self,
2260        chars: &mut State,
2261        settings: TokenizeQuotedStringSettings,
2262    ) -> Result<String, TokenizerError> {
2263        let mut s = String::new();
2264        let error_loc = chars.location();
2265
2266        // Consume any opening quotes.
2267        for _ in 0..settings.num_opening_quotes_to_consume {
2268            if Some(settings.quote_style) != chars.next() {
2269                return self.tokenizer_error(error_loc, "invalid string literal opening");
2270            }
2271        }
2272
2273        let mut num_consecutive_quotes = 0;
2274        while let Some(&ch) = chars.peek() {
2275            let pending_final_quote = match settings.num_quote_chars {
2276                NumStringQuoteChars::One => Some(NumStringQuoteChars::One),
2277                n @ NumStringQuoteChars::Many(count)
2278                    if num_consecutive_quotes + 1 == count.get() =>
2279                {
2280                    Some(n)
2281                }
2282                NumStringQuoteChars::Many(_) => None,
2283            };
2284
2285            match ch {
2286                char if char == settings.quote_style && pending_final_quote.is_some() => {
2287                    chars.next(); // consume
2288
2289                    if let Some(NumStringQuoteChars::Many(count)) = pending_final_quote {
2290                        // For an initial string like `"""abc"""`, at this point we have
2291                        // `abc""` in the buffer and have now matched the final `"`.
2292                        // However, the string to return is simply `abc`, so we strip off
2293                        // the trailing quotes before returning.
2294                        let mut buf = s.chars();
2295                        for _ in 1..count.get() {
2296                            buf.next_back();
2297                        }
2298                        return Ok(buf.as_str().to_string());
2299                    } else if chars
2300                        .peek()
2301                        .map(|c| *c == settings.quote_style)
2302                        .unwrap_or(false)
2303                    {
2304                        s.push(ch);
2305                        if !self.unescape {
2306                            // In no-escape mode, the given query has to be saved completely
2307                            s.push(ch);
2308                        }
2309                        chars.next();
2310                    } else {
2311                        return Ok(s);
2312                    }
2313                }
2314                '\\' if settings.backslash_escape => {
2315                    // consume backslash
2316                    chars.next();
2317
2318                    num_consecutive_quotes = 0;
2319
2320                    if let Some(next) = chars.peek() {
2321                        if !self.unescape
2322                            || (self.dialect.ignores_wildcard_escapes()
2323                                && (*next == '%' || *next == '_'))
2324                        {
2325                            // In no-escape mode, the given query has to be saved completely
2326                            // including backslashes. Similarly, with ignore_like_wildcard_escapes,
2327                            // the backslash is not stripped.
2328                            s.push(ch);
2329                            s.push(*next);
2330                            chars.next(); // consume next
2331                        } else {
2332                            let n = match next {
2333                                '0' => '\0',
2334                                'a' => '\u{7}',
2335                                'b' => '\u{8}',
2336                                'f' => '\u{c}',
2337                                'n' => '\n',
2338                                'r' => '\r',
2339                                't' => '\t',
2340                                'Z' => '\u{1a}',
2341                                _ => *next,
2342                            };
2343                            s.push(n);
2344                            chars.next(); // consume next
2345                        }
2346                    }
2347                }
2348                ch => {
2349                    chars.next(); // consume ch
2350
2351                    if ch == settings.quote_style {
2352                        num_consecutive_quotes += 1;
2353                    } else {
2354                        num_consecutive_quotes = 0;
2355                    }
2356
2357                    s.push(ch);
2358                }
2359            }
2360        }
2361        self.tokenizer_error(error_loc, "Unterminated string literal")
2362    }
2363
2364    fn tokenize_multiline_comment(
2365        &self,
2366        chars: &mut State,
2367    ) -> Result<Option<Token>, TokenizerError> {
2368        let mut s = String::new();
2369        let mut nested = 1;
2370        let supports_nested_comments = self.dialect.supports_nested_comments();
2371        loop {
2372            match chars.next() {
2373                Some('/') if matches!(chars.peek(), Some('*')) && supports_nested_comments => {
2374                    chars.next(); // consume the '*'
2375                    s.push('/');
2376                    s.push('*');
2377                    nested += 1;
2378                }
2379                Some('*') if matches!(chars.peek(), Some('/')) => {
2380                    chars.next(); // consume the '/'
2381                    nested -= 1;
2382                    if nested == 0 {
2383                        break Ok(Some(Token::Whitespace(Whitespace::MultiLineComment(s))));
2384                    }
2385                    s.push('*');
2386                    s.push('/');
2387                }
2388                Some(ch) => {
2389                    s.push(ch);
2390                }
2391                None => {
2392                    break self.tokenizer_error(
2393                        chars.location(),
2394                        "Unexpected EOF while in a multi-line comment",
2395                    );
2396                }
2397            }
2398        }
2399    }
2400
2401    fn parse_quoted_ident(&self, chars: &mut State, quote_end: char) -> (String, Option<char>) {
2402        let mut last_char = None;
2403        let mut s = String::new();
2404        while let Some(ch) = chars.next() {
2405            if ch == quote_end {
2406                if chars.peek() == Some(&quote_end) {
2407                    chars.next();
2408                    s.push(ch);
2409                    if !self.unescape {
2410                        // In no-escape mode, the given query has to be saved completely
2411                        s.push(ch);
2412                    }
2413                } else {
2414                    last_char = Some(quote_end);
2415                    break;
2416                }
2417            } else {
2418                s.push(ch);
2419            }
2420        }
2421        (s, last_char)
2422    }
2423
2424    #[allow(clippy::unnecessary_wraps)]
2425    fn consume_and_return(
2426        &self,
2427        chars: &mut State,
2428        t: Token,
2429    ) -> Result<Option<Token>, TokenizerError> {
2430        chars.next();
2431        Ok(Some(t))
2432    }
2433}
2434
2435/// Read from `chars` until `predicate` returns `false` or EOF is hit.
2436/// Return the characters read as String, and keep the first non-matching
2437/// char available as `chars.next()`.
2438fn peeking_take_while(chars: &mut State, mut predicate: impl FnMut(char) -> bool) -> String {
2439    let mut s = String::new();
2440    while let Some(&ch) = chars.peek() {
2441        if predicate(ch) {
2442            chars.next(); // consume
2443            s.push(ch);
2444        } else {
2445            break;
2446        }
2447    }
2448    s
2449}
2450
2451fn unescape_single_quoted_string(chars: &mut State<'_>) -> Option<String> {
2452    Unescape::new(chars).unescape()
2453}
2454
2455struct Unescape<'a: 'b, 'b> {
2456    chars: &'b mut State<'a>,
2457}
2458
2459impl<'a: 'b, 'b> Unescape<'a, 'b> {
2460    fn new(chars: &'b mut State<'a>) -> Self {
2461        Self { chars }
2462    }
2463    fn unescape(mut self) -> Option<String> {
2464        let mut unescaped = String::new();
2465
2466        self.chars.next();
2467
2468        while let Some(c) = self.chars.next() {
2469            if c == '\'' {
2470                // case: ''''
2471                if self.chars.peek().map(|c| *c == '\'').unwrap_or(false) {
2472                    self.chars.next();
2473                    unescaped.push('\'');
2474                    continue;
2475                }
2476                return Some(unescaped);
2477            }
2478
2479            if c != '\\' {
2480                unescaped.push(c);
2481                continue;
2482            }
2483
2484            let c = match self.chars.next()? {
2485                'b' => '\u{0008}',
2486                'f' => '\u{000C}',
2487                'n' => '\n',
2488                'r' => '\r',
2489                't' => '\t',
2490                'u' => self.unescape_unicode_16()?,
2491                'U' => self.unescape_unicode_32()?,
2492                'x' => self.unescape_hex()?,
2493                c if c.is_digit(8) => self.unescape_octal(c)?,
2494                c => c,
2495            };
2496
2497            unescaped.push(Self::check_null(c)?);
2498        }
2499
2500        None
2501    }
2502
2503    #[inline]
2504    fn check_null(c: char) -> Option<char> {
2505        if c == '\0' {
2506            None
2507        } else {
2508            Some(c)
2509        }
2510    }
2511
2512    #[inline]
2513    fn byte_to_char<const RADIX: u32>(s: &str) -> Option<char> {
2514        // u32 is used here because Pg has an overflow operation rather than throwing an exception directly.
2515        match u32::from_str_radix(s, RADIX) {
2516            Err(_) => None,
2517            Ok(n) => {
2518                let n = n & 0xFF;
2519                if n <= 127 {
2520                    char::from_u32(n)
2521                } else {
2522                    None
2523                }
2524            }
2525        }
2526    }
2527
2528    // Hexadecimal byte value. \xh, \xhh (h = 0–9, A–F)
2529    fn unescape_hex(&mut self) -> Option<char> {
2530        let mut s = String::new();
2531
2532        for _ in 0..2 {
2533            match self.next_hex_digit() {
2534                Some(c) => s.push(c),
2535                None => break,
2536            }
2537        }
2538
2539        if s.is_empty() {
2540            return Some('x');
2541        }
2542
2543        Self::byte_to_char::<16>(&s)
2544    }
2545
2546    #[inline]
2547    fn next_hex_digit(&mut self) -> Option<char> {
2548        match self.chars.peek() {
2549            Some(c) if c.is_ascii_hexdigit() => self.chars.next(),
2550            _ => None,
2551        }
2552    }
2553
2554    // Octal byte value. \o, \oo, \ooo (o = 0–7)
2555    fn unescape_octal(&mut self, c: char) -> Option<char> {
2556        let mut s = String::new();
2557
2558        s.push(c);
2559        for _ in 0..2 {
2560            match self.next_octal_digest() {
2561                Some(c) => s.push(c),
2562                None => break,
2563            }
2564        }
2565
2566        Self::byte_to_char::<8>(&s)
2567    }
2568
2569    #[inline]
2570    fn next_octal_digest(&mut self) -> Option<char> {
2571        match self.chars.peek() {
2572            Some(c) if c.is_digit(8) => self.chars.next(),
2573            _ => None,
2574        }
2575    }
2576
2577    // 16-bit hexadecimal Unicode character value. \uxxxx (x = 0–9, A–F)
2578    fn unescape_unicode_16(&mut self) -> Option<char> {
2579        self.unescape_unicode::<4>()
2580    }
2581
2582    // 32-bit hexadecimal Unicode character value. \Uxxxxxxxx (x = 0–9, A–F)
2583    fn unescape_unicode_32(&mut self) -> Option<char> {
2584        self.unescape_unicode::<8>()
2585    }
2586
2587    fn unescape_unicode<const NUM: usize>(&mut self) -> Option<char> {
2588        let mut s = String::new();
2589        for _ in 0..NUM {
2590            s.push(self.chars.next()?);
2591        }
2592        match u32::from_str_radix(&s, 16) {
2593            Err(_) => None,
2594            Ok(n) => char::from_u32(n),
2595        }
2596    }
2597}
2598
2599fn unescape_unicode_single_quoted_string(chars: &mut State<'_>) -> Result<String, TokenizerError> {
2600    let mut unescaped = String::new();
2601    chars.next(); // consume the opening quote
2602    while let Some(c) = chars.next() {
2603        match c {
2604            '\'' => {
2605                if chars.peek() == Some(&'\'') {
2606                    chars.next();
2607                    unescaped.push('\'');
2608                } else {
2609                    return Ok(unescaped);
2610                }
2611            }
2612            '\\' => match chars.peek() {
2613                Some('\\') => {
2614                    chars.next();
2615                    unescaped.push('\\');
2616                }
2617                Some('+') => {
2618                    chars.next();
2619                    unescaped.push(take_char_from_hex_digits(chars, 6)?);
2620                }
2621                _ => unescaped.push(take_char_from_hex_digits(chars, 4)?),
2622            },
2623            _ => {
2624                unescaped.push(c);
2625            }
2626        }
2627    }
2628    Err(TokenizerError {
2629        message: "Unterminated unicode encoded string literal".to_string(),
2630        location: chars.location(),
2631    })
2632}
2633
2634fn take_char_from_hex_digits(
2635    chars: &mut State<'_>,
2636    max_digits: usize,
2637) -> Result<char, TokenizerError> {
2638    let mut result = 0u32;
2639    for _ in 0..max_digits {
2640        let next_char = chars.next().ok_or_else(|| TokenizerError {
2641            message: "Unexpected EOF while parsing hex digit in escaped unicode string."
2642                .to_string(),
2643            location: chars.location(),
2644        })?;
2645        let digit = next_char.to_digit(16).ok_or_else(|| TokenizerError {
2646            message: format!("Invalid hex digit in escaped unicode string: {next_char}"),
2647            location: chars.location(),
2648        })?;
2649        result = result * 16 + digit;
2650    }
2651    char::from_u32(result).ok_or_else(|| TokenizerError {
2652        message: format!("Invalid unicode character: {result:x}"),
2653        location: chars.location(),
2654    })
2655}
2656
2657#[cfg(test)]
2658mod tests {
2659    use super::*;
2660    use crate::dialect::{
2661        BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, MySqlDialect,
2662        PostgreSqlDialect, SQLiteDialect,
2663    };
2664    use crate::test_utils::{all_dialects, all_dialects_except, all_dialects_where};
2665    use core::fmt::Debug;
2666
2667    #[test]
2668    fn tokenizer_error_impl() {
2669        let err = TokenizerError {
2670            message: "test".into(),
2671            location: Location { line: 1, column: 1 },
2672        };
2673        {
2674            use core::error::Error;
2675            assert!(err.source().is_none());
2676        }
2677        assert_eq!(err.to_string(), "test at Line: 1, Column: 1");
2678    }
2679
2680    #[test]
2681    fn tokenize_select_1() {
2682        let sql = String::from("SELECT 1");
2683        let dialect = GenericDialect {};
2684        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2685
2686        let expected = vec![
2687            Token::make_keyword("SELECT"),
2688            Token::Whitespace(Whitespace::Space),
2689            Token::Number(String::from("1"), false),
2690        ];
2691
2692        compare(expected, tokens);
2693    }
2694
2695    #[test]
2696    fn tokenize_select_float() {
2697        let sql = String::from("SELECT .1");
2698        let dialect = GenericDialect {};
2699        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2700
2701        let expected = vec![
2702            Token::make_keyword("SELECT"),
2703            Token::Whitespace(Whitespace::Space),
2704            Token::Number(String::from(".1"), false),
2705        ];
2706
2707        compare(expected, tokens);
2708    }
2709
2710    #[test]
2711    fn tokenize_with_mapper() {
2712        let sql = String::from("SELECT ?");
2713        let dialect = GenericDialect {};
2714        let mut param_num = 1;
2715
2716        let mut tokens = vec![];
2717        Tokenizer::new(&dialect, &sql)
2718            .tokenize_with_location_into_buf_with_mapper(&mut tokens, |mut token_span| {
2719                token_span.token = match token_span.token {
2720                    Token::Placeholder(n) => Token::Placeholder(if n == "?" {
2721                        let ret = format!("${}", param_num);
2722                        param_num += 1;
2723                        ret
2724                    } else {
2725                        n
2726                    }),
2727                    token => token,
2728                };
2729                token_span
2730            })
2731            .unwrap();
2732        let actual = tokens.into_iter().map(|t| t.token).collect();
2733        let expected = vec![
2734            Token::make_keyword("SELECT"),
2735            Token::Whitespace(Whitespace::Space),
2736            Token::Placeholder("$1".to_string()),
2737        ];
2738
2739        compare(expected, actual);
2740    }
2741
2742    #[test]
2743    fn tokenize_clickhouse_double_equal() {
2744        let sql = String::from("SELECT foo=='1'");
2745        let dialect = ClickHouseDialect {};
2746        let mut tokenizer = Tokenizer::new(&dialect, &sql);
2747        let tokens = tokenizer.tokenize().unwrap();
2748
2749        let expected = vec![
2750            Token::make_keyword("SELECT"),
2751            Token::Whitespace(Whitespace::Space),
2752            Token::Word(Word {
2753                value: "foo".to_string(),
2754                quote_style: None,
2755                keyword: Keyword::NoKeyword,
2756            }),
2757            Token::DoubleEq,
2758            Token::SingleQuotedString("1".to_string()),
2759        ];
2760
2761        compare(expected, tokens);
2762    }
2763
2764    #[test]
2765    fn tokenize_numeric_literal_underscore() {
2766        let dialect = GenericDialect {};
2767        let sql = String::from("SELECT 10_000");
2768        let mut tokenizer = Tokenizer::new(&dialect, &sql);
2769        let tokens = tokenizer.tokenize().unwrap();
2770        let expected = vec![
2771            Token::make_keyword("SELECT"),
2772            Token::Whitespace(Whitespace::Space),
2773            Token::Number("10".to_string(), false),
2774            Token::make_word("_000", None),
2775        ];
2776        compare(expected, tokens);
2777
2778        let numeric_underscore_dialects =
2779            all_dialects_where(|dialect| dialect.supports_numeric_literal_underscores());
2780
2781        numeric_underscore_dialects.tokenizes_to(
2782            "SELECT 10_000, _10_000, 1_000.123, 1_000.123_456, 1e1_0",
2783            vec![
2784                Token::make_keyword("SELECT"),
2785                Token::Whitespace(Whitespace::Space),
2786                Token::Number("10_000".to_string(), false),
2787                Token::Comma,
2788                Token::Whitespace(Whitespace::Space),
2789                Token::make_word("_10_000", None), // leading underscore tokenizes as a word (parsed as column identifier)
2790                Token::Comma,
2791                Token::Whitespace(Whitespace::Space),
2792                Token::Number("1_000.123".to_string(), false), // with decimal digits
2793                Token::Comma,
2794                Token::Whitespace(Whitespace::Space),
2795                Token::Number("1_000.123_456".to_string(), false), // with an underscore in the decimal digits
2796                Token::Comma,
2797                Token::Whitespace(Whitespace::Space),
2798                Token::Number("1e1_0".to_string(), false), // with an underscore in the exponent
2799            ],
2800        );
2801
2802        numeric_underscore_dialects.tokenizes_to(
2803            "0xFF_FF",
2804            vec![Token::HexStringLiteral("FF_FF".to_string())],
2805        );
2806
2807        for dialect in &numeric_underscore_dialects.dialects {
2808            for sql in [
2809                "SELECT 10_00_",
2810                "SELECT 10___0",
2811                "SELECT 1_000.123_",
2812                "SELECT 1._000",
2813                "SELECT 1_a",
2814                "SELECT 1e_1",
2815                "SELECT 1e1_",
2816                "SELECT 1e1__0",
2817                "SELECT 0x_1",
2818                "SELECT 0x1_",
2819            ] {
2820                let err = Tokenizer::new(&**dialect, sql).tokenize().unwrap_err();
2821                assert_eq!("Unexpected character '_'", err.message);
2822            }
2823        }
2824    }
2825
2826    #[test]
2827    fn tokenize_select_exponent() {
2828        let sql = String::from("SELECT 1e10, 1e-10, 1e+10, 1ea, 1e-10a, 1e-10-10");
2829        let dialect = GenericDialect {};
2830        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2831
2832        let expected = vec![
2833            Token::make_keyword("SELECT"),
2834            Token::Whitespace(Whitespace::Space),
2835            Token::Number(String::from("1e10"), false),
2836            Token::Comma,
2837            Token::Whitespace(Whitespace::Space),
2838            Token::Number(String::from("1e-10"), false),
2839            Token::Comma,
2840            Token::Whitespace(Whitespace::Space),
2841            Token::Number(String::from("1e+10"), false),
2842            Token::Comma,
2843            Token::Whitespace(Whitespace::Space),
2844            Token::Number(String::from("1"), false),
2845            Token::make_word("ea", None),
2846            Token::Comma,
2847            Token::Whitespace(Whitespace::Space),
2848            Token::Number(String::from("1e-10"), false),
2849            Token::make_word("a", None),
2850            Token::Comma,
2851            Token::Whitespace(Whitespace::Space),
2852            Token::Number(String::from("1e-10"), false),
2853            Token::Minus,
2854            Token::Number(String::from("10"), false),
2855        ];
2856
2857        compare(expected, tokens);
2858    }
2859
2860    #[test]
2861    fn tokenize_scalar_function() {
2862        let sql = String::from("SELECT sqrt(1)");
2863        let dialect = GenericDialect {};
2864        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2865
2866        let expected = vec![
2867            Token::make_keyword("SELECT"),
2868            Token::Whitespace(Whitespace::Space),
2869            Token::make_word("sqrt", None),
2870            Token::LParen,
2871            Token::Number(String::from("1"), false),
2872            Token::RParen,
2873        ];
2874
2875        compare(expected, tokens);
2876    }
2877
2878    #[test]
2879    fn tokenize_string_string_concat() {
2880        let sql = String::from("SELECT 'a' || 'b'");
2881        let dialect = GenericDialect {};
2882        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2883
2884        let expected = vec![
2885            Token::make_keyword("SELECT"),
2886            Token::Whitespace(Whitespace::Space),
2887            Token::SingleQuotedString(String::from("a")),
2888            Token::Whitespace(Whitespace::Space),
2889            Token::StringConcat,
2890            Token::Whitespace(Whitespace::Space),
2891            Token::SingleQuotedString(String::from("b")),
2892        ];
2893
2894        compare(expected, tokens);
2895    }
2896    #[test]
2897    fn tokenize_bitwise_op() {
2898        let sql = String::from("SELECT one | two ^ three");
2899        let dialect = GenericDialect {};
2900        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2901
2902        let expected = vec![
2903            Token::make_keyword("SELECT"),
2904            Token::Whitespace(Whitespace::Space),
2905            Token::make_word("one", None),
2906            Token::Whitespace(Whitespace::Space),
2907            Token::Pipe,
2908            Token::Whitespace(Whitespace::Space),
2909            Token::make_word("two", None),
2910            Token::Whitespace(Whitespace::Space),
2911            Token::Caret,
2912            Token::Whitespace(Whitespace::Space),
2913            Token::make_word("three", None),
2914        ];
2915        compare(expected, tokens);
2916    }
2917
2918    #[test]
2919    fn tokenize_logical_xor() {
2920        let sql =
2921            String::from("SELECT true XOR true, false XOR false, true XOR false, false XOR true");
2922        let dialect = GenericDialect {};
2923        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2924
2925        let expected = vec![
2926            Token::make_keyword("SELECT"),
2927            Token::Whitespace(Whitespace::Space),
2928            Token::make_keyword("true"),
2929            Token::Whitespace(Whitespace::Space),
2930            Token::make_keyword("XOR"),
2931            Token::Whitespace(Whitespace::Space),
2932            Token::make_keyword("true"),
2933            Token::Comma,
2934            Token::Whitespace(Whitespace::Space),
2935            Token::make_keyword("false"),
2936            Token::Whitespace(Whitespace::Space),
2937            Token::make_keyword("XOR"),
2938            Token::Whitespace(Whitespace::Space),
2939            Token::make_keyword("false"),
2940            Token::Comma,
2941            Token::Whitespace(Whitespace::Space),
2942            Token::make_keyword("true"),
2943            Token::Whitespace(Whitespace::Space),
2944            Token::make_keyword("XOR"),
2945            Token::Whitespace(Whitespace::Space),
2946            Token::make_keyword("false"),
2947            Token::Comma,
2948            Token::Whitespace(Whitespace::Space),
2949            Token::make_keyword("false"),
2950            Token::Whitespace(Whitespace::Space),
2951            Token::make_keyword("XOR"),
2952            Token::Whitespace(Whitespace::Space),
2953            Token::make_keyword("true"),
2954        ];
2955        compare(expected, tokens);
2956    }
2957
2958    #[test]
2959    fn tokenize_simple_select() {
2960        let sql = String::from("SELECT * FROM customer WHERE id = 1 LIMIT 5");
2961        let dialect = GenericDialect {};
2962        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2963
2964        let expected = vec![
2965            Token::make_keyword("SELECT"),
2966            Token::Whitespace(Whitespace::Space),
2967            Token::Mul,
2968            Token::Whitespace(Whitespace::Space),
2969            Token::make_keyword("FROM"),
2970            Token::Whitespace(Whitespace::Space),
2971            Token::make_word("customer", None),
2972            Token::Whitespace(Whitespace::Space),
2973            Token::make_keyword("WHERE"),
2974            Token::Whitespace(Whitespace::Space),
2975            Token::make_word("id", None),
2976            Token::Whitespace(Whitespace::Space),
2977            Token::Eq,
2978            Token::Whitespace(Whitespace::Space),
2979            Token::Number(String::from("1"), false),
2980            Token::Whitespace(Whitespace::Space),
2981            Token::make_keyword("LIMIT"),
2982            Token::Whitespace(Whitespace::Space),
2983            Token::Number(String::from("5"), false),
2984        ];
2985
2986        compare(expected, tokens);
2987    }
2988
2989    #[test]
2990    fn tokenize_explain_select() {
2991        let sql = String::from("EXPLAIN SELECT * FROM customer WHERE id = 1");
2992        let dialect = GenericDialect {};
2993        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2994
2995        let expected = vec![
2996            Token::make_keyword("EXPLAIN"),
2997            Token::Whitespace(Whitespace::Space),
2998            Token::make_keyword("SELECT"),
2999            Token::Whitespace(Whitespace::Space),
3000            Token::Mul,
3001            Token::Whitespace(Whitespace::Space),
3002            Token::make_keyword("FROM"),
3003            Token::Whitespace(Whitespace::Space),
3004            Token::make_word("customer", None),
3005            Token::Whitespace(Whitespace::Space),
3006            Token::make_keyword("WHERE"),
3007            Token::Whitespace(Whitespace::Space),
3008            Token::make_word("id", None),
3009            Token::Whitespace(Whitespace::Space),
3010            Token::Eq,
3011            Token::Whitespace(Whitespace::Space),
3012            Token::Number(String::from("1"), false),
3013        ];
3014
3015        compare(expected, tokens);
3016    }
3017
3018    #[test]
3019    fn tokenize_explain_analyze_select() {
3020        let sql = String::from("EXPLAIN ANALYZE SELECT * FROM customer WHERE id = 1");
3021        let dialect = GenericDialect {};
3022        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3023
3024        let expected = vec![
3025            Token::make_keyword("EXPLAIN"),
3026            Token::Whitespace(Whitespace::Space),
3027            Token::make_keyword("ANALYZE"),
3028            Token::Whitespace(Whitespace::Space),
3029            Token::make_keyword("SELECT"),
3030            Token::Whitespace(Whitespace::Space),
3031            Token::Mul,
3032            Token::Whitespace(Whitespace::Space),
3033            Token::make_keyword("FROM"),
3034            Token::Whitespace(Whitespace::Space),
3035            Token::make_word("customer", None),
3036            Token::Whitespace(Whitespace::Space),
3037            Token::make_keyword("WHERE"),
3038            Token::Whitespace(Whitespace::Space),
3039            Token::make_word("id", None),
3040            Token::Whitespace(Whitespace::Space),
3041            Token::Eq,
3042            Token::Whitespace(Whitespace::Space),
3043            Token::Number(String::from("1"), false),
3044        ];
3045
3046        compare(expected, tokens);
3047    }
3048
3049    #[test]
3050    fn tokenize_string_predicate() {
3051        let sql = String::from("SELECT * FROM customer WHERE salary != 'Not Provided'");
3052        let dialect = GenericDialect {};
3053        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3054
3055        let expected = vec![
3056            Token::make_keyword("SELECT"),
3057            Token::Whitespace(Whitespace::Space),
3058            Token::Mul,
3059            Token::Whitespace(Whitespace::Space),
3060            Token::make_keyword("FROM"),
3061            Token::Whitespace(Whitespace::Space),
3062            Token::make_word("customer", None),
3063            Token::Whitespace(Whitespace::Space),
3064            Token::make_keyword("WHERE"),
3065            Token::Whitespace(Whitespace::Space),
3066            Token::make_word("salary", None),
3067            Token::Whitespace(Whitespace::Space),
3068            Token::Neq,
3069            Token::Whitespace(Whitespace::Space),
3070            Token::SingleQuotedString(String::from("Not Provided")),
3071        ];
3072
3073        compare(expected, tokens);
3074    }
3075
3076    #[test]
3077    fn tokenize_invalid_string() {
3078        let sql = String::from("\n💝مصطفىh");
3079
3080        let dialect = GenericDialect {};
3081        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3082        // println!("tokens: {:#?}", tokens);
3083        let expected = vec![
3084            Token::Whitespace(Whitespace::Newline),
3085            Token::Char('💝'),
3086            Token::make_word("مصطفىh", None),
3087        ];
3088        compare(expected, tokens);
3089    }
3090
3091    #[test]
3092    fn tokenize_newline_in_string_literal() {
3093        let sql = String::from("'foo\r\nbar\nbaz'");
3094
3095        let dialect = GenericDialect {};
3096        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3097        let expected = vec![Token::SingleQuotedString("foo\r\nbar\nbaz".to_string())];
3098        compare(expected, tokens);
3099    }
3100
3101    #[test]
3102    fn tokenize_unterminated_string_literal() {
3103        let sql = String::from("select 'foo");
3104
3105        let dialect = GenericDialect {};
3106        let mut tokenizer = Tokenizer::new(&dialect, &sql);
3107        assert_eq!(
3108            tokenizer.tokenize(),
3109            Err(TokenizerError {
3110                message: "Unterminated string literal".to_string(),
3111                location: Location { line: 1, column: 8 },
3112            })
3113        );
3114    }
3115
3116    #[test]
3117    fn tokenize_unterminated_string_literal_utf8() {
3118        let sql = String::from("SELECT \"なにか\" FROM Y WHERE \"なにか\" = 'test;");
3119
3120        let dialect = GenericDialect {};
3121        let mut tokenizer = Tokenizer::new(&dialect, &sql);
3122        assert_eq!(
3123            tokenizer.tokenize(),
3124            Err(TokenizerError {
3125                message: "Unterminated string literal".to_string(),
3126                location: Location {
3127                    line: 1,
3128                    column: 35
3129                }
3130            })
3131        );
3132    }
3133
3134    #[test]
3135    fn tokenize_invalid_string_cols() {
3136        let sql = String::from("\n\nSELECT * FROM table\t💝مصطفىh");
3137
3138        let dialect = GenericDialect {};
3139        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3140        // println!("tokens: {:#?}", tokens);
3141        let expected = vec![
3142            Token::Whitespace(Whitespace::Newline),
3143            Token::Whitespace(Whitespace::Newline),
3144            Token::make_keyword("SELECT"),
3145            Token::Whitespace(Whitespace::Space),
3146            Token::Mul,
3147            Token::Whitespace(Whitespace::Space),
3148            Token::make_keyword("FROM"),
3149            Token::Whitespace(Whitespace::Space),
3150            Token::make_keyword("table"),
3151            Token::Whitespace(Whitespace::Tab),
3152            Token::Char('💝'),
3153            Token::make_word("مصطفىh", None),
3154        ];
3155        compare(expected, tokens);
3156    }
3157
3158    #[test]
3159    fn tokenize_dollar_quoted_string_tagged() {
3160        let test_cases = vec![
3161            (
3162                String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$tag$"),
3163                vec![
3164                    Token::make_keyword("SELECT"),
3165                    Token::Whitespace(Whitespace::Space),
3166                    Token::DollarQuotedString(DollarQuotedString {
3167                        value: "dollar '$' quoted strings have $tags like this$ or like this $$".into(),
3168                        tag: Some("tag".into()),
3169                    })
3170                ]
3171            ),
3172            (
3173                String::from("SELECT $abc$x$ab$abc$"),
3174                vec![
3175                    Token::make_keyword("SELECT"),
3176                    Token::Whitespace(Whitespace::Space),
3177                    Token::DollarQuotedString(DollarQuotedString {
3178                        value: "x$ab".into(),
3179                        tag: Some("abc".into()),
3180                    })
3181                ]
3182            ),
3183            (
3184                String::from("SELECT $abc$$abc$"),
3185                vec![
3186                    Token::make_keyword("SELECT"),
3187                    Token::Whitespace(Whitespace::Space),
3188                    Token::DollarQuotedString(DollarQuotedString {
3189                        value: "".into(),
3190                        tag: Some("abc".into()),
3191                    })
3192                ]
3193            ),
3194            (
3195                String::from("0$abc$$abc$1"),
3196                vec![
3197                    Token::Number("0".into(), false),
3198                    Token::DollarQuotedString(DollarQuotedString {
3199                        value: "".into(),
3200                        tag: Some("abc".into()),
3201                    }),
3202                    Token::Number("1".into(), false),
3203                ]
3204            ),
3205            (
3206                String::from("$function$abc$q$data$q$$function$"),
3207                vec![
3208                    Token::DollarQuotedString(DollarQuotedString {
3209                        value: "abc$q$data$q$".into(),
3210                        tag: Some("function".into()),
3211                    }),
3212                ]
3213            ),
3214        ];
3215
3216        let dialect = GenericDialect {};
3217        for (sql, expected) in test_cases {
3218            let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3219            compare(expected, tokens);
3220        }
3221    }
3222
3223    #[test]
3224    fn tokenize_dollar_quoted_string_tagged_unterminated() {
3225        let sql = String::from("SELECT $tag$dollar '$' quoted strings have $tags like this$ or like this $$$different tag$");
3226        let dialect = GenericDialect {};
3227        assert_eq!(
3228            Tokenizer::new(&dialect, &sql).tokenize(),
3229            Err(TokenizerError {
3230                message: "Unterminated dollar-quoted, expected $".into(),
3231                location: Location {
3232                    line: 1,
3233                    column: 91
3234                }
3235            })
3236        );
3237    }
3238
3239    #[test]
3240    fn tokenize_dollar_quoted_string_tagged_unterminated_mirror() {
3241        let sql = String::from("SELECT $abc$abc$");
3242        let dialect = GenericDialect {};
3243        assert_eq!(
3244            Tokenizer::new(&dialect, &sql).tokenize(),
3245            Err(TokenizerError {
3246                message: "Unterminated dollar-quoted, expected $".into(),
3247                location: Location {
3248                    line: 1,
3249                    column: 17
3250                }
3251            })
3252        );
3253    }
3254
3255    #[test]
3256    fn tokenize_dollar_placeholder() {
3257        let sql = String::from("SELECT $$, $$ABC$$, $ABC$, $ABC");
3258        let dialect = SQLiteDialect {};
3259        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3260        assert_eq!(
3261            tokens,
3262            vec![
3263                Token::make_keyword("SELECT"),
3264                Token::Whitespace(Whitespace::Space),
3265                Token::Placeholder("$$".into()),
3266                Token::Comma,
3267                Token::Whitespace(Whitespace::Space),
3268                Token::Placeholder("$$ABC$$".into()),
3269                Token::Comma,
3270                Token::Whitespace(Whitespace::Space),
3271                Token::Placeholder("$ABC$".into()),
3272                Token::Comma,
3273                Token::Whitespace(Whitespace::Space),
3274                Token::Placeholder("$ABC".into()),
3275            ]
3276        );
3277    }
3278
3279    #[test]
3280    fn tokenize_nested_dollar_quoted_strings() {
3281        let sql = String::from("SELECT $tag$dollar $nested$ string$tag$");
3282        let dialect = GenericDialect {};
3283        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3284        let expected = vec![
3285            Token::make_keyword("SELECT"),
3286            Token::Whitespace(Whitespace::Space),
3287            Token::DollarQuotedString(DollarQuotedString {
3288                value: "dollar $nested$ string".into(),
3289                tag: Some("tag".into()),
3290            }),
3291        ];
3292        compare(expected, tokens);
3293    }
3294
3295    #[test]
3296    fn tokenize_dollar_quoted_string_untagged_empty() {
3297        let sql = String::from("SELECT $$$$");
3298        let dialect = GenericDialect {};
3299        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3300        let expected = vec![
3301            Token::make_keyword("SELECT"),
3302            Token::Whitespace(Whitespace::Space),
3303            Token::DollarQuotedString(DollarQuotedString {
3304                value: "".into(),
3305                tag: None,
3306            }),
3307        ];
3308        compare(expected, tokens);
3309    }
3310
3311    #[test]
3312    fn tokenize_dollar_quoted_string_untagged() {
3313        let sql =
3314            String::from("SELECT $$within dollar '$' quoted strings have $tags like this$ $$");
3315        let dialect = GenericDialect {};
3316        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3317        let expected = vec![
3318            Token::make_keyword("SELECT"),
3319            Token::Whitespace(Whitespace::Space),
3320            Token::DollarQuotedString(DollarQuotedString {
3321                value: "within dollar '$' quoted strings have $tags like this$ ".into(),
3322                tag: None,
3323            }),
3324        ];
3325        compare(expected, tokens);
3326    }
3327
3328    #[test]
3329    fn tokenize_dollar_quoted_string_untagged_unterminated() {
3330        let sql = String::from(
3331            "SELECT $$dollar '$' quoted strings have $tags like this$ or like this $different tag$",
3332        );
3333        let dialect = GenericDialect {};
3334        assert_eq!(
3335            Tokenizer::new(&dialect, &sql).tokenize(),
3336            Err(TokenizerError {
3337                message: "Unterminated dollar-quoted string".into(),
3338                location: Location {
3339                    line: 1,
3340                    column: 86
3341                }
3342            })
3343        );
3344    }
3345
3346    #[test]
3347    fn tokenize_right_arrow() {
3348        let sql = String::from("FUNCTION(key=>value)");
3349        let dialect = GenericDialect {};
3350        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3351        let expected = vec![
3352            Token::make_word("FUNCTION", None),
3353            Token::LParen,
3354            Token::make_word("key", None),
3355            Token::RArrow,
3356            Token::make_word("value", None),
3357            Token::RParen,
3358        ];
3359        compare(expected, tokens);
3360    }
3361
3362    #[test]
3363    fn tokenize_is_null() {
3364        let sql = String::from("a IS NULL");
3365        let dialect = GenericDialect {};
3366        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3367
3368        let expected = vec![
3369            Token::make_word("a", None),
3370            Token::Whitespace(Whitespace::Space),
3371            Token::make_keyword("IS"),
3372            Token::Whitespace(Whitespace::Space),
3373            Token::make_keyword("NULL"),
3374        ];
3375
3376        compare(expected, tokens);
3377    }
3378
3379    #[test]
3380    fn tokenize_comment() {
3381        let test_cases = vec![
3382            (
3383                String::from("0--this is a comment\n1"),
3384                vec![
3385                    Token::Number("0".to_string(), false),
3386                    Token::Whitespace(Whitespace::SingleLineComment {
3387                        prefix: "--".to_string(),
3388                        comment: "this is a comment".to_string(),
3389                    }),
3390                    Token::Whitespace(Whitespace::Newline),
3391                    Token::Number("1".to_string(), false),
3392                ],
3393            ),
3394            (
3395                String::from("0--this is a comment\r1"),
3396                vec![
3397                    Token::Number("0".to_string(), false),
3398                    Token::Whitespace(Whitespace::SingleLineComment {
3399                        prefix: "--".to_string(),
3400                        comment: "this is a comment\r1".to_string(),
3401                    }),
3402                ],
3403            ),
3404            (
3405                String::from("0--this is a comment\r\n1"),
3406                vec![
3407                    Token::Number("0".to_string(), false),
3408                    Token::Whitespace(Whitespace::SingleLineComment {
3409                        prefix: "--".to_string(),
3410                        comment: "this is a comment\r".to_string(),
3411                    }),
3412                    Token::Whitespace(Whitespace::Newline),
3413                    Token::Number("1".to_string(), false),
3414                ],
3415            ),
3416        ];
3417
3418        let dialect = GenericDialect {};
3419
3420        for (sql, expected) in test_cases {
3421            let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3422            compare(expected, tokens);
3423        }
3424    }
3425
3426    #[test]
3427    fn tokenize_comment_postgres() {
3428        let sql = String::from("1--\r0");
3429
3430        let dialect = PostgreSqlDialect {};
3431        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3432        let expected = vec![
3433            Token::Number("1".to_string(), false),
3434            Token::Whitespace(Whitespace::SingleLineComment {
3435                prefix: "--".to_string(),
3436                comment: "".to_string(),
3437            }),
3438            Token::Whitespace(Whitespace::Newline), // Postgres treats \r as newline in single-line comments
3439            Token::Number("0".to_string(), false),
3440        ];
3441        compare(expected, tokens);
3442    }
3443
3444    #[test]
3445    fn tokenize_comment_at_eof() {
3446        let sql = String::from("--this is a comment");
3447
3448        let dialect = GenericDialect {};
3449        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3450        let expected = vec![Token::Whitespace(Whitespace::SingleLineComment {
3451            prefix: "--".to_string(),
3452            comment: "this is a comment".to_string(),
3453        })];
3454        compare(expected, tokens);
3455    }
3456
3457    #[test]
3458    fn tokenize_multiline_comment() {
3459        let sql = String::from("0/*multi-line\n* /comment*/1");
3460
3461        let dialect = GenericDialect {};
3462        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3463        let expected = vec![
3464            Token::Number("0".to_string(), false),
3465            Token::Whitespace(Whitespace::MultiLineComment(
3466                "multi-line\n* /comment".to_string(),
3467            )),
3468            Token::Number("1".to_string(), false),
3469        ];
3470        compare(expected, tokens);
3471    }
3472
3473    #[test]
3474    fn tokenize_nested_multiline_comment() {
3475        all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3476            "0/*multi-line\n* \n/* comment \n /*comment*/*/ */ /comment*/1",
3477            vec![
3478                Token::Number("0".to_string(), false),
3479                Token::Whitespace(Whitespace::MultiLineComment(
3480                    "multi-line\n* \n/* comment \n /*comment*/*/ ".into(),
3481                )),
3482                Token::Whitespace(Whitespace::Space),
3483                Token::Div,
3484                Token::Word(Word {
3485                    value: "comment".to_string(),
3486                    quote_style: None,
3487                    keyword: Keyword::COMMENT,
3488                }),
3489                Token::Mul,
3490                Token::Div,
3491                Token::Number("1".to_string(), false),
3492            ],
3493        );
3494
3495        all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3496            "0/*multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/*/1",
3497            vec![
3498                Token::Number("0".to_string(), false),
3499                Token::Whitespace(Whitespace::MultiLineComment(
3500                    "multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/".into(),
3501                )),
3502                Token::Number("1".to_string(), false),
3503            ],
3504        );
3505
3506        all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3507            "SELECT 1/* a /* b */ c */0",
3508            vec![
3509                Token::make_keyword("SELECT"),
3510                Token::Whitespace(Whitespace::Space),
3511                Token::Number("1".to_string(), false),
3512                Token::Whitespace(Whitespace::MultiLineComment(" a /* b */ c ".to_string())),
3513                Token::Number("0".to_string(), false),
3514            ],
3515        );
3516    }
3517
3518    #[test]
3519    fn tokenize_nested_multiline_comment_empty() {
3520        all_dialects_where(|d| d.supports_nested_comments()).tokenizes_to(
3521            "select 1/*/**/*/0",
3522            vec![
3523                Token::make_keyword("select"),
3524                Token::Whitespace(Whitespace::Space),
3525                Token::Number("1".to_string(), false),
3526                Token::Whitespace(Whitespace::MultiLineComment("/**/".to_string())),
3527                Token::Number("0".to_string(), false),
3528            ],
3529        );
3530    }
3531
3532    #[test]
3533    fn tokenize_nested_comments_if_not_supported() {
3534        all_dialects_except(|d| d.supports_nested_comments()).tokenizes_to(
3535            "SELECT 1/*/* nested comment */*/0",
3536            vec![
3537                Token::make_keyword("SELECT"),
3538                Token::Whitespace(Whitespace::Space),
3539                Token::Number("1".to_string(), false),
3540                Token::Whitespace(Whitespace::MultiLineComment(
3541                    "/* nested comment ".to_string(),
3542                )),
3543                Token::Mul,
3544                Token::Div,
3545                Token::Number("0".to_string(), false),
3546            ],
3547        );
3548    }
3549
3550    #[test]
3551    fn tokenize_multiline_comment_with_even_asterisks() {
3552        let sql = String::from("\n/** Comment **/\n");
3553
3554        let dialect = GenericDialect {};
3555        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3556        let expected = vec![
3557            Token::Whitespace(Whitespace::Newline),
3558            Token::Whitespace(Whitespace::MultiLineComment("* Comment *".to_string())),
3559            Token::Whitespace(Whitespace::Newline),
3560        ];
3561        compare(expected, tokens);
3562    }
3563
3564    #[test]
3565    fn tokenize_unicode_whitespace() {
3566        let sql = String::from(" \u{2003}\n");
3567
3568        let dialect = GenericDialect {};
3569        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3570        let expected = vec![
3571            Token::Whitespace(Whitespace::Space),
3572            Token::Whitespace(Whitespace::Space),
3573            Token::Whitespace(Whitespace::Newline),
3574        ];
3575        compare(expected, tokens);
3576    }
3577
3578    #[test]
3579    fn tokenize_mismatched_quotes() {
3580        let sql = String::from("\"foo");
3581
3582        let dialect = GenericDialect {};
3583        let mut tokenizer = Tokenizer::new(&dialect, &sql);
3584        assert_eq!(
3585            tokenizer.tokenize(),
3586            Err(TokenizerError {
3587                message: "Expected close delimiter '\"' before EOF.".to_string(),
3588                location: Location { line: 1, column: 1 },
3589            })
3590        );
3591    }
3592
3593    #[test]
3594    fn tokenize_newlines() {
3595        let sql = String::from("line1\nline2\rline3\r\nline4\r");
3596
3597        let dialect = GenericDialect {};
3598        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
3599        let expected = vec![
3600            Token::make_word("line1", None),
3601            Token::Whitespace(Whitespace::Newline),
3602            Token::make_word("line2", None),
3603            Token::Whitespace(Whitespace::Newline),
3604            Token::make_word("line3", None),
3605            Token::Whitespace(Whitespace::Newline),
3606            Token::make_word("line4", None),
3607            Token::Whitespace(Whitespace::Newline),
3608        ];
3609        compare(expected, tokens);
3610    }
3611
3612    #[test]
3613    fn tokenize_mssql_top() {
3614        let sql = "SELECT TOP 5 [bar] FROM foo";
3615        let dialect = MsSqlDialect {};
3616        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3617        let expected = vec![
3618            Token::make_keyword("SELECT"),
3619            Token::Whitespace(Whitespace::Space),
3620            Token::make_keyword("TOP"),
3621            Token::Whitespace(Whitespace::Space),
3622            Token::Number(String::from("5"), false),
3623            Token::Whitespace(Whitespace::Space),
3624            Token::make_word("bar", Some('[')),
3625            Token::Whitespace(Whitespace::Space),
3626            Token::make_keyword("FROM"),
3627            Token::Whitespace(Whitespace::Space),
3628            Token::make_word("foo", None),
3629        ];
3630        compare(expected, tokens);
3631    }
3632
3633    #[test]
3634    fn tokenize_pg_regex_match() {
3635        let sql = "SELECT col ~ '^a', col ~* '^a', col !~ '^a', col !~* '^a'";
3636        let dialect = GenericDialect {};
3637        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3638        let expected = vec![
3639            Token::make_keyword("SELECT"),
3640            Token::Whitespace(Whitespace::Space),
3641            Token::make_word("col", None),
3642            Token::Whitespace(Whitespace::Space),
3643            Token::Tilde,
3644            Token::Whitespace(Whitespace::Space),
3645            Token::SingleQuotedString("^a".into()),
3646            Token::Comma,
3647            Token::Whitespace(Whitespace::Space),
3648            Token::make_word("col", None),
3649            Token::Whitespace(Whitespace::Space),
3650            Token::TildeAsterisk,
3651            Token::Whitespace(Whitespace::Space),
3652            Token::SingleQuotedString("^a".into()),
3653            Token::Comma,
3654            Token::Whitespace(Whitespace::Space),
3655            Token::make_word("col", None),
3656            Token::Whitespace(Whitespace::Space),
3657            Token::ExclamationMarkTilde,
3658            Token::Whitespace(Whitespace::Space),
3659            Token::SingleQuotedString("^a".into()),
3660            Token::Comma,
3661            Token::Whitespace(Whitespace::Space),
3662            Token::make_word("col", None),
3663            Token::Whitespace(Whitespace::Space),
3664            Token::ExclamationMarkTildeAsterisk,
3665            Token::Whitespace(Whitespace::Space),
3666            Token::SingleQuotedString("^a".into()),
3667        ];
3668        compare(expected, tokens);
3669    }
3670
3671    #[test]
3672    fn tokenize_pg_like_match() {
3673        let sql = "SELECT col ~~ '_a%', col ~~* '_a%', col !~~ '_a%', col !~~* '_a%'";
3674        let dialect = GenericDialect {};
3675        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3676        let expected = vec![
3677            Token::make_keyword("SELECT"),
3678            Token::Whitespace(Whitespace::Space),
3679            Token::make_word("col", None),
3680            Token::Whitespace(Whitespace::Space),
3681            Token::DoubleTilde,
3682            Token::Whitespace(Whitespace::Space),
3683            Token::SingleQuotedString("_a%".into()),
3684            Token::Comma,
3685            Token::Whitespace(Whitespace::Space),
3686            Token::make_word("col", None),
3687            Token::Whitespace(Whitespace::Space),
3688            Token::DoubleTildeAsterisk,
3689            Token::Whitespace(Whitespace::Space),
3690            Token::SingleQuotedString("_a%".into()),
3691            Token::Comma,
3692            Token::Whitespace(Whitespace::Space),
3693            Token::make_word("col", None),
3694            Token::Whitespace(Whitespace::Space),
3695            Token::ExclamationMarkDoubleTilde,
3696            Token::Whitespace(Whitespace::Space),
3697            Token::SingleQuotedString("_a%".into()),
3698            Token::Comma,
3699            Token::Whitespace(Whitespace::Space),
3700            Token::make_word("col", None),
3701            Token::Whitespace(Whitespace::Space),
3702            Token::ExclamationMarkDoubleTildeAsterisk,
3703            Token::Whitespace(Whitespace::Space),
3704            Token::SingleQuotedString("_a%".into()),
3705        ];
3706        compare(expected, tokens);
3707    }
3708
3709    #[test]
3710    fn tokenize_quoted_identifier() {
3711        let sql = r#" "a "" b" "a """ "c """"" "#;
3712        let dialect = GenericDialect {};
3713        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3714        let expected = vec![
3715            Token::Whitespace(Whitespace::Space),
3716            Token::make_word(r#"a " b"#, Some('"')),
3717            Token::Whitespace(Whitespace::Space),
3718            Token::make_word(r#"a ""#, Some('"')),
3719            Token::Whitespace(Whitespace::Space),
3720            Token::make_word(r#"c """#, Some('"')),
3721            Token::Whitespace(Whitespace::Space),
3722        ];
3723        compare(expected, tokens);
3724    }
3725
3726    #[test]
3727    fn tokenize_snowflake_div() {
3728        let sql = r#"field/1000"#;
3729        let dialect = SnowflakeDialect {};
3730        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3731        let expected = vec![
3732            Token::make_word(r#"field"#, None),
3733            Token::Div,
3734            Token::Number("1000".to_string(), false),
3735        ];
3736        compare(expected, tokens);
3737    }
3738
3739    #[test]
3740    fn tokenize_quoted_identifier_with_no_escape() {
3741        let sql = r#" "a "" b" "a """ "c """"" "#;
3742        let dialect = GenericDialect {};
3743        let tokens = Tokenizer::new(&dialect, sql)
3744            .with_unescape(false)
3745            .tokenize()
3746            .unwrap();
3747        let expected = vec![
3748            Token::Whitespace(Whitespace::Space),
3749            Token::make_word(r#"a "" b"#, Some('"')),
3750            Token::Whitespace(Whitespace::Space),
3751            Token::make_word(r#"a """#, Some('"')),
3752            Token::Whitespace(Whitespace::Space),
3753            Token::make_word(r#"c """""#, Some('"')),
3754            Token::Whitespace(Whitespace::Space),
3755        ];
3756        compare(expected, tokens);
3757    }
3758
3759    #[test]
3760    fn tokenize_with_location() {
3761        let sql = "SELECT a,\n b";
3762        let dialect = GenericDialect {};
3763        let tokens = Tokenizer::new(&dialect, sql)
3764            .tokenize_with_location()
3765            .unwrap();
3766        let expected = vec![
3767            TokenWithSpan::at(Token::make_keyword("SELECT"), (1, 1).into(), (1, 7).into()),
3768            TokenWithSpan::at(
3769                Token::Whitespace(Whitespace::Space),
3770                (1, 7).into(),
3771                (1, 8).into(),
3772            ),
3773            TokenWithSpan::at(Token::make_word("a", None), (1, 8).into(), (1, 9).into()),
3774            TokenWithSpan::at(Token::Comma, (1, 9).into(), (1, 10).into()),
3775            TokenWithSpan::at(
3776                Token::Whitespace(Whitespace::Newline),
3777                (1, 10).into(),
3778                (2, 1).into(),
3779            ),
3780            TokenWithSpan::at(
3781                Token::Whitespace(Whitespace::Space),
3782                (2, 1).into(),
3783                (2, 2).into(),
3784            ),
3785            TokenWithSpan::at(Token::make_word("b", None), (2, 2).into(), (2, 3).into()),
3786        ];
3787        compare(expected, tokens);
3788    }
3789
3790    fn compare<T: PartialEq + fmt::Debug>(expected: Vec<T>, actual: Vec<T>) {
3791        //println!("------------------------------");
3792        //println!("tokens   = {:?}", actual);
3793        //println!("expected = {:?}", expected);
3794        //println!("------------------------------");
3795        assert_eq!(expected, actual);
3796    }
3797
3798    fn check_unescape(s: &str, expected: Option<&str>) {
3799        let s = format!("'{s}'");
3800        let mut state = State {
3801            peekable: s.chars().peekable(),
3802            line: 0,
3803            col: 0,
3804        };
3805
3806        assert_eq!(
3807            unescape_single_quoted_string(&mut state),
3808            expected.map(|s| s.to_string())
3809        );
3810    }
3811
3812    #[test]
3813    fn test_unescape() {
3814        check_unescape(r"\b", Some("\u{0008}"));
3815        check_unescape(r"\f", Some("\u{000C}"));
3816        check_unescape(r"\t", Some("\t"));
3817        check_unescape(r"\r\n", Some("\r\n"));
3818        check_unescape(r"\/", Some("/"));
3819        check_unescape(r"/", Some("/"));
3820        check_unescape(r"\\", Some("\\"));
3821
3822        // 16 and 32-bit hexadecimal Unicode character value
3823        check_unescape(r"\u0001", Some("\u{0001}"));
3824        check_unescape(r"\u4c91", Some("\u{4c91}"));
3825        check_unescape(r"\u4c916", Some("\u{4c91}6"));
3826        check_unescape(r"\u4c", None);
3827        check_unescape(r"\u0000", None);
3828        check_unescape(r"\U0010FFFF", Some("\u{10FFFF}"));
3829        check_unescape(r"\U00110000", None);
3830        check_unescape(r"\U00000000", None);
3831        check_unescape(r"\u", None);
3832        check_unescape(r"\U", None);
3833        check_unescape(r"\U1010FFFF", None);
3834
3835        // hexadecimal byte value
3836        check_unescape(r"\x4B", Some("\u{004b}"));
3837        check_unescape(r"\x4", Some("\u{0004}"));
3838        check_unescape(r"\x4L", Some("\u{0004}L"));
3839        check_unescape(r"\x", Some("x"));
3840        check_unescape(r"\xP", Some("xP"));
3841        check_unescape(r"\x0", None);
3842        check_unescape(r"\xCAD", None);
3843        check_unescape(r"\xA9", None);
3844
3845        // octal byte value
3846        check_unescape(r"\1", Some("\u{0001}"));
3847        check_unescape(r"\12", Some("\u{000a}"));
3848        check_unescape(r"\123", Some("\u{0053}"));
3849        check_unescape(r"\1232", Some("\u{0053}2"));
3850        check_unescape(r"\4", Some("\u{0004}"));
3851        check_unescape(r"\45", Some("\u{0025}"));
3852        check_unescape(r"\450", Some("\u{0028}"));
3853        check_unescape(r"\603", None);
3854        check_unescape(r"\0", None);
3855        check_unescape(r"\080", None);
3856
3857        // others
3858        check_unescape(r"\9", Some("9"));
3859        check_unescape(r"''", Some("'"));
3860        check_unescape(
3861            r"Hello\r\nRust/\u4c91 SQL Parser\U0010ABCD\1232",
3862            Some("Hello\r\nRust/\u{4c91} SQL Parser\u{10abcd}\u{0053}2"),
3863        );
3864        check_unescape(r"Hello\0", None);
3865        check_unescape(r"Hello\xCADRust", None);
3866    }
3867
3868    #[test]
3869    fn tokenize_numeric_prefix_trait() {
3870        #[derive(Debug)]
3871        struct NumericPrefixDialect;
3872
3873        impl Dialect for NumericPrefixDialect {
3874            fn is_identifier_start(&self, ch: char) -> bool {
3875                ch.is_ascii_lowercase()
3876                    || ch.is_ascii_uppercase()
3877                    || ch.is_ascii_digit()
3878                    || ch == '$'
3879            }
3880
3881            fn is_identifier_part(&self, ch: char) -> bool {
3882                ch.is_ascii_lowercase()
3883                    || ch.is_ascii_uppercase()
3884                    || ch.is_ascii_digit()
3885                    || ch == '_'
3886                    || ch == '$'
3887                    || ch == '{'
3888                    || ch == '}'
3889            }
3890
3891            fn supports_numeric_prefix(&self) -> bool {
3892                true
3893            }
3894        }
3895
3896        tokenize_numeric_prefix_inner(&NumericPrefixDialect {});
3897        tokenize_numeric_prefix_inner(&HiveDialect {});
3898        tokenize_numeric_prefix_inner(&MySqlDialect {});
3899    }
3900
3901    fn tokenize_numeric_prefix_inner(dialect: &dyn Dialect) {
3902        let sql = r#"SELECT * FROM 1"#;
3903        let tokens = Tokenizer::new(dialect, sql).tokenize().unwrap();
3904        let expected = vec![
3905            Token::make_keyword("SELECT"),
3906            Token::Whitespace(Whitespace::Space),
3907            Token::Mul,
3908            Token::Whitespace(Whitespace::Space),
3909            Token::make_keyword("FROM"),
3910            Token::Whitespace(Whitespace::Space),
3911            Token::Number(String::from("1"), false),
3912        ];
3913        compare(expected, tokens);
3914    }
3915
3916    #[test]
3917    fn tokenize_quoted_string_escape() {
3918        let dialect = SnowflakeDialect {};
3919        for (sql, expected, expected_unescaped) in [
3920            (r#"'%a\'%b'"#, r#"%a\'%b"#, r#"%a'%b"#),
3921            (r#"'a\'\'b\'c\'d'"#, r#"a\'\'b\'c\'d"#, r#"a''b'c'd"#),
3922            (r#"'\\'"#, r#"\\"#, r#"\"#),
3923            (
3924                r#"'\0\a\b\f\n\r\t\Z'"#,
3925                r#"\0\a\b\f\n\r\t\Z"#,
3926                "\0\u{7}\u{8}\u{c}\n\r\t\u{1a}",
3927            ),
3928            (r#"'\"'"#, r#"\""#, "\""),
3929            (r#"'\\a\\b\'c'"#, r#"\\a\\b\'c"#, r#"\a\b'c"#),
3930            (r#"'\'abcd'"#, r#"\'abcd"#, r#"'abcd"#),
3931            (r#"'''a''b'"#, r#"''a''b"#, r#"'a'b"#),
3932            (r#"'\q'"#, r#"\q"#, r#"q"#),
3933            (r#"'\%\_'"#, r#"\%\_"#, r#"%_"#),
3934            (r#"'\\%\\_'"#, r#"\\%\\_"#, r#"\%\_"#),
3935        ] {
3936            let tokens = Tokenizer::new(&dialect, sql)
3937                .with_unescape(false)
3938                .tokenize()
3939                .unwrap();
3940            let expected = vec![Token::SingleQuotedString(expected.to_string())];
3941            compare(expected, tokens);
3942
3943            let tokens = Tokenizer::new(&dialect, sql)
3944                .with_unescape(true)
3945                .tokenize()
3946                .unwrap();
3947            let expected = vec![Token::SingleQuotedString(expected_unescaped.to_string())];
3948            compare(expected, tokens);
3949        }
3950
3951        for sql in [r#"'\'"#, r#"'ab\'"#] {
3952            let mut tokenizer = Tokenizer::new(&dialect, sql);
3953            assert_eq!(
3954                "Unterminated string literal",
3955                tokenizer.tokenize().unwrap_err().message.as_str(),
3956            );
3957        }
3958
3959        // Non-escape dialect
3960        for (sql, expected) in [(r#"'\'"#, r#"\"#), (r#"'ab\'"#, r#"ab\"#)] {
3961            let dialect = GenericDialect {};
3962            let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3963
3964            let expected = vec![Token::SingleQuotedString(expected.to_string())];
3965
3966            compare(expected, tokens);
3967        }
3968
3969        // MySQL special case for LIKE escapes
3970        for (sql, expected) in [(r#"'\%'"#, r#"\%"#), (r#"'\_'"#, r#"\_"#)] {
3971            let dialect = MySqlDialect {};
3972            let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
3973
3974            let expected = vec![Token::SingleQuotedString(expected.to_string())];
3975
3976            compare(expected, tokens);
3977        }
3978    }
3979
3980    #[test]
3981    fn tokenize_triple_quoted_string() {
3982        fn check<F>(
3983            q: char, // The quote character to test
3984            r: char, // An alternate quote character.
3985            quote_token: F,
3986        ) where
3987            F: Fn(String) -> Token,
3988        {
3989            let dialect = BigQueryDialect {};
3990
3991            for (sql, expected, expected_unescaped) in [
3992                // Empty string
3993                (format!(r#"{q}{q}{q}{q}{q}{q}"#), "".into(), "".into()),
3994                // Should not count escaped quote as end of string.
3995                (
3996                    format!(r#"{q}{q}{q}ab{q}{q}\{q}{q}cd{q}{q}{q}"#),
3997                    format!(r#"ab{q}{q}\{q}{q}cd"#),
3998                    format!(r#"ab{q}{q}{q}{q}cd"#),
3999                ),
4000                // Simple string
4001                (
4002                    format!(r#"{q}{q}{q}abc{q}{q}{q}"#),
4003                    "abc".into(),
4004                    "abc".into(),
4005                ),
4006                // Mix single-double quotes unescaped.
4007                (
4008                    format!(r#"{q}{q}{q}ab{r}{r}{r}c{r}def{r}{r}{r}{q}{q}{q}"#),
4009                    format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
4010                    format!("ab{r}{r}{r}c{r}def{r}{r}{r}"),
4011                ),
4012                // Escaped quote.
4013                (
4014                    format!(r#"{q}{q}{q}ab{q}{q}c{q}{q}\{q}de{q}{q}f{q}{q}{q}"#),
4015                    format!(r#"ab{q}{q}c{q}{q}\{q}de{q}{q}f"#),
4016                    format!(r#"ab{q}{q}c{q}{q}{q}de{q}{q}f"#),
4017                ),
4018                // backslash-escaped quote characters.
4019                (
4020                    format!(r#"{q}{q}{q}a\'\'b\'c\'d{q}{q}{q}"#),
4021                    r#"a\'\'b\'c\'d"#.into(),
4022                    r#"a''b'c'd"#.into(),
4023                ),
4024                // backslash-escaped characters
4025                (
4026                    format!(r#"{q}{q}{q}abc\0\n\rdef{q}{q}{q}"#),
4027                    r#"abc\0\n\rdef"#.into(),
4028                    "abc\0\n\rdef".into(),
4029                ),
4030            ] {
4031                let tokens = Tokenizer::new(&dialect, sql.as_str())
4032                    .with_unescape(false)
4033                    .tokenize()
4034                    .unwrap();
4035                let expected = vec![quote_token(expected.to_string())];
4036                compare(expected, tokens);
4037
4038                let tokens = Tokenizer::new(&dialect, sql.as_str())
4039                    .with_unescape(true)
4040                    .tokenize()
4041                    .unwrap();
4042                let expected = vec![quote_token(expected_unescaped.to_string())];
4043                compare(expected, tokens);
4044            }
4045
4046            for sql in [
4047                format!(r#"{q}{q}{q}{q}{q}\{q}"#),
4048                format!(r#"{q}{q}{q}abc{q}{q}\{q}"#),
4049                format!(r#"{q}{q}{q}{q}"#),
4050                format!(r#"{q}{q}{q}{r}{r}"#),
4051                format!(r#"{q}{q}{q}abc{q}"#),
4052                format!(r#"{q}{q}{q}abc{q}{q}"#),
4053                format!(r#"{q}{q}{q}abc"#),
4054            ] {
4055                let dialect = BigQueryDialect {};
4056                let mut tokenizer = Tokenizer::new(&dialect, sql.as_str());
4057                assert_eq!(
4058                    "Unterminated string literal",
4059                    tokenizer.tokenize().unwrap_err().message.as_str(),
4060                );
4061            }
4062        }
4063
4064        check('"', '\'', Token::TripleDoubleQuotedString);
4065
4066        check('\'', '"', Token::TripleSingleQuotedString);
4067
4068        let dialect = BigQueryDialect {};
4069
4070        let sql = r#"""''"#;
4071        let tokens = Tokenizer::new(&dialect, sql)
4072            .with_unescape(true)
4073            .tokenize()
4074            .unwrap();
4075        let expected = vec![
4076            Token::DoubleQuotedString("".to_string()),
4077            Token::SingleQuotedString("".to_string()),
4078        ];
4079        compare(expected, tokens);
4080
4081        let sql = r#"''"""#;
4082        let tokens = Tokenizer::new(&dialect, sql)
4083            .with_unescape(true)
4084            .tokenize()
4085            .unwrap();
4086        let expected = vec![
4087            Token::SingleQuotedString("".to_string()),
4088            Token::DoubleQuotedString("".to_string()),
4089        ];
4090        compare(expected, tokens);
4091
4092        // Non-triple quoted string dialect
4093        let dialect = SnowflakeDialect {};
4094        let sql = r#"''''''"#;
4095        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4096        let expected = vec![Token::SingleQuotedString("''".to_string())];
4097        compare(expected, tokens);
4098    }
4099
4100    #[test]
4101    fn test_mysql_users_grantees() {
4102        let dialect = MySqlDialect {};
4103
4104        let sql = "CREATE USER `root`@`%`";
4105        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4106        let expected = vec![
4107            Token::make_keyword("CREATE"),
4108            Token::Whitespace(Whitespace::Space),
4109            Token::make_keyword("USER"),
4110            Token::Whitespace(Whitespace::Space),
4111            Token::make_word("root", Some('`')),
4112            Token::AtSign,
4113            Token::make_word("%", Some('`')),
4114        ];
4115        compare(expected, tokens);
4116    }
4117
4118    #[test]
4119    fn test_postgres_abs_without_space_and_string_literal() {
4120        let dialect = MySqlDialect {};
4121
4122        let sql = "SELECT @'1'";
4123        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4124        let expected = vec![
4125            Token::make_keyword("SELECT"),
4126            Token::Whitespace(Whitespace::Space),
4127            Token::AtSign,
4128            Token::SingleQuotedString("1".to_string()),
4129        ];
4130        compare(expected, tokens);
4131    }
4132
4133    #[test]
4134    fn test_postgres_abs_without_space_and_quoted_column() {
4135        let dialect = MySqlDialect {};
4136
4137        let sql = r#"SELECT @"bar" FROM foo"#;
4138        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4139        let expected = vec![
4140            Token::make_keyword("SELECT"),
4141            Token::Whitespace(Whitespace::Space),
4142            Token::AtSign,
4143            Token::DoubleQuotedString("bar".to_string()),
4144            Token::Whitespace(Whitespace::Space),
4145            Token::make_keyword("FROM"),
4146            Token::Whitespace(Whitespace::Space),
4147            Token::make_word("foo", None),
4148        ];
4149        compare(expected, tokens);
4150    }
4151
4152    #[test]
4153    fn test_national_strings_backslash_escape_not_supported() {
4154        all_dialects_where(|dialect| {
4155            !dialect.supports_string_literal_backslash_escape()
4156                && dialect.supports_national_string_literal()
4157        })
4158        .tokenizes_to(
4159            "select n'''''\\'",
4160            vec![
4161                Token::make_keyword("select"),
4162                Token::Whitespace(Whitespace::Space),
4163                Token::NationalStringLiteral("''\\".to_string()),
4164            ],
4165        );
4166    }
4167
4168    #[test]
4169    fn test_national_strings_backslash_escape_supported() {
4170        all_dialects_where(|dialect| dialect.supports_string_literal_backslash_escape())
4171            .tokenizes_to(
4172                "select n'''''\\''",
4173                vec![
4174                    Token::make_keyword("select"),
4175                    Token::Whitespace(Whitespace::Space),
4176                    Token::NationalStringLiteral("'''".to_string()),
4177                ],
4178            );
4179    }
4180
4181    #[test]
4182    fn test_string_escape_constant_not_supported() {
4183        all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
4184            "select e'...'",
4185            vec![
4186                Token::make_keyword("select"),
4187                Token::Whitespace(Whitespace::Space),
4188                Token::make_word("e", None),
4189                Token::SingleQuotedString("...".to_string()),
4190            ],
4191        );
4192
4193        all_dialects_where(|dialect| !dialect.supports_string_escape_constant()).tokenizes_to(
4194            "select E'...'",
4195            vec![
4196                Token::make_keyword("select"),
4197                Token::Whitespace(Whitespace::Space),
4198                Token::make_word("E", None),
4199                Token::SingleQuotedString("...".to_string()),
4200            ],
4201        );
4202    }
4203
4204    #[test]
4205    fn test_string_escape_constant_supported() {
4206        all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
4207            "select e'\\''",
4208            vec![
4209                Token::make_keyword("select"),
4210                Token::Whitespace(Whitespace::Space),
4211                Token::EscapedStringLiteral("'".to_string()),
4212            ],
4213        );
4214
4215        all_dialects_where(|dialect| dialect.supports_string_escape_constant()).tokenizes_to(
4216            "select E'\\''",
4217            vec![
4218                Token::make_keyword("select"),
4219                Token::Whitespace(Whitespace::Space),
4220                Token::EscapedStringLiteral("'".to_string()),
4221            ],
4222        );
4223    }
4224
4225    #[test]
4226    fn test_whitespace_required_after_single_line_comment() {
4227        all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4228            .tokenizes_to(
4229                "SELECT --'abc'",
4230                vec![
4231                    Token::make_keyword("SELECT"),
4232                    Token::Whitespace(Whitespace::Space),
4233                    Token::Minus,
4234                    Token::Minus,
4235                    Token::SingleQuotedString("abc".to_string()),
4236                ],
4237            );
4238
4239        all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4240            .tokenizes_to(
4241                "SELECT -- 'abc'",
4242                vec![
4243                    Token::make_keyword("SELECT"),
4244                    Token::Whitespace(Whitespace::Space),
4245                    Token::Whitespace(Whitespace::SingleLineComment {
4246                        prefix: "--".to_string(),
4247                        comment: " 'abc'".to_string(),
4248                    }),
4249                ],
4250            );
4251
4252        all_dialects_where(|dialect| dialect.requires_single_line_comment_whitespace())
4253            .tokenizes_to(
4254                "SELECT --",
4255                vec![
4256                    Token::make_keyword("SELECT"),
4257                    Token::Whitespace(Whitespace::Space),
4258                    Token::Minus,
4259                    Token::Minus,
4260                ],
4261            );
4262
4263        all_dialects_where(|d| d.requires_single_line_comment_whitespace()).tokenizes_to(
4264            "--\n-- Table structure for table...\n--\n",
4265            vec![
4266                Token::Whitespace(Whitespace::SingleLineComment {
4267                    prefix: "--".to_string(),
4268                    comment: "".to_string(),
4269                }),
4270                Token::Whitespace(Whitespace::Newline),
4271                Token::Whitespace(Whitespace::SingleLineComment {
4272                    prefix: "--".to_string(),
4273                    comment: " Table structure for table...".to_string(),
4274                }),
4275                Token::Whitespace(Whitespace::Newline),
4276                Token::Whitespace(Whitespace::SingleLineComment {
4277                    prefix: "--".to_string(),
4278                    comment: "".to_string(),
4279                }),
4280                Token::Whitespace(Whitespace::Newline),
4281            ],
4282        );
4283    }
4284
4285    #[test]
4286    fn test_whitespace_not_required_after_single_line_comment() {
4287        all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4288            .tokenizes_to(
4289                "SELECT --'abc'",
4290                vec![
4291                    Token::make_keyword("SELECT"),
4292                    Token::Whitespace(Whitespace::Space),
4293                    Token::Whitespace(Whitespace::SingleLineComment {
4294                        prefix: "--".to_string(),
4295                        comment: "'abc'".to_string(),
4296                    }),
4297                ],
4298            );
4299
4300        all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4301            .tokenizes_to(
4302                "SELECT -- 'abc'",
4303                vec![
4304                    Token::make_keyword("SELECT"),
4305                    Token::Whitespace(Whitespace::Space),
4306                    Token::Whitespace(Whitespace::SingleLineComment {
4307                        prefix: "--".to_string(),
4308                        comment: " 'abc'".to_string(),
4309                    }),
4310                ],
4311            );
4312
4313        all_dialects_where(|dialect| !dialect.requires_single_line_comment_whitespace())
4314            .tokenizes_to(
4315                "SELECT --",
4316                vec![
4317                    Token::make_keyword("SELECT"),
4318                    Token::Whitespace(Whitespace::Space),
4319                    Token::Whitespace(Whitespace::SingleLineComment {
4320                        prefix: "--".to_string(),
4321                        comment: "".to_string(),
4322                    }),
4323                ],
4324            );
4325    }
4326
4327    #[test]
4328    fn test_tokenize_identifiers_numeric_prefix() {
4329        all_dialects_where(|dialect| dialect.supports_numeric_prefix())
4330            .tokenizes_to("123abc", vec![Token::make_word("123abc", None)]);
4331
4332        all_dialects_where(|dialect| dialect.supports_numeric_prefix())
4333            .tokenizes_to("12e34", vec![Token::Number("12e34".to_string(), false)]);
4334
4335        all_dialects_where(|dialect| dialect.supports_numeric_prefix()).tokenizes_to(
4336            "t.12e34",
4337            vec![
4338                Token::make_word("t", None),
4339                Token::Period,
4340                Token::make_word("12e34", None),
4341            ],
4342        );
4343
4344        all_dialects_where(|dialect| dialect.supports_numeric_prefix()).tokenizes_to(
4345            "t.1two3",
4346            vec![
4347                Token::make_word("t", None),
4348                Token::Period,
4349                Token::make_word("1two3", None),
4350            ],
4351        );
4352    }
4353
4354    #[test]
4355    fn tokenize_period_underscore() {
4356        let sql = String::from("SELECT table._col");
4357        // a dialect that supports underscores in numeric literals
4358        let dialect = PostgreSqlDialect {};
4359        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
4360
4361        let expected = vec![
4362            Token::make_keyword("SELECT"),
4363            Token::Whitespace(Whitespace::Space),
4364            Token::Word(Word {
4365                value: "table".to_string(),
4366                quote_style: None,
4367                keyword: Keyword::TABLE,
4368            }),
4369            Token::Period,
4370            Token::Word(Word {
4371                value: "_col".to_string(),
4372                quote_style: None,
4373                keyword: Keyword::NoKeyword,
4374            }),
4375        ];
4376
4377        compare(expected, tokens);
4378
4379        let sql = String::from("SELECT ._123");
4380        if let Ok(tokens) = Tokenizer::new(&dialect, &sql).tokenize() {
4381            panic!("Tokenizer should have failed on {sql}, but it succeeded with {tokens:?}");
4382        }
4383
4384        let sql = String::from("SELECT ._abc");
4385        if let Ok(tokens) = Tokenizer::new(&dialect, &sql).tokenize() {
4386            panic!("Tokenizer should have failed on {sql}, but it succeeded with {tokens:?}");
4387        }
4388    }
4389
4390    #[test]
4391    fn tokenize_question_mark() {
4392        let dialect = PostgreSqlDialect {};
4393        let sql = "SELECT x ? y";
4394        let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4395        compare(
4396            tokens,
4397            vec![
4398                Token::make_keyword("SELECT"),
4399                Token::Whitespace(Whitespace::Space),
4400                Token::make_word("x", None),
4401                Token::Whitespace(Whitespace::Space),
4402                Token::Question,
4403                Token::Whitespace(Whitespace::Space),
4404                Token::make_word("y", None),
4405            ],
4406        );
4407    }
4408
4409    #[test]
4410    fn tokenize_multiline_comment_with_comment_hint() {
4411        let sql = String::from("0/*! word */1");
4412
4413        let dialect = MySqlDialect {};
4414        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
4415        let expected = vec![
4416            Token::Number("0".to_string(), false),
4417            Token::Whitespace(Whitespace::Space),
4418            Token::Word(Word {
4419                value: "word".to_string(),
4420                quote_style: None,
4421                keyword: Keyword::NoKeyword,
4422            }),
4423            Token::Whitespace(Whitespace::Space),
4424            Token::Number("1".to_string(), false),
4425        ];
4426        compare(expected, tokens);
4427    }
4428
4429    #[test]
4430    fn tokenize_multiline_comment_with_comment_hint_and_version() {
4431        let sql_multi = String::from("0 /*!50110 KEY_BLOCK_SIZE = 1024*/ 1");
4432        let dialect = MySqlDialect {};
4433        let tokens = Tokenizer::new(&dialect, &sql_multi).tokenize().unwrap();
4434        let expected = vec![
4435            Token::Number("0".to_string(), false),
4436            Token::Whitespace(Whitespace::Space),
4437            Token::Whitespace(Whitespace::Space),
4438            Token::Word(Word {
4439                value: "KEY_BLOCK_SIZE".to_string(),
4440                quote_style: None,
4441                keyword: Keyword::KEY_BLOCK_SIZE,
4442            }),
4443            Token::Whitespace(Whitespace::Space),
4444            Token::Eq,
4445            Token::Whitespace(Whitespace::Space),
4446            Token::Number("1024".to_string(), false),
4447            Token::Whitespace(Whitespace::Space),
4448            Token::Number("1".to_string(), false),
4449        ];
4450        compare(expected, tokens);
4451
4452        let tokens = Tokenizer::new(&dialect, "0 /*!50110 */ 1")
4453            .tokenize()
4454            .unwrap();
4455        compare(
4456            vec![
4457                Token::Number("0".to_string(), false),
4458                Token::Whitespace(Whitespace::Space),
4459                Token::Whitespace(Whitespace::Space),
4460                Token::Whitespace(Whitespace::Space),
4461                Token::Number("1".to_string(), false),
4462            ],
4463            tokens,
4464        );
4465
4466        let tokens = Tokenizer::new(&dialect, "0 /*!*/ 1").tokenize().unwrap();
4467        compare(
4468            vec![
4469                Token::Number("0".to_string(), false),
4470                Token::Whitespace(Whitespace::Space),
4471                Token::Whitespace(Whitespace::Space),
4472                Token::Number("1".to_string(), false),
4473            ],
4474            tokens,
4475        );
4476        let tokens = Tokenizer::new(&dialect, "0 /*!   */ 1").tokenize().unwrap();
4477        compare(
4478            vec![
4479                Token::Number("0".to_string(), false),
4480                Token::Whitespace(Whitespace::Space),
4481                Token::Whitespace(Whitespace::Space),
4482                Token::Whitespace(Whitespace::Space),
4483                Token::Whitespace(Whitespace::Space),
4484                Token::Whitespace(Whitespace::Space),
4485                Token::Number("1".to_string(), false),
4486            ],
4487            tokens,
4488        );
4489    }
4490
4491    #[test]
4492    fn tokenize_lt() {
4493        all_dialects().tokenizes_to(
4494            "select a <-50",
4495            vec![
4496                Token::make_keyword("select"),
4497                Token::Whitespace(Whitespace::Space),
4498                Token::make_word("a", None),
4499                Token::Whitespace(Whitespace::Space),
4500                Token::Lt,
4501                Token::Minus,
4502                Token::Number("50".to_string(), false),
4503            ],
4504        );
4505        all_dialects().tokenizes_to(
4506            "select a <+50",
4507            vec![
4508                Token::make_keyword("select"),
4509                Token::Whitespace(Whitespace::Space),
4510                Token::make_word("a", None),
4511                Token::Whitespace(Whitespace::Space),
4512                Token::Lt,
4513                Token::Plus,
4514                Token::Number("50".to_string(), false),
4515            ],
4516        );
4517        all_dialects().tokenizes_to(
4518            "select a <=-50",
4519            vec![
4520                Token::make_keyword("select"),
4521                Token::Whitespace(Whitespace::Space),
4522                Token::make_word("a", None),
4523                Token::Whitespace(Whitespace::Space),
4524                Token::LtEq,
4525                Token::Minus,
4526                Token::Number("50".to_string(), false),
4527            ],
4528        );
4529        all_dialects().tokenizes_to(
4530            "select a <=+50",
4531            vec![
4532                Token::make_keyword("select"),
4533                Token::Whitespace(Whitespace::Space),
4534                Token::make_word("a", None),
4535                Token::Whitespace(Whitespace::Space),
4536                Token::LtEq,
4537                Token::Plus,
4538                Token::Number("50".to_string(), false),
4539            ],
4540        );
4541        all_dialects_where(|d| d.supports_geometric_types()).tokenizes_to(
4542            "select a <->b",
4543            vec![
4544                Token::make_keyword("select"),
4545                Token::Whitespace(Whitespace::Space),
4546                Token::make_word("a", None),
4547                Token::Whitespace(Whitespace::Space),
4548                Token::TwoWayArrow,
4549                Token::make_word("b", None),
4550            ],
4551        );
4552
4553        all_dialects().tokenizes_to(
4554            "select a <-b",
4555            vec![
4556                Token::make_keyword("select"),
4557                Token::Whitespace(Whitespace::Space),
4558                Token::make_word("a", None),
4559                Token::Whitespace(Whitespace::Space),
4560                Token::Lt,
4561                Token::Minus,
4562                Token::make_word("b", None),
4563            ],
4564        );
4565        all_dialects().tokenizes_to(
4566            "select a <+b",
4567            vec![
4568                Token::make_keyword("select"),
4569                Token::Whitespace(Whitespace::Space),
4570                Token::make_word("a", None),
4571                Token::Whitespace(Whitespace::Space),
4572                Token::Lt,
4573                Token::Plus,
4574                Token::make_word("b", None),
4575            ],
4576        );
4577    }
4578
4579    #[test]
4580    fn test_word_display_quote_escaping() {
4581        assert_eq!(
4582            Word {
4583                value: "a\"b".to_string(),
4584                quote_style: Some('"'),
4585                keyword: Keyword::NoKeyword,
4586            }
4587            .to_string(),
4588            "\"a\"\"b\""
4589        );
4590        assert_eq!(
4591            Word {
4592                value: "a`b".to_string(),
4593                quote_style: Some('`'),
4594                keyword: Keyword::NoKeyword,
4595            }
4596            .to_string(),
4597            "`a``b`"
4598        );
4599        assert_eq!(
4600            Word {
4601                value: "a b".to_string(),
4602                quote_style: Some('['),
4603                keyword: Keyword::NoKeyword,
4604            }
4605            .to_string(),
4606            "[a b]"
4607        );
4608    }
4609
4610    #[test]
4611    fn tokenize_period_before_digits_after_identifier_or_bracket() {
4612        let dialect = ClickHouseDialect {};
4613        for (sql, expected) in [
4614            (
4615                "t.1",
4616                vec![
4617                    Token::make_word("t", None),
4618                    Token::Period,
4619                    Token::Number("1".to_string(), false),
4620                ],
4621            ),
4622            (
4623                "(1).1",
4624                vec![
4625                    Token::LParen,
4626                    Token::Number("1".to_string(), false),
4627                    Token::RParen,
4628                    Token::Period,
4629                    Token::Number("1".to_string(), false),
4630                ],
4631            ),
4632            (
4633                "a[1].1",
4634                vec![
4635                    Token::make_word("a", None),
4636                    Token::LBracket,
4637                    Token::Number("1".to_string(), false),
4638                    Token::RBracket,
4639                    Token::Period,
4640                    Token::Number("1".to_string(), false),
4641                ],
4642            ),
4643            (
4644                "t.1.2",
4645                vec![
4646                    Token::make_word("t", None),
4647                    Token::Period,
4648                    Token::Number("1".to_string(), false),
4649                    Token::Period,
4650                    Token::Number("2".to_string(), false),
4651                ],
4652            ),
4653            (
4654                "t .1",
4655                vec![
4656                    Token::make_word("t", None),
4657                    Token::Whitespace(Whitespace::Space),
4658                    Token::Number(".1".to_string(), false),
4659                ],
4660            ),
4661            ("1.5", vec![Token::Number("1.5".to_string(), false)]),
4662            (".5", vec![Token::Number(".5".to_string(), false)]),
4663        ] {
4664            let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
4665            compare(expected, tokens);
4666        }
4667
4668        let tokens = Tokenizer::new(&GenericDialect {}, "t.1")
4669            .tokenize()
4670            .unwrap();
4671        compare(
4672            vec![
4673                Token::make_word("t", None),
4674                Token::Number(".1".to_string(), false),
4675            ],
4676            tokens,
4677        );
4678    }
4679}