Skip to main content

fsqlite_parser/
token.rs

1// bd-2tu6: ยง10.1 SQL Token Types
2//
3// Every SQL token carries a discriminant and a byte-offset Span.
4// Keywords are their own variants for O(1) matching in the parser.
5
6use fsqlite_ast::Span;
7
8/// A single token produced by the lexer.
9#[derive(Debug, Clone, PartialEq)]
10pub struct Token {
11    /// The token discriminant.
12    pub kind: TokenKind,
13    /// Byte-offset span into the original source.
14    pub span: Span,
15    /// Line number (1-based) at the start of the token.
16    pub line: u32,
17    /// Column number (1-based) at the start of the token.
18    pub col: u32,
19}
20
21/// Token discriminant.
22///
23/// Organized by category: literals, identifiers, keywords (~120), operators,
24/// punctuation, and special tokens.
25#[derive(Debug, Clone, PartialEq)]
26pub enum TokenKind {
27    // === Literals ===
28    /// Integer literal: `42`, `-7`, `0xFF`.
29    Integer(i64),
30    /// Integer literal that exceeded `i64::MAX` but has no decimal/exponent.
31    OversizedInt(String),
32    /// Float literal: `3.14`, `1e10`, `.5`.
33    Float(f64),
34    /// String literal (single-quoted): `'hello'`.
35    String(String),
36    /// Blob literal: `X'CAFE'`.
37    Blob(Vec<u8>),
38
39    // === Identifiers ===
40    /// Unquoted identifier.
41    Id(String),
42    /// Quoted identifier (`"name"`, `[name]`, `` `name` ``).
43    /// The bool is the EP_DblQuoted flag (true if double-quoted).
44    QuotedId(String, bool),
45
46    // === Variables / bind parameters ===
47    /// `?` anonymous positional.
48    Question,
49    /// `?NNN` numbered positional.
50    QuestionNum(u32),
51    /// `:name` colon-prefixed named.
52    ColonParam(String),
53    /// `@name` at-prefixed named.
54    AtParam(String),
55    /// `$name` dollar-prefixed named.
56    DollarParam(String),
57
58    // === Operators ===
59    Plus,
60    Minus,
61    Star,
62    Slash,
63    Percent,
64    Ampersand,
65    Pipe,
66    Tilde,
67    ShiftLeft,
68    ShiftRight,
69    Eq,   // `=`
70    EqEq, // `==`
71    Ne,   // `!=`
72    LtGt, // `<>`
73    Lt,
74    Le,
75    Gt,
76    Ge,
77    Concat,      // `||`
78    Arrow,       // `->`
79    DoubleArrow, // `->>`
80
81    // === Punctuation ===
82    Dot,
83    Comma,
84    Semicolon,
85    LeftParen,
86    RightParen,
87
88    // === Keywords ===
89    KwAbort,
90    KwAction,
91    KwAdd,
92    KwAfter,
93    KwAll,
94    KwAlter,
95    KwAlways,
96    KwAnalyze,
97    KwAnd,
98    KwAs,
99    KwAsc,
100    KwAttach,
101    KwAutoincrement,
102    KwBefore,
103    KwBegin,
104    KwBetween,
105    KwBy,
106    KwCascade,
107    KwCase,
108    KwCast,
109    KwCheck,
110    KwCollate,
111    KwColumn,
112    KwCommit,
113    KwCommitseq,
114    KwConcurrent,
115    KwConflict,
116    KwConstraint,
117    KwCreate,
118    KwCross,
119    KwCurrentDate,
120    KwCurrentTime,
121    KwCurrentTimestamp,
122    KwDatabase,
123    KwDefault,
124    KwDeferrable,
125    KwDeferred,
126    KwDelete,
127    KwDesc,
128    KwDetach,
129    KwDistinct,
130    KwDo,
131    KwDrop,
132    KwEach,
133    KwElse,
134    KwEnd,
135    KwEscape,
136    KwExcept,
137    KwExclude,
138    KwExclusive,
139    KwExists,
140    KwExplain,
141    KwFail,
142    KwFilter,
143    KwFirst,
144    KwFollowing,
145    KwFor,
146    KwForeign,
147    KwFrom,
148    KwFull,
149    KwGenerated,
150    KwGlob,
151    KwGroup,
152    KwGroups,
153    KwHaving,
154    KwIf,
155    KwIgnore,
156    KwImmediate,
157    KwIn,
158    KwIndex,
159    KwIndexed,
160    KwInitially,
161    KwInner,
162    KwInsert,
163    KwInstead,
164    KwIntersect,
165    KwInto,
166    KwIs,
167    KwIsnull,
168    KwJoin,
169    KwKey,
170    KwLast,
171    KwLeft,
172    KwLike,
173    KwLimit,
174    KwMatch,
175    KwMaterialized,
176    KwNatural,
177    KwNo,
178    KwNot,
179    KwNothing,
180    KwNotnull,
181    KwNull,
182    KwNulls,
183    KwOf,
184    KwOffset,
185    KwOn,
186    KwOr,
187    KwOrder,
188    KwOthers,
189    KwOuter,
190    KwOver,
191    KwPartition,
192    KwPlan,
193    KwPragma,
194    KwPreceding,
195    KwPrimary,
196    KwQuery,
197    KwRaise,
198    KwRange,
199    KwRecursive,
200    KwReferences,
201    KwRegexp,
202    KwReindex,
203    KwRelease,
204    KwRename,
205    KwReplace,
206    KwRestrict,
207    KwReturning,
208    KwRight,
209    KwRollback,
210    KwRow,
211    KwRows,
212    KwSavepoint,
213    KwSelect,
214    KwSet,
215    KwStored,
216    KwStrict,
217    KwTable,
218    KwTemp,
219    KwTemporary,
220    KwThen,
221    KwTies,
222    KwTo,
223    KwTransaction,
224    KwTrigger,
225    KwTrue,
226    KwFalse,
227    KwUnbounded,
228    KwUnion,
229    KwUnique,
230    KwUpdate,
231    KwUsing,
232    KwVacuum,
233    KwValues,
234    KwView,
235    KwVirtual,
236    KwWhen,
237    KwWhere,
238    KwWindow,
239    KwWith,
240    KwWithout,
241
242    // === Special ===
243    /// End of input.
244    Eof,
245    /// Lexer error (invalid input).
246    Error(String),
247}
248
249impl TokenKind {
250    /// Fast-path lookup for common SQL keywords directly on ASCII bytes.
251    ///
252    /// This avoids allocating an owned identifier string and an uppercased
253    /// temporary for the keywords that dominate hot query parsing paths.
254    #[must_use]
255    pub(crate) fn lookup_common_keyword_bytes(s: &[u8]) -> Option<Self> {
256        match s.len() {
257            2 => {
258                if s.eq_ignore_ascii_case(b"AS") {
259                    Some(Self::KwAs)
260                } else if s.eq_ignore_ascii_case(b"BY") {
261                    Some(Self::KwBy)
262                } else if s.eq_ignore_ascii_case(b"DO") {
263                    Some(Self::KwDo)
264                } else if s.eq_ignore_ascii_case(b"IF") {
265                    Some(Self::KwIf)
266                } else if s.eq_ignore_ascii_case(b"IN") {
267                    Some(Self::KwIn)
268                } else if s.eq_ignore_ascii_case(b"IS") {
269                    Some(Self::KwIs)
270                } else if s.eq_ignore_ascii_case(b"NO") {
271                    Some(Self::KwNo)
272                } else if s.eq_ignore_ascii_case(b"OF") {
273                    Some(Self::KwOf)
274                } else if s.eq_ignore_ascii_case(b"ON") {
275                    Some(Self::KwOn)
276                } else if s.eq_ignore_ascii_case(b"OR") {
277                    Some(Self::KwOr)
278                } else if s.eq_ignore_ascii_case(b"TO") {
279                    Some(Self::KwTo)
280                } else {
281                    None
282                }
283            }
284            3 => {
285                if s.eq_ignore_ascii_case(b"AND") {
286                    Some(Self::KwAnd)
287                } else if s.eq_ignore_ascii_case(b"ASC") {
288                    Some(Self::KwAsc)
289                } else if s.eq_ignore_ascii_case(b"END") {
290                    Some(Self::KwEnd)
291                } else if s.eq_ignore_ascii_case(b"FOR") {
292                    Some(Self::KwFor)
293                } else if s.eq_ignore_ascii_case(b"KEY") {
294                    Some(Self::KwKey)
295                } else if s.eq_ignore_ascii_case(b"NOT") {
296                    Some(Self::KwNot)
297                } else if s.eq_ignore_ascii_case(b"SET") {
298                    Some(Self::KwSet)
299                } else {
300                    None
301                }
302            }
303            4 => {
304                if s.eq_ignore_ascii_case(b"FROM") {
305                    Some(Self::KwFrom)
306                } else if s.eq_ignore_ascii_case(b"INTO") {
307                    Some(Self::KwInto)
308                } else if s.eq_ignore_ascii_case(b"JOIN") {
309                    Some(Self::KwJoin)
310                } else if s.eq_ignore_ascii_case(b"LEFT") {
311                    Some(Self::KwLeft)
312                } else if s.eq_ignore_ascii_case(b"LIKE") {
313                    Some(Self::KwLike)
314                } else if s.eq_ignore_ascii_case(b"NULL") {
315                    Some(Self::KwNull)
316                } else if s.eq_ignore_ascii_case(b"THEN") {
317                    Some(Self::KwThen)
318                } else if s.eq_ignore_ascii_case(b"WHEN") {
319                    Some(Self::KwWhen)
320                } else {
321                    None
322                }
323            }
324            5 => {
325                if s.eq_ignore_ascii_case(b"BEGIN") {
326                    Some(Self::KwBegin)
327                } else if s.eq_ignore_ascii_case(b"CROSS") {
328                    Some(Self::KwCross)
329                } else if s.eq_ignore_ascii_case(b"GROUP") {
330                    Some(Self::KwGroup)
331                } else if s.eq_ignore_ascii_case(b"INNER") {
332                    Some(Self::KwInner)
333                } else if s.eq_ignore_ascii_case(b"LIMIT") {
334                    Some(Self::KwLimit)
335                } else if s.eq_ignore_ascii_case(b"ORDER") {
336                    Some(Self::KwOrder)
337                } else if s.eq_ignore_ascii_case(b"TABLE") {
338                    Some(Self::KwTable)
339                } else if s.eq_ignore_ascii_case(b"WHERE") {
340                    Some(Self::KwWhere)
341                } else {
342                    None
343                }
344            }
345            6 => {
346                if s.eq_ignore_ascii_case(b"COMMIT") {
347                    Some(Self::KwCommit)
348                } else if s.eq_ignore_ascii_case(b"CREATE") {
349                    Some(Self::KwCreate)
350                } else if s.eq_ignore_ascii_case(b"DELETE") {
351                    Some(Self::KwDelete)
352                } else if s.eq_ignore_ascii_case(b"EXISTS") {
353                    Some(Self::KwExists)
354                } else if s.eq_ignore_ascii_case(b"INSERT") {
355                    Some(Self::KwInsert)
356                } else if s.eq_ignore_ascii_case(b"OFFSET") {
357                    Some(Self::KwOffset)
358                } else if s.eq_ignore_ascii_case(b"SELECT") {
359                    Some(Self::KwSelect)
360                } else if s.eq_ignore_ascii_case(b"UPDATE") {
361                    Some(Self::KwUpdate)
362                } else if s.eq_ignore_ascii_case(b"VALUES") {
363                    Some(Self::KwValues)
364                } else {
365                    None
366                }
367            }
368            7 => {
369                if s.eq_ignore_ascii_case(b"BETWEEN") {
370                    Some(Self::KwBetween)
371                } else if s.eq_ignore_ascii_case(b"DEFAULT") {
372                    Some(Self::KwDefault)
373                } else if s.eq_ignore_ascii_case(b"HAVING") {
374                    Some(Self::KwHaving)
375                } else if s.eq_ignore_ascii_case(b"INDEXED") {
376                    Some(Self::KwIndexed)
377                } else if s.eq_ignore_ascii_case(b"PRIMARY") {
378                    Some(Self::KwPrimary)
379                } else {
380                    None
381                }
382            }
383            8 => {
384                if s.eq_ignore_ascii_case(b"DISTINCT") {
385                    Some(Self::KwDistinct)
386                } else {
387                    None
388                }
389            }
390            9 => {
391                if s.eq_ignore_ascii_case(b"RETURNING") {
392                    Some(Self::KwReturning)
393                } else {
394                    None
395                }
396            }
397            10 => {
398                if s.eq_ignore_ascii_case(b"CONCURRENT") {
399                    Some(Self::KwConcurrent)
400                } else {
401                    None
402                }
403            }
404            11 => {
405                if s.eq_ignore_ascii_case(b"TRANSACTION") {
406                    Some(Self::KwTransaction)
407                } else {
408                    None
409                }
410            }
411            _ => None,
412        }
413    }
414
415    /// Look up an identifier string to see if it's a keyword.
416    /// Returns the keyword variant if so, else `None`.
417    #[must_use]
418    #[allow(clippy::too_many_lines)]
419    pub fn lookup_keyword(s: &str) -> Option<Self> {
420        if let Some(keyword) = Self::lookup_common_keyword_bytes(s.as_bytes()) {
421            return Some(keyword);
422        }
423        // Case-insensitive keyword matching.
424        // We uppercase for comparison since SQL keywords are case-insensitive.
425        match s.to_ascii_uppercase().as_str() {
426            "ABORT" => Some(Self::KwAbort),
427            "ACTION" => Some(Self::KwAction),
428            "ADD" => Some(Self::KwAdd),
429            "AFTER" => Some(Self::KwAfter),
430            "ALL" => Some(Self::KwAll),
431            "ALTER" => Some(Self::KwAlter),
432            "ALWAYS" => Some(Self::KwAlways),
433            "ANALYZE" => Some(Self::KwAnalyze),
434            "AND" => Some(Self::KwAnd),
435            "AS" => Some(Self::KwAs),
436            "ASC" => Some(Self::KwAsc),
437            "ATTACH" => Some(Self::KwAttach),
438            "AUTOINCREMENT" => Some(Self::KwAutoincrement),
439            "BEFORE" => Some(Self::KwBefore),
440            "BEGIN" => Some(Self::KwBegin),
441            "BETWEEN" => Some(Self::KwBetween),
442            "BY" => Some(Self::KwBy),
443            "CASCADE" => Some(Self::KwCascade),
444            "CASE" => Some(Self::KwCase),
445            "CAST" => Some(Self::KwCast),
446            "CHECK" => Some(Self::KwCheck),
447            "COLLATE" => Some(Self::KwCollate),
448            "COLUMN" => Some(Self::KwColumn),
449            "COMMIT" => Some(Self::KwCommit),
450            "COMMITSEQ" => Some(Self::KwCommitseq),
451            "CONCURRENT" => Some(Self::KwConcurrent),
452            "CONFLICT" => Some(Self::KwConflict),
453            "CONSTRAINT" => Some(Self::KwConstraint),
454            "CREATE" => Some(Self::KwCreate),
455            "CROSS" => Some(Self::KwCross),
456            "CURRENT_DATE" => Some(Self::KwCurrentDate),
457            "CURRENT_TIME" => Some(Self::KwCurrentTime),
458            "CURRENT_TIMESTAMP" => Some(Self::KwCurrentTimestamp),
459            "DATABASE" => Some(Self::KwDatabase),
460            "DEFAULT" => Some(Self::KwDefault),
461            "DEFERRABLE" => Some(Self::KwDeferrable),
462            "DEFERRED" => Some(Self::KwDeferred),
463            "DELETE" => Some(Self::KwDelete),
464            "DESC" => Some(Self::KwDesc),
465            "DETACH" => Some(Self::KwDetach),
466            "DISTINCT" => Some(Self::KwDistinct),
467            "DO" => Some(Self::KwDo),
468            "DROP" => Some(Self::KwDrop),
469            "EACH" => Some(Self::KwEach),
470            "ELSE" => Some(Self::KwElse),
471            "END" => Some(Self::KwEnd),
472            "ESCAPE" => Some(Self::KwEscape),
473            "EXCEPT" => Some(Self::KwExcept),
474            "EXCLUDE" => Some(Self::KwExclude),
475            "EXCLUSIVE" => Some(Self::KwExclusive),
476            "EXISTS" => Some(Self::KwExists),
477            "EXPLAIN" => Some(Self::KwExplain),
478            "FAIL" => Some(Self::KwFail),
479            "FILTER" => Some(Self::KwFilter),
480            "FIRST" => Some(Self::KwFirst),
481            "FOLLOWING" => Some(Self::KwFollowing),
482            "FOR" => Some(Self::KwFor),
483            "FOREIGN" => Some(Self::KwForeign),
484            "FROM" => Some(Self::KwFrom),
485            "FULL" => Some(Self::KwFull),
486            "GENERATED" => Some(Self::KwGenerated),
487            "GLOB" => Some(Self::KwGlob),
488            "GROUP" => Some(Self::KwGroup),
489            "GROUPS" => Some(Self::KwGroups),
490            "HAVING" => Some(Self::KwHaving),
491            "IF" => Some(Self::KwIf),
492            "IGNORE" => Some(Self::KwIgnore),
493            "IMMEDIATE" => Some(Self::KwImmediate),
494            "IN" => Some(Self::KwIn),
495            "INDEX" => Some(Self::KwIndex),
496            "INDEXED" => Some(Self::KwIndexed),
497            "INITIALLY" => Some(Self::KwInitially),
498            "INNER" => Some(Self::KwInner),
499            "INSERT" => Some(Self::KwInsert),
500            "INSTEAD" => Some(Self::KwInstead),
501            "INTERSECT" => Some(Self::KwIntersect),
502            "INTO" => Some(Self::KwInto),
503            "IS" => Some(Self::KwIs),
504            "ISNULL" => Some(Self::KwIsnull),
505            "JOIN" => Some(Self::KwJoin),
506            "KEY" => Some(Self::KwKey),
507            "LAST" => Some(Self::KwLast),
508            "LEFT" => Some(Self::KwLeft),
509            "LIKE" => Some(Self::KwLike),
510            "LIMIT" => Some(Self::KwLimit),
511            "MATCH" => Some(Self::KwMatch),
512            "MATERIALIZED" => Some(Self::KwMaterialized),
513            "NATURAL" => Some(Self::KwNatural),
514            "NO" => Some(Self::KwNo),
515            "NOT" => Some(Self::KwNot),
516            "NOTHING" => Some(Self::KwNothing),
517            "NOTNULL" => Some(Self::KwNotnull),
518            "NULL" => Some(Self::KwNull),
519            "NULLS" => Some(Self::KwNulls),
520            "OF" => Some(Self::KwOf),
521            "OFFSET" => Some(Self::KwOffset),
522            "ON" => Some(Self::KwOn),
523            "OR" => Some(Self::KwOr),
524            "ORDER" => Some(Self::KwOrder),
525            "OTHERS" => Some(Self::KwOthers),
526            "OUTER" => Some(Self::KwOuter),
527            "OVER" => Some(Self::KwOver),
528            "PARTITION" => Some(Self::KwPartition),
529            "PLAN" => Some(Self::KwPlan),
530            "PRAGMA" => Some(Self::KwPragma),
531            "PRECEDING" => Some(Self::KwPreceding),
532            "PRIMARY" => Some(Self::KwPrimary),
533            "QUERY" => Some(Self::KwQuery),
534            "RAISE" => Some(Self::KwRaise),
535            "RANGE" => Some(Self::KwRange),
536            "RECURSIVE" => Some(Self::KwRecursive),
537            "REFERENCES" => Some(Self::KwReferences),
538            "REGEXP" => Some(Self::KwRegexp),
539            "REINDEX" => Some(Self::KwReindex),
540            "RELEASE" => Some(Self::KwRelease),
541            "RENAME" => Some(Self::KwRename),
542            "REPLACE" => Some(Self::KwReplace),
543            "RESTRICT" => Some(Self::KwRestrict),
544            "RETURNING" => Some(Self::KwReturning),
545            "RIGHT" => Some(Self::KwRight),
546            "ROLLBACK" => Some(Self::KwRollback),
547            "ROW" => Some(Self::KwRow),
548            "ROWS" => Some(Self::KwRows),
549            "SAVEPOINT" => Some(Self::KwSavepoint),
550            "SELECT" => Some(Self::KwSelect),
551            "SET" => Some(Self::KwSet),
552            "STORED" => Some(Self::KwStored),
553            "STRICT" => Some(Self::KwStrict),
554            "TABLE" => Some(Self::KwTable),
555            "TEMP" => Some(Self::KwTemp),
556            "TEMPORARY" => Some(Self::KwTemporary),
557            "THEN" => Some(Self::KwThen),
558            "TIES" => Some(Self::KwTies),
559            "TO" => Some(Self::KwTo),
560            "TRANSACTION" => Some(Self::KwTransaction),
561            "TRIGGER" => Some(Self::KwTrigger),
562            "TRUE" => Some(Self::KwTrue),
563            "FALSE" => Some(Self::KwFalse),
564            "UNBOUNDED" => Some(Self::KwUnbounded),
565            "UNION" => Some(Self::KwUnion),
566            "UNIQUE" => Some(Self::KwUnique),
567            "UPDATE" => Some(Self::KwUpdate),
568            "USING" => Some(Self::KwUsing),
569            "VACUUM" => Some(Self::KwVacuum),
570            "VALUES" => Some(Self::KwValues),
571            "VIEW" => Some(Self::KwView),
572            "VIRTUAL" => Some(Self::KwVirtual),
573            "WHEN" => Some(Self::KwWhen),
574            "WHERE" => Some(Self::KwWhere),
575            "WINDOW" => Some(Self::KwWindow),
576            "WITH" => Some(Self::KwWith),
577            "WITHOUT" => Some(Self::KwWithout),
578            _ => None,
579        }
580    }
581
582    /// Returns true if this is a keyword that can start a statement.
583    /// Used by the parser for error recovery sync points.
584    #[must_use]
585    pub fn is_statement_start(&self) -> bool {
586        matches!(
587            self,
588            Self::KwSelect
589                | Self::KwValues
590                | Self::KwInsert
591                | Self::KwUpdate
592                | Self::KwDelete
593                | Self::KwCreate
594                | Self::KwDrop
595                | Self::KwAlter
596                | Self::KwBegin
597                | Self::KwCommit
598                | Self::KwEnd
599                | Self::KwRollback
600                | Self::KwSavepoint
601                | Self::KwRelease
602                | Self::KwAttach
603                | Self::KwDetach
604                | Self::KwPragma
605                | Self::KwVacuum
606                | Self::KwReindex
607                | Self::KwAnalyze
608                | Self::KwExplain
609                | Self::KwWith
610                | Self::KwReplace
611        )
612    }
613
614    /// Reconstruct a SQL text fragment from this token kind.
615    ///
616    /// Used for opaque argument collection (e.g. virtual table args) where
617    /// the parser needs to produce readable SQL strings from tokenised input.
618    #[must_use]
619    #[allow(clippy::too_many_lines)]
620    pub fn to_sql(&self) -> String {
621        match self {
622            Self::Integer(i) => i.to_string(),
623            Self::OversizedInt(s) | Self::Id(s) | Self::Error(s) => s.clone(),
624            Self::Float(f) => format!("{f}"),
625            Self::String(s) => format!("'{}'", s.replace('\'', "''")),
626            Self::Blob(b) => {
627                use std::fmt::Write;
628                let mut hex = std::string::String::with_capacity(3 + b.len() * 2);
629                hex.push_str("X'");
630                for byte in b {
631                    let _ = write!(hex, "{byte:02X}");
632                }
633                hex.push('\'');
634                hex
635            }
636            Self::QuotedId(s, _) => format!("\"{}\"", s.replace('"', "\"\"")),
637            Self::Question => "?".to_owned(),
638            Self::QuestionNum(n) => format!("?{n}"),
639            Self::ColonParam(s) => format!(":{s}"),
640            Self::AtParam(s) => format!("@{s}"),
641            Self::DollarParam(s) => format!("${s}"),
642            Self::Plus => "+".to_owned(),
643            Self::Minus => "-".to_owned(),
644            Self::Star => "*".to_owned(),
645            Self::Slash => "/".to_owned(),
646            Self::Percent => "%".to_owned(),
647            Self::Ampersand => "&".to_owned(),
648            Self::Pipe => "|".to_owned(),
649            Self::Tilde => "~".to_owned(),
650            Self::ShiftLeft => "<<".to_owned(),
651            Self::ShiftRight => ">>".to_owned(),
652            Self::Eq => "=".to_owned(),
653            Self::EqEq => "==".to_owned(),
654            Self::Ne => "!=".to_owned(),
655            Self::LtGt => "<>".to_owned(),
656            Self::Lt => "<".to_owned(),
657            Self::Le => "<=".to_owned(),
658            Self::Gt => ">".to_owned(),
659            Self::Ge => ">=".to_owned(),
660            Self::Concat => "||".to_owned(),
661            Self::Arrow => "->".to_owned(),
662            Self::DoubleArrow => "->>".to_owned(),
663            Self::Dot => ".".to_owned(),
664            Self::Comma => ",".to_owned(),
665            Self::Semicolon => ";".to_owned(),
666            Self::LeftParen => "(".to_owned(),
667            Self::RightParen => ")".to_owned(),
668            Self::Eof => String::new(),
669            // Keywords: return the uppercase SQL keyword text.
670            kw => kw.keyword_str().unwrap_or_default().to_owned(),
671        }
672    }
673
674    /// Return the SQL keyword text for keyword variants.
675    #[must_use]
676    #[allow(clippy::too_many_lines)]
677    pub const fn keyword_str(&self) -> Option<&'static str> {
678        match self {
679            Self::KwAbort => Some("ABORT"),
680            Self::KwAction => Some("ACTION"),
681            Self::KwAdd => Some("ADD"),
682            Self::KwAfter => Some("AFTER"),
683            Self::KwAll => Some("ALL"),
684            Self::KwAlter => Some("ALTER"),
685            Self::KwAlways => Some("ALWAYS"),
686            Self::KwAnalyze => Some("ANALYZE"),
687            Self::KwAnd => Some("AND"),
688            Self::KwAs => Some("AS"),
689            Self::KwAsc => Some("ASC"),
690            Self::KwAttach => Some("ATTACH"),
691            Self::KwAutoincrement => Some("AUTOINCREMENT"),
692            Self::KwBefore => Some("BEFORE"),
693            Self::KwBegin => Some("BEGIN"),
694            Self::KwBetween => Some("BETWEEN"),
695            Self::KwBy => Some("BY"),
696            Self::KwCascade => Some("CASCADE"),
697            Self::KwCase => Some("CASE"),
698            Self::KwCast => Some("CAST"),
699            Self::KwCheck => Some("CHECK"),
700            Self::KwCollate => Some("COLLATE"),
701            Self::KwColumn => Some("COLUMN"),
702            Self::KwCommit => Some("COMMIT"),
703            Self::KwCommitseq => Some("COMMITSEQ"),
704            Self::KwConcurrent => Some("CONCURRENT"),
705            Self::KwConflict => Some("CONFLICT"),
706            Self::KwConstraint => Some("CONSTRAINT"),
707            Self::KwCreate => Some("CREATE"),
708            Self::KwCross => Some("CROSS"),
709            Self::KwCurrentDate => Some("CURRENT_DATE"),
710            Self::KwCurrentTime => Some("CURRENT_TIME"),
711            Self::KwCurrentTimestamp => Some("CURRENT_TIMESTAMP"),
712            Self::KwDatabase => Some("DATABASE"),
713            Self::KwDefault => Some("DEFAULT"),
714            Self::KwDeferrable => Some("DEFERRABLE"),
715            Self::KwDeferred => Some("DEFERRED"),
716            Self::KwDelete => Some("DELETE"),
717            Self::KwDesc => Some("DESC"),
718            Self::KwDetach => Some("DETACH"),
719            Self::KwDistinct => Some("DISTINCT"),
720            Self::KwDo => Some("DO"),
721            Self::KwDrop => Some("DROP"),
722            Self::KwEach => Some("EACH"),
723            Self::KwElse => Some("ELSE"),
724            Self::KwEnd => Some("END"),
725            Self::KwEscape => Some("ESCAPE"),
726            Self::KwExcept => Some("EXCEPT"),
727            Self::KwExclude => Some("EXCLUDE"),
728            Self::KwExclusive => Some("EXCLUSIVE"),
729            Self::KwExists => Some("EXISTS"),
730            Self::KwExplain => Some("EXPLAIN"),
731            Self::KwFail => Some("FAIL"),
732            Self::KwFilter => Some("FILTER"),
733            Self::KwFirst => Some("FIRST"),
734            Self::KwFollowing => Some("FOLLOWING"),
735            Self::KwFor => Some("FOR"),
736            Self::KwForeign => Some("FOREIGN"),
737            Self::KwFrom => Some("FROM"),
738            Self::KwFull => Some("FULL"),
739            Self::KwGenerated => Some("GENERATED"),
740            Self::KwGlob => Some("GLOB"),
741            Self::KwGroup => Some("GROUP"),
742            Self::KwGroups => Some("GROUPS"),
743            Self::KwHaving => Some("HAVING"),
744            Self::KwIf => Some("IF"),
745            Self::KwIgnore => Some("IGNORE"),
746            Self::KwImmediate => Some("IMMEDIATE"),
747            Self::KwIn => Some("IN"),
748            Self::KwIndex => Some("INDEX"),
749            Self::KwIndexed => Some("INDEXED"),
750            Self::KwInitially => Some("INITIALLY"),
751            Self::KwInner => Some("INNER"),
752            Self::KwInsert => Some("INSERT"),
753            Self::KwInstead => Some("INSTEAD"),
754            Self::KwIntersect => Some("INTERSECT"),
755            Self::KwInto => Some("INTO"),
756            Self::KwIs => Some("IS"),
757            Self::KwIsnull => Some("ISNULL"),
758            Self::KwJoin => Some("JOIN"),
759            Self::KwKey => Some("KEY"),
760            Self::KwLast => Some("LAST"),
761            Self::KwLeft => Some("LEFT"),
762            Self::KwLike => Some("LIKE"),
763            Self::KwLimit => Some("LIMIT"),
764            Self::KwMatch => Some("MATCH"),
765            Self::KwMaterialized => Some("MATERIALIZED"),
766            Self::KwNatural => Some("NATURAL"),
767            Self::KwNo => Some("NO"),
768            Self::KwNot => Some("NOT"),
769            Self::KwNothing => Some("NOTHING"),
770            Self::KwNotnull => Some("NOTNULL"),
771            Self::KwNull => Some("NULL"),
772            Self::KwNulls => Some("NULLS"),
773            Self::KwOf => Some("OF"),
774            Self::KwOffset => Some("OFFSET"),
775            Self::KwOn => Some("ON"),
776            Self::KwOr => Some("OR"),
777            Self::KwOrder => Some("ORDER"),
778            Self::KwOthers => Some("OTHERS"),
779            Self::KwOuter => Some("OUTER"),
780            Self::KwOver => Some("OVER"),
781            Self::KwPartition => Some("PARTITION"),
782            Self::KwPlan => Some("PLAN"),
783            Self::KwPragma => Some("PRAGMA"),
784            Self::KwPreceding => Some("PRECEDING"),
785            Self::KwPrimary => Some("PRIMARY"),
786            Self::KwQuery => Some("QUERY"),
787            Self::KwRaise => Some("RAISE"),
788            Self::KwRange => Some("RANGE"),
789            Self::KwRecursive => Some("RECURSIVE"),
790            Self::KwReferences => Some("REFERENCES"),
791            Self::KwRegexp => Some("REGEXP"),
792            Self::KwReindex => Some("REINDEX"),
793            Self::KwRelease => Some("RELEASE"),
794            Self::KwRename => Some("RENAME"),
795            Self::KwReplace => Some("REPLACE"),
796            Self::KwRestrict => Some("RESTRICT"),
797            Self::KwReturning => Some("RETURNING"),
798            Self::KwRight => Some("RIGHT"),
799            Self::KwRollback => Some("ROLLBACK"),
800            Self::KwRow => Some("ROW"),
801            Self::KwRows => Some("ROWS"),
802            Self::KwSavepoint => Some("SAVEPOINT"),
803            Self::KwSelect => Some("SELECT"),
804            Self::KwSet => Some("SET"),
805            Self::KwStored => Some("STORED"),
806            Self::KwStrict => Some("STRICT"),
807            Self::KwTable => Some("TABLE"),
808            Self::KwTemp => Some("TEMP"),
809            Self::KwTemporary => Some("TEMPORARY"),
810            Self::KwThen => Some("THEN"),
811            Self::KwTies => Some("TIES"),
812            Self::KwTo => Some("TO"),
813            Self::KwTransaction => Some("TRANSACTION"),
814            Self::KwTrigger => Some("TRIGGER"),
815            Self::KwTrue => Some("TRUE"),
816            Self::KwFalse => Some("FALSE"),
817            Self::KwUnbounded => Some("UNBOUNDED"),
818            Self::KwUnion => Some("UNION"),
819            Self::KwUnique => Some("UNIQUE"),
820            Self::KwUpdate => Some("UPDATE"),
821            Self::KwUsing => Some("USING"),
822            Self::KwVacuum => Some("VACUUM"),
823            Self::KwValues => Some("VALUES"),
824            Self::KwView => Some("VIEW"),
825            Self::KwVirtual => Some("VIRTUAL"),
826            Self::KwWhen => Some("WHEN"),
827            Self::KwWhere => Some("WHERE"),
828            Self::KwWindow => Some("WINDOW"),
829            Self::KwWith => Some("WITH"),
830            Self::KwWithout => Some("WITHOUT"),
831            _ => None,
832        }
833    }
834}