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