1use crate::error::{Error, Result};
7use crate::guard::TokenGuardStats;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::fmt;
11use std::ops::Deref;
12use std::sync::{Arc, LazyLock};
13#[cfg(feature = "bindings")]
14use ts_rs::TS;
15
16pub fn parse_dollar_string_token(text: &str) -> (Option<String>, String) {
20 if let Some(pos) = text.find('\x00') {
21 let tag = &text[..pos];
22 let content = &text[pos + 1..];
23 (Some(tag.to_string()), content.to_string())
24 } else {
25 (None, text.to_string())
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
31#[cfg_attr(feature = "bindings", derive(TS))]
32pub struct Span {
33 pub start: usize,
35 pub end: usize,
37 pub line: usize,
39 pub column: usize,
41}
42
43impl Span {
44 pub(crate) fn through(self, end: Self) -> Self {
46 Self {
47 start: self.start,
48 ..end
49 }
50 }
51
52 pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
53 Self {
54 start,
55 end,
56 line,
57 column,
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub struct Token {
65 pub token_type: TokenType,
67 pub text: String,
69 pub span: Span,
71 #[serde(default)]
73 pub comments: Vec<String>,
74 #[serde(default)]
76 pub trailing_comments: Vec<String>,
77}
78
79impl Token {
80 pub fn new(token_type: TokenType, text: impl Into<String>, span: Span) -> Self {
82 Self {
83 token_type,
84 text: text.into(),
85 span,
86 comments: Vec::new(),
87 trailing_comments: Vec::new(),
88 }
89 }
90
91 pub fn number(n: i64) -> Self {
93 Self::new(TokenType::Number, n.to_string(), Span::default())
94 }
95
96 pub fn string(s: impl Into<String>) -> Self {
98 Self::new(TokenType::String, s, Span::default())
99 }
100
101 pub fn identifier(s: impl Into<String>) -> Self {
103 Self::new(TokenType::Identifier, s, Span::default())
104 }
105
106 pub fn var(s: impl Into<String>) -> Self {
108 Self::new(TokenType::Var, s, Span::default())
109 }
110
111 pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
113 self.comments.push(comment.into());
114 self
115 }
116}
117
118#[derive(Debug, Clone)]
119pub(crate) enum ParserTokenText {
120 Source {
121 source: Arc<str>,
122 start: usize,
123 end: usize,
124 },
125 Owned(String),
126}
127
128impl Deref for ParserTokenText {
129 type Target = str;
130
131 fn deref(&self) -> &Self::Target {
132 match self {
133 Self::Source { source, start, end } => &source[*start..*end],
134 Self::Owned(text) => text,
135 }
136 }
137}
138
139impl fmt::Display for ParserTokenText {
140 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141 formatter.write_str(self)
142 }
143}
144
145impl PartialEq<str> for ParserTokenText {
146 fn eq(&self, other: &str) -> bool {
147 self.deref() == other
148 }
149}
150
151impl PartialEq<&str> for ParserTokenText {
152 fn eq(&self, other: &&str) -> bool {
153 self.deref() == *other
154 }
155}
156
157#[derive(Debug, Clone)]
158pub(crate) struct ParserToken {
159 pub token_type: TokenType,
160 pub span: Span,
161 pub comments: Vec<String>,
162 pub trailing_comments: Vec<String>,
163 pub(crate) text: ParserTokenText,
164}
165
166impl ParserToken {
167 pub(crate) fn text(&self) -> &str {
168 &self.text
169 }
170
171 pub(crate) fn text_owned(&self) -> String {
172 self.text.to_string()
173 }
174}
175
176impl From<Token> for ParserToken {
177 fn from(token: Token) -> Self {
178 Self {
179 token_type: token.token_type,
180 span: token.span,
181 comments: token.comments,
182 trailing_comments: token.trailing_comments,
183 text: ParserTokenText::Owned(token.text),
184 }
185 }
186}
187
188trait TokenOutput: Sized {
189 fn from_source(
190 token_type: TokenType,
191 source: &str,
192 text_start: usize,
193 text_end: usize,
194 span: Span,
195 shared_source: Option<&Arc<str>>,
196 ) -> Self;
197 fn from_owned(token_type: TokenType, text: String, span: Span) -> Self;
198 fn token_type(&self) -> TokenType;
199 fn text<'a>(&'a self, source: &'a str) -> &'a str;
200 fn comments_mut(&mut self) -> &mut Vec<String>;
201 fn trailing_comments_mut(&mut self) -> &mut Vec<String>;
202}
203
204impl TokenOutput for Token {
205 fn from_source(
206 token_type: TokenType,
207 source: &str,
208 text_start: usize,
209 text_end: usize,
210 span: Span,
211 _shared_source: Option<&Arc<str>>,
212 ) -> Self {
213 Self::new(token_type, &source[text_start..text_end], span)
214 }
215
216 fn from_owned(token_type: TokenType, text: String, span: Span) -> Self {
217 Self::new(token_type, text, span)
218 }
219
220 fn token_type(&self) -> TokenType {
221 self.token_type
222 }
223
224 fn text<'a>(&'a self, _source: &'a str) -> &'a str {
225 &self.text
226 }
227
228 fn comments_mut(&mut self) -> &mut Vec<String> {
229 &mut self.comments
230 }
231
232 fn trailing_comments_mut(&mut self) -> &mut Vec<String> {
233 &mut self.trailing_comments
234 }
235}
236
237impl TokenOutput for ParserToken {
238 fn from_source(
239 token_type: TokenType,
240 _source: &str,
241 text_start: usize,
242 text_end: usize,
243 span: Span,
244 shared_source: Option<&Arc<str>>,
245 ) -> Self {
246 Self {
247 token_type,
248 span,
249 comments: Vec::new(),
250 trailing_comments: Vec::new(),
251 text: ParserTokenText::Source {
252 source: Arc::clone(shared_source.expect("parser tokenization requires source SQL")),
253 start: text_start,
254 end: text_end,
255 },
256 }
257 }
258
259 fn from_owned(token_type: TokenType, text: String, span: Span) -> Self {
260 Self {
261 token_type,
262 span,
263 comments: Vec::new(),
264 trailing_comments: Vec::new(),
265 text: ParserTokenText::Owned(text),
266 }
267 }
268
269 fn token_type(&self) -> TokenType {
270 self.token_type
271 }
272
273 fn text<'a>(&'a self, _source: &'a str) -> &'a str {
274 self.text()
275 }
276
277 fn comments_mut(&mut self) -> &mut Vec<String> {
278 &mut self.comments
279 }
280
281 fn trailing_comments_mut(&mut self) -> &mut Vec<String> {
282 &mut self.trailing_comments
283 }
284}
285
286impl fmt::Display for Token {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "{:?}({})", self.token_type, self.text)
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
294#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
295#[repr(u16)]
296pub enum TokenType {
297 LParen,
299 RParen,
300 LBracket,
301 RBracket,
302 LBrace,
303 RBrace,
304 Comma,
305 Dot,
306 Dash,
307 Plus,
308 Colon,
309 DotColon,
310 DColon,
311 DColonDollar,
312 DColonPercent,
313 DColonQMark,
314 DQMark,
315 Semicolon,
316 Star,
317 Backslash,
318 Slash,
319 Lt,
320 Lte,
321 Gt,
322 Gte,
323 Not,
324 Eq,
325 Neq,
326 NullsafeEq,
327 ColonEq,
328 ColonGt,
329 NColonGt,
330 And,
331 Or,
332 Amp,
333 DPipe,
334 PipeGt,
335 Pipe,
336 PipeSlash,
337 DPipeSlash,
338 Caret,
339 CaretAt,
340 LtLt, GtGt, Tilde,
343 Arrow,
344 DArrow,
345 FArrow,
346 Hash,
347 HashArrow,
348 DHashArrow,
349 LrArrow,
350 DAt,
351 AtAt,
352 AtQMark,
353 LtAt,
354 AtGt,
355 Dollar,
356 Parameter,
357 Session,
358 SessionParameter,
359 SessionUser,
360 DAmp,
361 AmpLt,
362 AmpGt,
363 Adjacent,
364 Xor,
365 DStar,
366 QMarkAmp,
367 QMarkPipe,
368 HashDash,
369 Exclamation,
370
371 UriStart,
372 BlockStart,
373 BlockEnd,
374 Space,
375 Break,
376
377 BlockComment, LineComment, String,
383 DollarString, TripleDoubleQuotedString, TripleSingleQuotedString, Number,
387 Identifier,
388 QuotedIdentifier,
389 Database,
390 Column,
391 ColumnDef,
392 Schema,
393 Table,
394 Warehouse,
395 Stage,
396 Streamlit,
397 Var,
398 BitString,
399 HexString,
400 HexNumber,
402 ByteString,
403 NationalString,
404 EscapeString, RawString,
406 HeredocString,
407 HeredocStringAlternative,
408 UnicodeString,
409
410 Bit,
412 Boolean,
413 TinyInt,
414 UTinyInt,
415 SmallInt,
416 USmallInt,
417 MediumInt,
418 UMediumInt,
419 Int,
420 UInt,
421 BigInt,
422 UBigInt,
423 BigNum,
424 Int128,
425 UInt128,
426 Int256,
427 UInt256,
428 Float,
429 Double,
430 UDouble,
431 Decimal,
432 Decimal32,
433 Decimal64,
434 Decimal128,
435 Decimal256,
436 DecFloat,
437 UDecimal,
438 BigDecimal,
439 Char,
440 NChar,
441 VarChar,
442 NVarChar,
443 BpChar,
444 Text,
445 MediumText,
446 LongText,
447 Blob,
448 MediumBlob,
449 LongBlob,
450 TinyBlob,
451 TinyText,
452 Name,
453 Binary,
454 VarBinary,
455 Json,
456 JsonB,
457 Time,
458 TimeTz,
459 TimeNs,
460 Timestamp,
461 TimestampTz,
462 TimestampLtz,
463 TimestampNtz,
464 TimestampS,
465 TimestampMs,
466 TimestampNs,
467 DateTime,
468 DateTime2,
469 DateTime64,
470 SmallDateTime,
471 Date,
472 Date32,
473 Int4Range,
474 Int4MultiRange,
475 Int8Range,
476 Int8MultiRange,
477 NumRange,
478 NumMultiRange,
479 TsRange,
480 TsMultiRange,
481 TsTzRange,
482 TsTzMultiRange,
483 DateRange,
484 DateMultiRange,
485 Uuid,
486 Geography,
487 GeographyPoint,
488 Nullable,
489 Geometry,
490 Point,
491 Ring,
492 LineString,
493 LocalTime,
494 LocalTimestamp,
495 SysTimestamp,
496 MultiLineString,
497 Polygon,
498 MultiPolygon,
499 HllSketch,
500 HStore,
501 Super,
502 Serial,
503 SmallSerial,
504 BigSerial,
505 Xml,
506 Year,
507 UserDefined,
508 Money,
509 SmallMoney,
510 RowVersion,
511 Image,
512 Variant,
513 Object,
514 Inet,
515 IpAddress,
516 IpPrefix,
517 Ipv4,
518 Ipv6,
519 Enum,
520 Enum8,
521 Enum16,
522 FixedString,
523 LowCardinality,
524 Nested,
525 AggregateFunction,
526 SimpleAggregateFunction,
527 TDigest,
528 Unknown,
529 Vector,
530 Dynamic,
531 Void,
532
533 Add,
535 Alias,
536 Alter,
537 All,
538 Anti,
539 Any,
540 Apply,
541 Array,
542 Asc,
543 AsOf,
544 Attach,
545 AutoIncrement,
546 Begin,
547 Between,
548 BulkCollectInto,
549 Cache,
550 Cascade,
551 Case,
552 CharacterSet,
553 Cluster,
554 ClusterBy,
555 Collate,
556 Command,
557 Comment,
558 Commit,
559 Prepare,
560 Preserve,
561 Connect,
562 ConnectBy,
563 Constraint,
564 Copy,
565 Create,
566 Cross,
567 Cube,
568 CurrentDate,
569 CurrentDateTime,
570 CurrentSchema,
571 CurrentTime,
572 CurrentTimestamp,
573 CurrentUser,
574 CurrentRole,
575 CurrentCatalog,
576 Declare,
577 Default,
578 Delete,
579 Desc,
580 Describe,
581 Detach,
582 Dictionary,
583 Distinct,
584 Distribute,
585 DistributeBy,
586 Div,
587 Drop,
588 Else,
589 End,
590 Escape,
591 Except,
592 Execute,
593 Exists,
594 False,
595 Fetch,
596 File,
597 FileFormat,
598 Filter,
599 Final,
600 First,
601 For,
602 Force,
603 ForeignKey,
604 Format,
605 From,
606 Full,
607 Function,
608 Get,
609 Glob,
610 Global,
611 Grant,
612 GroupBy,
613 GroupingSets,
614 Having,
615 Hint,
616 Ignore,
617 ILike,
618 In,
619 Index,
620 IndexedBy,
621 Inner,
622 Input,
623 Insert,
624 Install,
625 Intersect,
626 Interval,
627 Into,
628 Inpath,
629 InputFormat,
630 Introducer,
631 IRLike,
632 Is,
633 IsNull,
634 Join,
635 JoinMarker,
636 Keep,
637 Key,
638 Kill,
639 Lambda,
640 Language,
641 Lateral,
642 Left,
643 Like,
644 NotLike, NotILike, NotRLike, NotIRLike, Limit,
649 List,
650 Load,
651 Local,
652 Lock,
653 Map,
654 Match,
655 MatchCondition,
656 MatchRecognize,
657 MemberOf,
658 Materialized,
659 Merge,
660 Mod,
661 Model,
662 Natural,
663 Next,
664 NoAction,
665 Nothing,
666 NotNull,
667 Null,
668 ObjectIdentifier,
669 Offset,
670 On,
671 Only,
672 Operator,
673 OrderBy,
674 OrderSiblingsBy,
675 Ordered,
676 Ordinality,
677 Out,
678 Outer,
679 Output,
680 Over,
681 Overlaps,
682 Overwrite,
683 Partition,
684 PartitionBy,
685 Percent,
686 Pivot,
687 Placeholder,
688 Positional,
689 Pragma,
690 Prewhere,
691 PrimaryKey,
692 Procedure,
693 Properties,
694 PseudoType,
695 Put,
696 Qualify,
697 Quote,
698 QDColon,
699 Range,
700 Recursive,
701 Refresh,
702 Rename,
703 Replace,
704 Returning,
705 Revoke,
706 References,
707 Restrict,
708 Right,
709 RLike,
710 Rollback,
711 Rollup,
712 Row,
713 Rows,
714 Select,
715 Semi,
716 Savepoint,
717 Separator,
718 Sequence,
719 Serde,
720 SerdeProperties,
721 Set,
722 Settings,
723 Show,
724 Siblings,
725 SimilarTo,
726 Some,
727 Sort,
728 SortBy,
729 SoundsLike,
730 StartWith,
731 StorageIntegration,
732 StraightJoin,
733 Struct,
734 Summarize,
735 TableSample,
736 Sample,
737 Bernoulli,
738 System,
739 Block,
740 Seed,
741 Repeatable,
742 Tag,
743 Temporary,
744 Transaction,
745 To,
746 Top,
747 Then,
748 True,
749 Truncate,
750 Uncache,
751 Union,
752 Unnest,
753 Unpivot,
754 Update,
755 Use,
756 Using,
757 Values,
758 View,
759 SemanticView,
760 Volatile,
761 When,
762 Where,
763 Window,
764 With,
765 Ties,
766 Exclude,
767 No,
768 Others,
769 Unique,
770 UtcDate,
771 UtcTime,
772 UtcTimestamp,
773 VersionSnapshot,
774 TimestampSnapshot,
775 Option,
776 Sink,
777 Source,
778 Analyze,
779 Namespace,
780 Export,
781 As,
782 By,
783 Nulls,
784 Respect,
785 Last,
786 If,
787 Cast,
788 TryCast,
789 SafeCast,
790 Count,
791 Extract,
792 Substring,
793 Trim,
794 Leading,
795 Trailing,
796 Both,
797 Position,
798 Overlaying,
799 Placing,
800 Treat,
801 Within,
802 Group,
803 Order,
804
805 Unbounded,
807 Preceding,
808 Following,
809 Current,
810 Groups,
811
812 Trigger,
814 Type,
815 Domain,
816 Returns,
817 Body,
818 Increment,
819 Minvalue,
820 Maxvalue,
821 Start,
822 Cycle,
823 NoCycle,
824 Prior,
825 Generated,
826 Identity,
827 Always,
828 Measures,
830 Pattern,
831 Define,
832 Running,
833 Owned,
834 After,
835 Before,
836 Instead,
837 Each,
838 Statement,
839 Referencing,
840 Old,
841 New,
842 Of,
843 Check,
844 Authorization,
845 Restart,
846
847 Eof,
853}
854
855impl TokenType {
856 pub fn is_keyword(&self) -> bool {
858 matches!(
859 self,
860 TokenType::Select
861 | TokenType::From
862 | TokenType::Where
863 | TokenType::And
864 | TokenType::Or
865 | TokenType::Not
866 | TokenType::In
867 | TokenType::Is
868 | TokenType::Null
869 | TokenType::True
870 | TokenType::False
871 | TokenType::As
872 | TokenType::On
873 | TokenType::Join
874 | TokenType::Left
875 | TokenType::Right
876 | TokenType::Inner
877 | TokenType::Outer
878 | TokenType::Full
879 | TokenType::Cross
880 | TokenType::Semi
881 | TokenType::Anti
882 | TokenType::Union
883 | TokenType::Except
884 | TokenType::Intersect
885 | TokenType::GroupBy
886 | TokenType::OrderBy
887 | TokenType::Having
888 | TokenType::Limit
889 | TokenType::Offset
890 | TokenType::Case
891 | TokenType::When
892 | TokenType::Then
893 | TokenType::Else
894 | TokenType::End
895 | TokenType::Create
896 | TokenType::Drop
897 | TokenType::Alter
898 | TokenType::Insert
899 | TokenType::Update
900 | TokenType::Delete
901 | TokenType::Into
902 | TokenType::Values
903 | TokenType::Set
904 | TokenType::With
905 | TokenType::Distinct
906 | TokenType::All
907 | TokenType::Exists
908 | TokenType::Between
909 | TokenType::Like
910 | TokenType::ILike
911 | TokenType::Filter
913 | TokenType::Date
914 | TokenType::Timestamp
915 | TokenType::TimestampTz
916 | TokenType::Interval
917 | TokenType::Time
918 | TokenType::Table
919 | TokenType::Index
920 | TokenType::Column
921 | TokenType::Database
922 | TokenType::Schema
923 | TokenType::View
924 | TokenType::Function
925 | TokenType::Procedure
926 | TokenType::Trigger
927 | TokenType::Sequence
928 | TokenType::Over
929 | TokenType::Partition
930 | TokenType::Window
931 | TokenType::Rows
932 | TokenType::Range
933 | TokenType::First
934 | TokenType::Last
935 | TokenType::Preceding
936 | TokenType::Following
937 | TokenType::Current
938 | TokenType::Row
939 | TokenType::Unbounded
940 | TokenType::Array
941 | TokenType::Struct
942 | TokenType::Map
943 | TokenType::PrimaryKey
944 | TokenType::Key
945 | TokenType::ForeignKey
946 | TokenType::References
947 | TokenType::Unique
948 | TokenType::Check
949 | TokenType::Default
950 | TokenType::Constraint
951 | TokenType::Comment
952 | TokenType::Rollup
953 | TokenType::Cube
954 | TokenType::Grant
955 | TokenType::Revoke
956 | TokenType::Type
957 | TokenType::Use
958 | TokenType::Cache
959 | TokenType::Uncache
960 | TokenType::Load
961 | TokenType::Any
962 | TokenType::Some
963 | TokenType::Asc
964 | TokenType::Desc
965 | TokenType::Nulls
966 | TokenType::Lateral
967 | TokenType::Natural
968 | TokenType::Escape
969 | TokenType::Glob
970 | TokenType::Match
971 | TokenType::Recursive
972 | TokenType::Replace
973 | TokenType::Returns
974 | TokenType::If
975 | TokenType::Pivot
976 | TokenType::Unpivot
977 | TokenType::Json
978 | TokenType::Blob
979 | TokenType::Text
980 | TokenType::Int
981 | TokenType::BigInt
982 | TokenType::SmallInt
983 | TokenType::TinyInt
984 | TokenType::Int128
985 | TokenType::UInt128
986 | TokenType::Int256
987 | TokenType::UInt256
988 | TokenType::UInt
989 | TokenType::UBigInt
990 | TokenType::Float
991 | TokenType::Double
992 | TokenType::Decimal
993 | TokenType::Boolean
994 | TokenType::VarChar
995 | TokenType::Char
996 | TokenType::Binary
997 | TokenType::VarBinary
998 | TokenType::No
999 | TokenType::DateTime
1000 | TokenType::Truncate
1001 | TokenType::Execute
1002 | TokenType::Merge
1003 | TokenType::Top
1004 | TokenType::Begin
1005 | TokenType::Generated
1006 | TokenType::Identity
1007 | TokenType::Always
1008 | TokenType::Extract
1009 | TokenType::AsOf
1011 | TokenType::Prior
1012 | TokenType::After
1013 | TokenType::Restrict
1014 | TokenType::Cascade
1015 | TokenType::Local
1016 | TokenType::Rename
1017 | TokenType::Enum
1018 | TokenType::Within
1019 | TokenType::Format
1020 | TokenType::Final
1021 | TokenType::FileFormat
1022 | TokenType::Input
1023 | TokenType::InputFormat
1024 | TokenType::Copy
1025 | TokenType::Put
1026 | TokenType::Get
1027 | TokenType::Show
1028 | TokenType::Serde
1029 | TokenType::Sample
1030 | TokenType::Sort
1031 | TokenType::Collate
1032 | TokenType::Ties
1033 | TokenType::IsNull
1034 | TokenType::NotNull
1035 | TokenType::Exclude
1036 | TokenType::Temporary
1037 | TokenType::Add
1038 | TokenType::Ordinality
1039 | TokenType::Overlaps
1040 | TokenType::Block
1041 | TokenType::Pattern
1042 | TokenType::Group
1043 | TokenType::Cluster
1044 | TokenType::Repeatable
1045 | TokenType::Groups
1046 | TokenType::Commit
1047 | TokenType::Warehouse
1048 | TokenType::System
1049 | TokenType::By
1050 | TokenType::To
1051 | TokenType::Fetch
1052 | TokenType::For
1053 | TokenType::Only
1054 | TokenType::Next
1055 | TokenType::Lock
1056 | TokenType::Refresh
1057 | TokenType::Settings
1058 | TokenType::Operator
1059 | TokenType::Overwrite
1060 | TokenType::StraightJoin
1061 | TokenType::Start
1062 | TokenType::Ignore
1064 | TokenType::Domain
1065 | TokenType::Apply
1066 | TokenType::Respect
1067 | TokenType::Materialized
1068 | TokenType::Prewhere
1069 | TokenType::Old
1070 | TokenType::New
1071 | TokenType::Cast
1072 | TokenType::TryCast
1073 | TokenType::SafeCast
1074 | TokenType::Transaction
1075 | TokenType::Describe
1076 | TokenType::Kill
1077 | TokenType::Lambda
1078 | TokenType::Declare
1079 | TokenType::Keep
1080 | TokenType::Output
1081 | TokenType::Percent
1082 | TokenType::Qualify
1083 | TokenType::Returning
1084 | TokenType::Language
1085 | TokenType::Prepare
1086 | TokenType::Preserve
1087 | TokenType::Savepoint
1088 | TokenType::Rollback
1089 | TokenType::Body
1090 | TokenType::Increment
1091 | TokenType::Minvalue
1092 | TokenType::Maxvalue
1093 | TokenType::Cycle
1094 | TokenType::NoCycle
1095 | TokenType::Seed
1096 | TokenType::Namespace
1097 | TokenType::Authorization
1098 | TokenType::Order
1099 | TokenType::Restart
1100 | TokenType::Before
1101 | TokenType::Instead
1102 | TokenType::Each
1103 | TokenType::Statement
1104 | TokenType::Referencing
1105 | TokenType::Of
1106 | TokenType::Separator
1107 | TokenType::Others
1108 | TokenType::Placing
1109 | TokenType::Owned
1110 | TokenType::Running
1111 | TokenType::Define
1112 | TokenType::Measures
1113 | TokenType::MatchRecognize
1114 | TokenType::AutoIncrement
1115 | TokenType::Connect
1116 | TokenType::Distribute
1117 | TokenType::Bernoulli
1118 | TokenType::TableSample
1119 | TokenType::Inpath
1120 | TokenType::Pragma
1121 | TokenType::Siblings
1122 | TokenType::SerdeProperties
1123 | TokenType::RLike
1124 )
1125 }
1126
1127 pub fn is_comparison(&self) -> bool {
1129 matches!(
1130 self,
1131 TokenType::Eq
1132 | TokenType::Neq
1133 | TokenType::Lt
1134 | TokenType::Lte
1135 | TokenType::Gt
1136 | TokenType::Gte
1137 | TokenType::NullsafeEq
1138 )
1139 }
1140
1141 pub fn is_arithmetic(&self) -> bool {
1143 matches!(
1144 self,
1145 TokenType::Plus
1146 | TokenType::Dash
1147 | TokenType::Star
1148 | TokenType::Slash
1149 | TokenType::Percent
1150 | TokenType::Mod
1151 | TokenType::Div
1152 )
1153 }
1154}
1155
1156impl fmt::Display for TokenType {
1157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1158 write!(f, "{:?}", self)
1159 }
1160}
1161
1162static DEFAULT_KEYWORDS: LazyLock<HashMap<String, TokenType>> = LazyLock::new(|| {
1165 let mut keywords = HashMap::with_capacity(300);
1166 keywords.insert("SELECT".to_string(), TokenType::Select);
1168 keywords.insert("FROM".to_string(), TokenType::From);
1169 keywords.insert("WHERE".to_string(), TokenType::Where);
1170 keywords.insert("AND".to_string(), TokenType::And);
1171 keywords.insert("OR".to_string(), TokenType::Or);
1172 keywords.insert("NOT".to_string(), TokenType::Not);
1173 keywords.insert("AS".to_string(), TokenType::As);
1174 keywords.insert("ON".to_string(), TokenType::On);
1175 keywords.insert("JOIN".to_string(), TokenType::Join);
1176 keywords.insert("LEFT".to_string(), TokenType::Left);
1177 keywords.insert("RIGHT".to_string(), TokenType::Right);
1178 keywords.insert("INNER".to_string(), TokenType::Inner);
1179 keywords.insert("OUTER".to_string(), TokenType::Outer);
1180 keywords.insert("OUTPUT".to_string(), TokenType::Output);
1181 keywords.insert("FULL".to_string(), TokenType::Full);
1182 keywords.insert("CROSS".to_string(), TokenType::Cross);
1183 keywords.insert("SEMI".to_string(), TokenType::Semi);
1184 keywords.insert("ANTI".to_string(), TokenType::Anti);
1185 keywords.insert("STRAIGHT_JOIN".to_string(), TokenType::StraightJoin);
1186 keywords.insert("UNION".to_string(), TokenType::Union);
1187 keywords.insert("EXCEPT".to_string(), TokenType::Except);
1188 keywords.insert("MINUS".to_string(), TokenType::Except); keywords.insert("INTERSECT".to_string(), TokenType::Intersect);
1190 keywords.insert("GROUP".to_string(), TokenType::Group);
1191 keywords.insert("CUBE".to_string(), TokenType::Cube);
1192 keywords.insert("ROLLUP".to_string(), TokenType::Rollup);
1193 keywords.insert("WITHIN".to_string(), TokenType::Within);
1194 keywords.insert("ORDER".to_string(), TokenType::Order);
1195 keywords.insert("BY".to_string(), TokenType::By);
1196 keywords.insert("HAVING".to_string(), TokenType::Having);
1197 keywords.insert("LIMIT".to_string(), TokenType::Limit);
1198 keywords.insert("OFFSET".to_string(), TokenType::Offset);
1199 keywords.insert("ORDINALITY".to_string(), TokenType::Ordinality);
1200 keywords.insert("FETCH".to_string(), TokenType::Fetch);
1201 keywords.insert("FIRST".to_string(), TokenType::First);
1202 keywords.insert("NEXT".to_string(), TokenType::Next);
1203 keywords.insert("ONLY".to_string(), TokenType::Only);
1204 keywords.insert("KEEP".to_string(), TokenType::Keep);
1205 keywords.insert("IGNORE".to_string(), TokenType::Ignore);
1206 keywords.insert("INPUT".to_string(), TokenType::Input);
1207 keywords.insert("CASE".to_string(), TokenType::Case);
1208 keywords.insert("WHEN".to_string(), TokenType::When);
1209 keywords.insert("THEN".to_string(), TokenType::Then);
1210 keywords.insert("ELSE".to_string(), TokenType::Else);
1211 keywords.insert("END".to_string(), TokenType::End);
1212 keywords.insert("ENDIF".to_string(), TokenType::End); keywords.insert("NULL".to_string(), TokenType::Null);
1214 keywords.insert("TRUE".to_string(), TokenType::True);
1215 keywords.insert("FALSE".to_string(), TokenType::False);
1216 keywords.insert("IS".to_string(), TokenType::Is);
1217 keywords.insert("IN".to_string(), TokenType::In);
1218 keywords.insert("BETWEEN".to_string(), TokenType::Between);
1219 keywords.insert("OVERLAPS".to_string(), TokenType::Overlaps);
1220 keywords.insert("LIKE".to_string(), TokenType::Like);
1221 keywords.insert("ILIKE".to_string(), TokenType::ILike);
1222 keywords.insert("RLIKE".to_string(), TokenType::RLike);
1223 keywords.insert("REGEXP".to_string(), TokenType::RLike);
1224 keywords.insert("ESCAPE".to_string(), TokenType::Escape);
1225 keywords.insert("EXISTS".to_string(), TokenType::Exists);
1226 keywords.insert("DISTINCT".to_string(), TokenType::Distinct);
1227 keywords.insert("ALL".to_string(), TokenType::All);
1228 keywords.insert("WITH".to_string(), TokenType::With);
1229 keywords.insert("CREATE".to_string(), TokenType::Create);
1230 keywords.insert("DROP".to_string(), TokenType::Drop);
1231 keywords.insert("ALTER".to_string(), TokenType::Alter);
1232 keywords.insert("TRUNCATE".to_string(), TokenType::Truncate);
1233 keywords.insert("TABLE".to_string(), TokenType::Table);
1234 keywords.insert("VIEW".to_string(), TokenType::View);
1235 keywords.insert("INDEX".to_string(), TokenType::Index);
1236 keywords.insert("COLUMN".to_string(), TokenType::Column);
1237 keywords.insert("CONSTRAINT".to_string(), TokenType::Constraint);
1238 keywords.insert("ADD".to_string(), TokenType::Add);
1239 keywords.insert("CASCADE".to_string(), TokenType::Cascade);
1240 keywords.insert("RESTRICT".to_string(), TokenType::Restrict);
1241 keywords.insert("RENAME".to_string(), TokenType::Rename);
1242 keywords.insert("TEMPORARY".to_string(), TokenType::Temporary);
1243 keywords.insert("TEMP".to_string(), TokenType::Temporary);
1244 keywords.insert("UNIQUE".to_string(), TokenType::Unique);
1245 keywords.insert("PRIMARY".to_string(), TokenType::PrimaryKey);
1246 keywords.insert("FOREIGN".to_string(), TokenType::ForeignKey);
1247 keywords.insert("KEY".to_string(), TokenType::Key);
1248 keywords.insert("KILL".to_string(), TokenType::Kill);
1249 keywords.insert("REFERENCES".to_string(), TokenType::References);
1250 keywords.insert("DEFAULT".to_string(), TokenType::Default);
1251 keywords.insert("DECLARE".to_string(), TokenType::Declare);
1252 keywords.insert("AUTO_INCREMENT".to_string(), TokenType::AutoIncrement);
1253 keywords.insert("AUTOINCREMENT".to_string(), TokenType::AutoIncrement); keywords.insert("MATERIALIZED".to_string(), TokenType::Materialized);
1255 keywords.insert("REPLACE".to_string(), TokenType::Replace);
1256 keywords.insert("TO".to_string(), TokenType::To);
1257 keywords.insert("INSERT".to_string(), TokenType::Insert);
1258 keywords.insert("OVERWRITE".to_string(), TokenType::Overwrite);
1259 keywords.insert("UPDATE".to_string(), TokenType::Update);
1260 keywords.insert("USE".to_string(), TokenType::Use);
1261 keywords.insert("WAREHOUSE".to_string(), TokenType::Warehouse);
1262 keywords.insert("GLOB".to_string(), TokenType::Glob);
1263 keywords.insert("DELETE".to_string(), TokenType::Delete);
1264 keywords.insert("MERGE".to_string(), TokenType::Merge);
1265 keywords.insert("CACHE".to_string(), TokenType::Cache);
1266 keywords.insert("UNCACHE".to_string(), TokenType::Uncache);
1267 keywords.insert("REFRESH".to_string(), TokenType::Refresh);
1268 keywords.insert("GRANT".to_string(), TokenType::Grant);
1269 keywords.insert("REVOKE".to_string(), TokenType::Revoke);
1270 keywords.insert("COMMENT".to_string(), TokenType::Comment);
1271 keywords.insert("COLLATE".to_string(), TokenType::Collate);
1272 keywords.insert("INTO".to_string(), TokenType::Into);
1273 keywords.insert("VALUES".to_string(), TokenType::Values);
1274 keywords.insert("SET".to_string(), TokenType::Set);
1275 keywords.insert("SETTINGS".to_string(), TokenType::Settings);
1276 keywords.insert("SEPARATOR".to_string(), TokenType::Separator);
1277 keywords.insert("ASC".to_string(), TokenType::Asc);
1278 keywords.insert("DESC".to_string(), TokenType::Desc);
1279 keywords.insert("NULLS".to_string(), TokenType::Nulls);
1280 keywords.insert("RESPECT".to_string(), TokenType::Respect);
1281 keywords.insert("FIRST".to_string(), TokenType::First);
1282 keywords.insert("LAST".to_string(), TokenType::Last);
1283 keywords.insert("IF".to_string(), TokenType::If);
1284 keywords.insert("CAST".to_string(), TokenType::Cast);
1285 keywords.insert("TRY_CAST".to_string(), TokenType::TryCast);
1286 keywords.insert("SAFE_CAST".to_string(), TokenType::SafeCast);
1287 keywords.insert("OVER".to_string(), TokenType::Over);
1288 keywords.insert("PARTITION".to_string(), TokenType::Partition);
1289 keywords.insert("PLACING".to_string(), TokenType::Placing);
1290 keywords.insert("WINDOW".to_string(), TokenType::Window);
1291 keywords.insert("ROWS".to_string(), TokenType::Rows);
1292 keywords.insert("RANGE".to_string(), TokenType::Range);
1293 keywords.insert("FILTER".to_string(), TokenType::Filter);
1294 keywords.insert("NATURAL".to_string(), TokenType::Natural);
1295 keywords.insert("USING".to_string(), TokenType::Using);
1296 keywords.insert("UNBOUNDED".to_string(), TokenType::Unbounded);
1297 keywords.insert("PRECEDING".to_string(), TokenType::Preceding);
1298 keywords.insert("FOLLOWING".to_string(), TokenType::Following);
1299 keywords.insert("CURRENT".to_string(), TokenType::Current);
1300 keywords.insert("ROW".to_string(), TokenType::Row);
1301 keywords.insert("GROUPS".to_string(), TokenType::Groups);
1302 keywords.insert("RECURSIVE".to_string(), TokenType::Recursive);
1303 keywords.insert("BOTH".to_string(), TokenType::Both);
1305 keywords.insert("LEADING".to_string(), TokenType::Leading);
1306 keywords.insert("TRAILING".to_string(), TokenType::Trailing);
1307 keywords.insert("INTERVAL".to_string(), TokenType::Interval);
1308 keywords.insert("TOP".to_string(), TokenType::Top);
1310 keywords.insert("QUALIFY".to_string(), TokenType::Qualify);
1311 keywords.insert("SAMPLE".to_string(), TokenType::Sample);
1312 keywords.insert("TABLESAMPLE".to_string(), TokenType::TableSample);
1313 keywords.insert("BERNOULLI".to_string(), TokenType::Bernoulli);
1314 keywords.insert("SYSTEM".to_string(), TokenType::System);
1315 keywords.insert("BLOCK".to_string(), TokenType::Block);
1316 keywords.insert("TIES".to_string(), TokenType::Ties);
1317 keywords.insert("LATERAL".to_string(), TokenType::Lateral);
1318 keywords.insert("LAMBDA".to_string(), TokenType::Lambda);
1319 keywords.insert("APPLY".to_string(), TokenType::Apply);
1320 keywords.insert("CONNECT".to_string(), TokenType::Connect);
1322 keywords.insert("CLUSTER".to_string(), TokenType::Cluster);
1324 keywords.insert("DISTRIBUTE".to_string(), TokenType::Distribute);
1325 keywords.insert("SORT".to_string(), TokenType::Sort);
1326 keywords.insert("PIVOT".to_string(), TokenType::Pivot);
1327 keywords.insert("PREWHERE".to_string(), TokenType::Prewhere);
1328 keywords.insert("UNPIVOT".to_string(), TokenType::Unpivot);
1329 keywords.insert("FOR".to_string(), TokenType::For);
1330 keywords.insert("ANY".to_string(), TokenType::Any);
1331 keywords.insert("SOME".to_string(), TokenType::Some);
1332 keywords.insert("ASOF".to_string(), TokenType::AsOf);
1333 keywords.insert("PERCENT".to_string(), TokenType::Percent);
1334 keywords.insert("EXCLUDE".to_string(), TokenType::Exclude);
1335 keywords.insert("NO".to_string(), TokenType::No);
1336 keywords.insert("OTHERS".to_string(), TokenType::Others);
1337 keywords.insert("OPERATOR".to_string(), TokenType::Operator);
1339 keywords.insert("SCHEMA".to_string(), TokenType::Schema);
1341 keywords.insert("NAMESPACE".to_string(), TokenType::Namespace);
1342 keywords.insert("DATABASE".to_string(), TokenType::Database);
1343 keywords.insert("FUNCTION".to_string(), TokenType::Function);
1344 keywords.insert("PROCEDURE".to_string(), TokenType::Procedure);
1345 keywords.insert("PROC".to_string(), TokenType::Procedure);
1346 keywords.insert("SEQUENCE".to_string(), TokenType::Sequence);
1347 keywords.insert("TRIGGER".to_string(), TokenType::Trigger);
1348 keywords.insert("TYPE".to_string(), TokenType::Type);
1349 keywords.insert("DOMAIN".to_string(), TokenType::Domain);
1350 keywords.insert("RETURNS".to_string(), TokenType::Returns);
1351 keywords.insert("RETURNING".to_string(), TokenType::Returning);
1352 keywords.insert("LANGUAGE".to_string(), TokenType::Language);
1353 keywords.insert("ROLLBACK".to_string(), TokenType::Rollback);
1354 keywords.insert("COMMIT".to_string(), TokenType::Commit);
1355 keywords.insert("BEGIN".to_string(), TokenType::Begin);
1356 keywords.insert("DESCRIBE".to_string(), TokenType::Describe);
1357 keywords.insert("PREPARE".to_string(), TokenType::Prepare);
1358 keywords.insert("PRESERVE".to_string(), TokenType::Preserve);
1359 keywords.insert("TRANSACTION".to_string(), TokenType::Transaction);
1360 keywords.insert("SAVEPOINT".to_string(), TokenType::Savepoint);
1361 keywords.insert("BODY".to_string(), TokenType::Body);
1362 keywords.insert("INCREMENT".to_string(), TokenType::Increment);
1363 keywords.insert("MINVALUE".to_string(), TokenType::Minvalue);
1364 keywords.insert("MAXVALUE".to_string(), TokenType::Maxvalue);
1365 keywords.insert("CYCLE".to_string(), TokenType::Cycle);
1366 keywords.insert("NOCYCLE".to_string(), TokenType::NoCycle);
1367 keywords.insert("PRIOR".to_string(), TokenType::Prior);
1368 keywords.insert("MATCH".to_string(), TokenType::Match);
1370 keywords.insert("MATCH_RECOGNIZE".to_string(), TokenType::MatchRecognize);
1371 keywords.insert("MEASURES".to_string(), TokenType::Measures);
1372 keywords.insert("PATTERN".to_string(), TokenType::Pattern);
1373 keywords.insert("DEFINE".to_string(), TokenType::Define);
1374 keywords.insert("RUNNING".to_string(), TokenType::Running);
1375 keywords.insert("FINAL".to_string(), TokenType::Final);
1376 keywords.insert("OWNED".to_string(), TokenType::Owned);
1377 keywords.insert("AFTER".to_string(), TokenType::After);
1378 keywords.insert("BEFORE".to_string(), TokenType::Before);
1379 keywords.insert("INSTEAD".to_string(), TokenType::Instead);
1380 keywords.insert("EACH".to_string(), TokenType::Each);
1381 keywords.insert("STATEMENT".to_string(), TokenType::Statement);
1382 keywords.insert("REFERENCING".to_string(), TokenType::Referencing);
1383 keywords.insert("OLD".to_string(), TokenType::Old);
1384 keywords.insert("NEW".to_string(), TokenType::New);
1385 keywords.insert("OF".to_string(), TokenType::Of);
1386 keywords.insert("CHECK".to_string(), TokenType::Check);
1387 keywords.insert("START".to_string(), TokenType::Start);
1388 keywords.insert("ENUM".to_string(), TokenType::Enum);
1389 keywords.insert("AUTHORIZATION".to_string(), TokenType::Authorization);
1390 keywords.insert("RESTART".to_string(), TokenType::Restart);
1391 keywords.insert("DATE".to_string(), TokenType::Date);
1393 keywords.insert("TIME".to_string(), TokenType::Time);
1394 keywords.insert("TIMESTAMP".to_string(), TokenType::Timestamp);
1395 keywords.insert("DATETIME".to_string(), TokenType::DateTime);
1396 keywords.insert("GENERATED".to_string(), TokenType::Generated);
1397 keywords.insert("IDENTITY".to_string(), TokenType::Identity);
1398 keywords.insert("ALWAYS".to_string(), TokenType::Always);
1399 keywords.insert("LOAD".to_string(), TokenType::Load);
1401 keywords.insert("LOCAL".to_string(), TokenType::Local);
1402 keywords.insert("INPATH".to_string(), TokenType::Inpath);
1403 keywords.insert("INPUTFORMAT".to_string(), TokenType::InputFormat);
1404 keywords.insert("SERDE".to_string(), TokenType::Serde);
1405 keywords.insert("SERDEPROPERTIES".to_string(), TokenType::SerdeProperties);
1406 keywords.insert("FORMAT".to_string(), TokenType::Format);
1407 keywords.insert("PRAGMA".to_string(), TokenType::Pragma);
1409 keywords.insert("SHOW".to_string(), TokenType::Show);
1411 keywords.insert("SIBLINGS".to_string(), TokenType::Siblings);
1413 keywords.insert("COPY".to_string(), TokenType::Copy);
1415 keywords.insert("PUT".to_string(), TokenType::Put);
1416 keywords.insert("GET".to_string(), TokenType::Get);
1417 keywords.insert("EXEC".to_string(), TokenType::Execute);
1419 keywords.insert("EXECUTE".to_string(), TokenType::Execute);
1420 keywords.insert("ISNULL".to_string(), TokenType::IsNull);
1422 keywords.insert("NOTNULL".to_string(), TokenType::NotNull);
1423 keywords
1424});
1425
1426static DEFAULT_SINGLE_TOKENS: LazyLock<HashMap<char, TokenType>> = LazyLock::new(|| {
1427 let mut single_tokens = HashMap::with_capacity(30);
1428 single_tokens.insert('(', TokenType::LParen);
1429 single_tokens.insert(')', TokenType::RParen);
1430 single_tokens.insert('[', TokenType::LBracket);
1431 single_tokens.insert(']', TokenType::RBracket);
1432 single_tokens.insert('{', TokenType::LBrace);
1433 single_tokens.insert('}', TokenType::RBrace);
1434 single_tokens.insert(',', TokenType::Comma);
1435 single_tokens.insert('.', TokenType::Dot);
1436 single_tokens.insert(';', TokenType::Semicolon);
1437 single_tokens.insert('+', TokenType::Plus);
1438 single_tokens.insert('-', TokenType::Dash);
1439 single_tokens.insert('*', TokenType::Star);
1440 single_tokens.insert('/', TokenType::Slash);
1441 single_tokens.insert('%', TokenType::Percent);
1442 single_tokens.insert('&', TokenType::Amp);
1443 single_tokens.insert('|', TokenType::Pipe);
1444 single_tokens.insert('^', TokenType::Caret);
1445 single_tokens.insert('~', TokenType::Tilde);
1446 single_tokens.insert('<', TokenType::Lt);
1447 single_tokens.insert('>', TokenType::Gt);
1448 single_tokens.insert('=', TokenType::Eq);
1449 single_tokens.insert('!', TokenType::Exclamation);
1450 single_tokens.insert(':', TokenType::Colon);
1451 single_tokens.insert('@', TokenType::DAt);
1452 single_tokens.insert('#', TokenType::Hash);
1453 single_tokens.insert('$', TokenType::Dollar);
1454 single_tokens.insert('?', TokenType::Parameter);
1455 single_tokens
1456});
1457
1458static DEFAULT_QUOTES: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
1459 let mut quotes = HashMap::with_capacity(4);
1460 quotes.insert("'".to_string(), "'".to_string());
1461 quotes.insert("\"\"\"".to_string(), "\"\"\"".to_string());
1463 quotes
1464});
1465
1466static DEFAULT_IDENTIFIERS: LazyLock<HashMap<char, char>> = LazyLock::new(|| {
1467 let mut identifiers = HashMap::with_capacity(4);
1468 identifiers.insert('"', '"');
1469 identifiers.insert('`', '`');
1470 identifiers
1473});
1474
1475static DEFAULT_COMMENTS: LazyLock<HashMap<String, Option<String>>> = LazyLock::new(|| {
1476 let mut comments = HashMap::with_capacity(4);
1477 comments.insert("--".to_string(), None);
1478 comments.insert("/*".to_string(), Some("*/".to_string()));
1479 comments
1480});
1481
1482#[derive(Debug, Clone)]
1484pub struct TokenizerConfig {
1485 pub keywords: HashMap<String, TokenType>,
1487 pub single_tokens: HashMap<char, TokenType>,
1489 pub quotes: HashMap<String, String>,
1491 pub identifiers: HashMap<char, char>,
1493 pub identifier_backslash_escapes: bool,
1495 pub comments: HashMap<String, Option<String>>,
1497 pub string_escapes: Vec<char>,
1499 pub nested_comments: bool,
1501 pub escape_follow_chars: Vec<char>,
1506 pub b_prefix_is_byte_string: bool,
1509 pub numeric_literals: HashMap<String, String>,
1512 pub identifiers_can_start_with_digit: bool,
1516 pub hex_number_strings: bool,
1520 pub hex_string_is_integer_type: bool,
1524 pub allow_empty_hex_string: bool,
1527 pub string_escapes_allowed_in_raw_strings: bool,
1532 pub hash_comments: bool,
1534 pub dollar_sign_is_identifier: bool,
1538 pub insert_format_raw_data: bool,
1542 pub numbers_can_be_underscore_separated: bool,
1546 pub recover_terminal_backslash_quote: bool,
1550 pub recover_unterminated_string: bool,
1554 pub double_slash_int_div: bool,
1556}
1557
1558impl Default for TokenizerConfig {
1559 fn default() -> Self {
1560 Self {
1561 keywords: DEFAULT_KEYWORDS.clone(),
1562 single_tokens: DEFAULT_SINGLE_TOKENS.clone(),
1563 quotes: DEFAULT_QUOTES.clone(),
1564 identifiers: DEFAULT_IDENTIFIERS.clone(),
1565 identifier_backslash_escapes: false,
1566 comments: DEFAULT_COMMENTS.clone(),
1567 string_escapes: vec!['\''],
1570 nested_comments: true,
1571 escape_follow_chars: vec![],
1573 b_prefix_is_byte_string: false,
1575 numeric_literals: HashMap::new(),
1576 identifiers_can_start_with_digit: false,
1577 hex_number_strings: false,
1578 hex_string_is_integer_type: false,
1579 allow_empty_hex_string: false,
1580 string_escapes_allowed_in_raw_strings: true,
1583 hash_comments: false,
1584 dollar_sign_is_identifier: false,
1585 insert_format_raw_data: false,
1586 numbers_can_be_underscore_separated: false,
1587 recover_terminal_backslash_quote: false,
1588 recover_unterminated_string: false,
1589 double_slash_int_div: false,
1590 }
1591 }
1592}
1593
1594pub struct Tokenizer {
1596 config: Arc<TokenizerConfig>,
1597}
1598
1599impl Tokenizer {
1600 pub fn new(config: TokenizerConfig) -> Self {
1602 Self {
1603 config: Arc::new(config),
1604 }
1605 }
1606
1607 pub(crate) fn from_shared_config(config: Arc<TokenizerConfig>) -> Self {
1608 Self { config }
1609 }
1610
1611 pub fn default_config() -> Self {
1613 Self::new(TokenizerConfig::default())
1614 }
1615
1616 pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
1618 if sql.is_ascii() {
1619 TokenizerState::<_, Token>::new(sql, &self.config, AsciiCursor(sql.as_bytes()))
1620 .tokenize()
1621 } else {
1622 TokenizerState::<_, Token>::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1623 }
1624 }
1625
1626 pub(crate) fn tokenize_for_parser(
1627 &self,
1628 sql: &Arc<str>,
1629 ) -> Result<(Vec<ParserToken>, TokenGuardStats)> {
1630 if sql.is_ascii() {
1631 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1632 sql,
1633 Arc::clone(sql),
1634 &self.config,
1635 AsciiCursor(sql.as_bytes()),
1636 );
1637 let tokens = state.tokenize()?;
1638 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1639 } else {
1640 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1641 sql,
1642 Arc::clone(sql),
1643 &self.config,
1644 UnicodeCursor::new(sql),
1645 );
1646 let tokens = state.tokenize()?;
1647 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1648 }
1649 }
1650
1651 #[cfg(test)]
1652 fn tokenize_without_ascii_fast_path(&self, sql: &str) -> Result<Vec<Token>> {
1653 TokenizerState::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1654 }
1655
1656 #[cfg(test)]
1657 pub(crate) fn shares_config_with(&self, other: &Self) -> bool {
1658 Arc::ptr_eq(&self.config, &other.config)
1659 }
1660}
1661
1662impl Default for Tokenizer {
1663 fn default() -> Self {
1664 Self::default_config()
1665 }
1666}
1667
1668trait TokenizerCursor {
1669 fn len(&self) -> usize;
1670 fn char_at(&self, index: usize) -> char;
1671 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String;
1672
1673 fn source_range<'a>(&self, _source: &'a str, _start: usize, _end: usize) -> Option<&'a str> {
1674 None
1675 }
1676
1677 fn range_contains(&self, start: usize, needle: char) -> bool {
1678 (start..self.len()).any(|index| self.char_at(index) == needle)
1679 }
1680}
1681
1682struct AsciiCursor<'a>(&'a [u8]);
1683
1684impl TokenizerCursor for AsciiCursor<'_> {
1685 #[inline]
1686 fn len(&self) -> usize {
1687 self.0.len()
1688 }
1689
1690 #[inline]
1691 fn char_at(&self, index: usize) -> char {
1692 self.0[index] as char
1693 }
1694
1695 #[inline]
1696 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String {
1697 source[start..end].to_string()
1698 }
1699
1700 #[inline]
1701 fn source_range<'a>(&self, source: &'a str, start: usize, end: usize) -> Option<&'a str> {
1702 Some(&source[start..end])
1703 }
1704}
1705
1706struct UnicodeCursor(Vec<char>);
1707
1708impl UnicodeCursor {
1709 fn new(source: &str) -> Self {
1710 Self(source.chars().collect())
1711 }
1712}
1713
1714impl TokenizerCursor for UnicodeCursor {
1715 #[inline]
1716 fn len(&self) -> usize {
1717 self.0.len()
1718 }
1719
1720 #[inline]
1721 fn char_at(&self, index: usize) -> char {
1722 self.0[index]
1723 }
1724
1725 #[inline]
1726 fn text_from_range(&self, _source: &str, start: usize, end: usize) -> String {
1727 self.0[start..end].iter().collect()
1728 }
1729}
1730
1731struct TokenizerState<'a, C, T> {
1733 source: &'a str,
1734 shared_source: Option<Arc<str>>,
1735 cursor: C,
1736 size: usize,
1737 tokens: Vec<T>,
1738 start: usize,
1739 current: usize,
1740 line: usize,
1741 column: usize,
1742 comments: Vec<String>,
1743 guard_stats: Option<TokenGuardStats>,
1744 config: &'a TokenizerConfig,
1745}
1746
1747impl<'a, C: TokenizerCursor, T: TokenOutput> TokenizerState<'a, C, T> {
1748 fn new(sql: &'a str, config: &'a TokenizerConfig, cursor: C) -> Self {
1749 let size = cursor.len();
1750 Self {
1751 source: sql,
1752 shared_source: None,
1753 cursor,
1754 size,
1755 tokens: Vec::new(),
1756 start: 0,
1757 current: 0,
1758 line: 1,
1759 column: 1,
1760 comments: Vec::new(),
1761 guard_stats: None,
1762 config,
1763 }
1764 }
1765
1766 fn new_shared(sql: &'a str, source: Arc<str>, config: &'a TokenizerConfig, cursor: C) -> Self {
1767 let size = cursor.len();
1768 Self {
1769 source: sql,
1770 shared_source: Some(source),
1771 cursor,
1772 size,
1773 tokens: Vec::new(),
1774 start: 0,
1775 current: 0,
1776 line: 1,
1777 column: 1,
1778 comments: Vec::new(),
1779 guard_stats: Some(TokenGuardStats::default()),
1780 config,
1781 }
1782 }
1783
1784 fn tokenize(&mut self) -> Result<Vec<T>> {
1785 while !self.is_at_end() {
1786 self.skip_whitespace();
1787 if self.is_at_end() {
1788 break;
1789 }
1790
1791 self.start = self.current;
1792 self.scan_token()?;
1793
1794 if self.config.insert_format_raw_data {
1797 if let Some(raw) = self.try_scan_insert_format_raw_data() {
1798 if !raw.is_empty() {
1799 self.start = self.current;
1800 self.add_token_with_text(TokenType::Var, raw);
1801 }
1802 }
1803 }
1804 }
1805
1806 if !self.comments.is_empty() {
1811 if let Some(last) = self.tokens.last_mut() {
1812 last.trailing_comments_mut().extend(self.comments.drain(..));
1813 }
1814 }
1815
1816 Ok(std::mem::take(&mut self.tokens))
1817 }
1818
1819 #[inline]
1820 fn is_at_end(&self) -> bool {
1821 self.current >= self.size
1822 }
1823
1824 #[inline]
1825 fn text_from_range(&self, start: usize, end: usize) -> String {
1826 self.cursor.text_from_range(self.source, start, end)
1827 }
1828
1829 #[inline]
1830 fn char_at(&self, index: usize) -> char {
1831 self.cursor.char_at(index)
1832 }
1833
1834 #[inline]
1835 fn range_contains(&self, start: usize, needle: char) -> bool {
1836 self.cursor.range_contains(start, needle)
1837 }
1838
1839 #[inline]
1840 fn peek(&self) -> char {
1841 if self.is_at_end() {
1842 '\0'
1843 } else {
1844 self.char_at(self.current)
1845 }
1846 }
1847
1848 #[inline]
1849 fn peek_next(&self) -> char {
1850 if self.current + 1 >= self.size {
1851 '\0'
1852 } else {
1853 self.char_at(self.current + 1)
1854 }
1855 }
1856
1857 #[inline]
1858 fn advance(&mut self) -> char {
1859 let c = self.peek();
1860 self.current += 1;
1861 if c == '\n' {
1862 self.line += 1;
1863 self.column = 1;
1864 } else {
1865 self.column += 1;
1866 }
1867 c
1868 }
1869
1870 #[inline]
1871 fn advance_ascii_to(&mut self, end: usize) -> bool {
1872 let Some(text) = self.cursor.source_range(self.source, self.current, end) else {
1873 return false;
1874 };
1875
1876 let newline_count = text
1877 .as_bytes()
1878 .iter()
1879 .filter(|&&byte| byte == b'\n')
1880 .count();
1881 if newline_count == 0 {
1882 self.column += end - self.current;
1883 } else {
1884 self.line += newline_count;
1885 let last_newline = text
1886 .as_bytes()
1887 .iter()
1888 .rposition(|&byte| byte == b'\n')
1889 .expect("newline count is non-zero");
1890 self.column = text.len() - last_newline;
1891 }
1892 self.current = end;
1893 true
1894 }
1895
1896 #[inline]
1897 fn advance_ascii_digits(&mut self) -> bool {
1898 let Some(rest) = self
1899 .cursor
1900 .source_range(self.source, self.current, self.size)
1901 else {
1902 return false;
1903 };
1904 let bytes = rest.as_bytes();
1905 let mut length = 0;
1906 while length < bytes.len() {
1907 match bytes[length] {
1908 b'0'..=b'9' => length += 1,
1909 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_digit) => length += 1,
1910 _ => break,
1911 }
1912 }
1913 self.current += length;
1914 self.column += length;
1915 true
1916 }
1917
1918 #[inline]
1919 fn advance_ascii_hex_digits(&mut self) -> bool {
1920 let Some(rest) = self
1921 .cursor
1922 .source_range(self.source, self.current, self.size)
1923 else {
1924 return false;
1925 };
1926 let bytes = rest.as_bytes();
1927 let mut length = 0;
1928 while length < bytes.len() {
1929 match bytes[length] {
1930 byte if byte.is_ascii_hexdigit() => length += 1,
1931 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_hexdigit) => length += 1,
1932 _ => break,
1933 }
1934 }
1935 self.current += length;
1936 self.column += length;
1937 true
1938 }
1939
1940 #[inline]
1941 fn advance_ascii_identifier(&mut self) -> bool {
1942 let Some(rest) = self
1943 .cursor
1944 .source_range(self.source, self.current, self.size)
1945 else {
1946 return false;
1947 };
1948 let bytes = rest.as_bytes();
1949 let mut length = 0;
1950 while length < bytes.len() {
1951 let byte = bytes[length];
1952 if byte == b'#' && matches!(bytes.get(length + 1), Some(b'>') | Some(b'-')) {
1953 break;
1954 }
1955 if byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$' | b'#' | b'@') {
1956 length += 1;
1957 } else {
1958 break;
1959 }
1960 }
1961 self.current += length;
1962 self.column += length;
1963 true
1964 }
1965
1966 fn try_scan_simple_quoted_content(
1967 &mut self,
1968 quote: char,
1969 backslash_is_escape: bool,
1970 ) -> Option<(usize, usize)> {
1971 let content_start = self.current;
1972 let rest = self
1973 .cursor
1974 .source_range(self.source, content_start, self.size)?;
1975 let quote_offset = rest.find(quote)?;
1976 let content_end = content_start + quote_offset;
1977
1978 if (content_end + 1 < self.size && self.char_at(content_end + 1) == quote)
1979 || (backslash_is_escape && rest[..quote_offset].contains('\\'))
1980 {
1981 return None;
1982 }
1983
1984 self.advance_ascii_to(content_end);
1985 self.advance();
1986 Some((content_start, content_end))
1987 }
1988
1989 fn skip_whitespace(&mut self) {
1990 let mut saw_newline = false;
1995 while !self.is_at_end() {
1996 let c = self.peek();
1997 match c {
1998 ' ' | '\t' | '\r' => {
1999 self.advance();
2000 }
2001 '\n' => {
2002 saw_newline = true;
2003 self.advance();
2004 }
2005 '\u{00A0}' | '\u{2000}'..='\u{200B}' | '\u{3000}' | '\u{FEFF}' => {
2010 self.advance();
2011 }
2012 '-' if self.peek_next() == '-' => {
2013 self.scan_line_comment(saw_newline);
2014 saw_newline = true;
2016 }
2017 '/' if self.peek_next() == '/' && self.config.hash_comments => {
2018 self.scan_double_slash_comment();
2020 }
2021 '/' if self.peek_next() == '*' => {
2022 if self.current + 2 < self.size && self.char_at(self.current + 2) == '+' {
2024 break;
2026 }
2027 if self.scan_block_comment(saw_newline).is_err() {
2028 return;
2029 }
2030 }
2032 '/' if self.peek_next() == '/' && self.config.comments.contains_key("//") => {
2033 let prev_non_ws = if self.current > 0 {
2037 let mut i = self.current - 1;
2038 while i > 0 && (self.char_at(i) == ' ' || self.char_at(i) == '\t') {
2039 i -= 1;
2040 }
2041 self.char_at(i)
2042 } else {
2043 '\0'
2044 };
2045 if prev_non_ws == ':' || prev_non_ws == '/' {
2046 break;
2048 }
2049 self.scan_line_comment(saw_newline);
2050 saw_newline = true;
2052 }
2053 '#' if self.config.hash_comments => {
2054 self.scan_hash_line_comment();
2055 }
2056 _ => break,
2057 }
2058 }
2059 }
2060
2061 fn scan_hash_line_comment(&mut self) {
2062 self.advance(); let start = self.current;
2064 while !self.is_at_end() && self.peek() != '\n' {
2065 self.advance();
2066 }
2067 let comment = self.text_from_range(start, self.current);
2068 let comment_text = comment.trim().to_string();
2069 if let Some(last) = self.tokens.last_mut() {
2070 last.trailing_comments_mut().push(comment_text);
2071 } else {
2072 self.comments.push(comment_text);
2073 }
2074 }
2075
2076 fn scan_double_slash_comment(&mut self) {
2077 self.advance(); self.advance(); let start = self.current;
2080 while !self.is_at_end() && self.peek() != '\n' {
2081 self.advance();
2082 }
2083 let comment = self.text_from_range(start, self.current);
2084 let comment_text = comment.trim().to_string();
2085 if let Some(last) = self.tokens.last_mut() {
2086 last.trailing_comments_mut().push(comment_text);
2087 } else {
2088 self.comments.push(comment_text);
2089 }
2090 }
2091
2092 fn scan_line_comment(&mut self, after_newline: bool) {
2093 self.advance(); self.advance(); let start = self.current;
2096 while !self.is_at_end() && self.peek() != '\n' {
2097 self.advance();
2098 }
2099 let comment_text = self.text_from_range(start, self.current);
2100
2101 if after_newline || self.tokens.is_empty() {
2104 self.comments.push(comment_text);
2105 } else if let Some(last) = self.tokens.last_mut() {
2106 last.trailing_comments_mut().push(comment_text);
2107 }
2108 }
2109
2110 fn scan_block_comment(&mut self, after_newline: bool) -> Result<()> {
2111 self.advance(); self.advance(); let content_start = self.current;
2114 let mut depth = 1;
2115
2116 while !self.is_at_end() && depth > 0 {
2117 if self.peek() == '/' && self.peek_next() == '*' && self.config.nested_comments {
2118 self.advance();
2119 self.advance();
2120 depth += 1;
2121 } else if self.peek() == '*' && self.peek_next() == '/' {
2122 depth -= 1;
2123 if depth > 0 {
2124 self.advance();
2125 self.advance();
2126 }
2127 } else {
2128 self.advance();
2129 }
2130 }
2131
2132 if depth > 0 {
2133 return Err(Error::tokenize(
2134 "Unterminated block comment",
2135 self.line,
2136 self.column,
2137 self.start,
2138 self.current,
2139 ));
2140 }
2141
2142 let content = self.text_from_range(content_start, self.current);
2144 self.advance(); self.advance(); let comment_text = format!("/*{}*/", content);
2149
2150 if after_newline || self.tokens.is_empty() {
2153 self.comments.push(comment_text);
2154 } else if let Some(last) = self.tokens.last_mut() {
2155 last.trailing_comments_mut().push(comment_text);
2156 }
2157
2158 Ok(())
2159 }
2160
2161 fn scan_hint(&mut self) -> Result<()> {
2163 self.advance(); self.advance(); self.advance(); let hint_start = self.current;
2167
2168 while !self.is_at_end() {
2170 if self.peek() == '*' && self.peek_next() == '/' {
2171 break;
2172 }
2173 self.advance();
2174 }
2175
2176 if self.is_at_end() {
2177 return Err(Error::tokenize(
2178 "Unterminated hint comment",
2179 self.line,
2180 self.column,
2181 self.start,
2182 self.current,
2183 ));
2184 }
2185
2186 let hint_text = self.text_from_range(hint_start, self.current);
2187 self.advance(); self.advance(); self.add_token_with_text(TokenType::Hint, hint_text.trim().to_string());
2191
2192 Ok(())
2193 }
2194
2195 fn scan_positional_parameter(&mut self) -> Result<()> {
2197 self.advance(); let start = self.current;
2199
2200 while !self.is_at_end() && self.peek().is_ascii_digit() {
2201 self.advance();
2202 }
2203
2204 let number = self.text_from_range(start, self.current);
2205 self.add_token_with_text(TokenType::Parameter, number);
2206 Ok(())
2207 }
2208
2209 fn try_scan_tagged_dollar_string(&mut self) -> Result<Option<()>> {
2214 let saved_pos = self.current;
2215
2216 self.advance(); let tag_start = self.current;
2222 while !self.is_at_end()
2223 && (self.peek().is_alphanumeric() || self.peek() == '_' || !self.peek().is_ascii())
2224 {
2225 self.advance();
2226 }
2227 let tag = self.text_from_range(tag_start, self.current);
2228
2229 if self.is_at_end() || self.peek() != '$' {
2231 self.current = saved_pos;
2233 return Ok(None);
2234 }
2235 self.advance(); let content_start = self.current;
2239 let closing_tag = format!("${}$", tag);
2240 let closing_chars: Vec<char> = closing_tag.chars().collect();
2241
2242 loop {
2243 if self.is_at_end() {
2244 self.current = saved_pos;
2246 return Ok(None);
2247 }
2248
2249 if self.peek() == '$' && self.current + closing_chars.len() <= self.size {
2251 let matches = closing_chars.iter().enumerate().all(|(j, &ch)| {
2252 self.current + j < self.size && self.char_at(self.current + j) == ch
2253 });
2254 if matches {
2255 let content = self.text_from_range(content_start, self.current);
2256 for _ in 0..closing_chars.len() {
2258 self.advance();
2259 }
2260 let token_text = format!("{}\x00{}", tag, content);
2262 self.add_token_with_text(TokenType::DollarString, token_text);
2263 return Ok(Some(()));
2264 }
2265 }
2266 self.advance();
2267 }
2268 }
2269
2270 fn scan_dollar_quoted_string(&mut self) -> Result<()> {
2275 self.advance(); self.advance(); let start = self.current;
2280 while !self.is_at_end() {
2281 if self.peek() == '$'
2282 && self.current + 1 < self.size
2283 && self.char_at(self.current + 1) == '$'
2284 {
2285 break;
2286 }
2287 self.advance();
2288 }
2289
2290 let content = self.text_from_range(start, self.current);
2291
2292 if !self.is_at_end() {
2293 self.advance(); self.advance(); }
2296
2297 self.add_token_with_text(TokenType::DollarString, content);
2298 Ok(())
2299 }
2300
2301 fn scan_token(&mut self) -> Result<()> {
2302 let c = self.peek();
2303
2304 if c == '\'' {
2306 if self.config.quotes.contains_key("'''")
2308 && self.peek_next() == '\''
2309 && self.current + 2 < self.size
2310 && self.char_at(self.current + 2) == '\''
2311 {
2312 return self.scan_triple_quoted_string('\'');
2313 }
2314 return self.scan_string();
2315 }
2316
2317 if c == '"'
2319 && self.config.quotes.contains_key("\"\"\"")
2320 && self.peek_next() == '"'
2321 && self.current + 2 < self.size
2322 && self.char_at(self.current + 2) == '"'
2323 {
2324 return self.scan_triple_quoted_string('"');
2325 }
2326
2327 if c == '"'
2330 && self.config.quotes.contains_key("\"")
2331 && !self.config.identifiers.contains_key(&'"')
2332 {
2333 return self.scan_double_quoted_string();
2334 }
2335
2336 if let Some(&end_quote) = self.config.identifiers.get(&c) {
2338 return self.scan_quoted_identifier(end_quote);
2339 }
2340
2341 if c.is_ascii_digit() {
2343 return self.scan_number();
2344 }
2345
2346 if c == '.' && self.peek_next().is_ascii_digit() {
2353 let prev_char = if self.current > 0 {
2354 self.char_at(self.current - 1)
2355 } else {
2356 '\0'
2357 };
2358 let is_after_ident = prev_char.is_alphanumeric()
2359 || prev_char == '_'
2360 || prev_char == '`'
2361 || prev_char == '"'
2362 || prev_char == ']'
2363 || prev_char == ')';
2364 if prev_char != '.' && !is_after_ident {
2365 return self.scan_number_starting_with_dot();
2366 }
2367 }
2368
2369 if c == '/'
2371 && self.peek_next() == '*'
2372 && self.current + 2 < self.size
2373 && self.char_at(self.current + 2) == '+'
2374 {
2375 return self.scan_hint();
2376 }
2377
2378 if let Some(token_type) = self.try_scan_multi_char_operator() {
2380 self.add_token(token_type);
2381 return Ok(());
2382 }
2383
2384 if c == '$'
2387 && (self.peek_next().is_alphanumeric()
2388 || self.peek_next() == '_'
2389 || !self.peek_next().is_ascii())
2390 {
2391 if let Some(()) = self.try_scan_tagged_dollar_string()? {
2392 return Ok(());
2393 }
2394 if self.config.dollar_sign_is_identifier {
2397 return self.scan_dollar_identifier();
2398 }
2399 }
2400
2401 if c == '$' && self.peek_next() == '$' {
2403 return self.scan_dollar_quoted_string();
2404 }
2405
2406 if c == '$' && self.peek_next().is_ascii_digit() {
2408 return self.scan_positional_parameter();
2409 }
2410
2411 if c == '$' && self.config.dollar_sign_is_identifier {
2413 return self.scan_dollar_identifier();
2414 }
2415
2416 if (c == '#' || c == '@')
2419 && (self.peek_next().is_alphanumeric()
2420 || self.peek_next() == '_'
2421 || self.peek_next() == '#')
2422 {
2423 return self.scan_tsql_identifier();
2424 }
2425
2426 if let Some(&token_type) = self.config.single_tokens.get(&c) {
2428 self.advance();
2429 self.add_token(token_type);
2430 return Ok(());
2431 }
2432
2433 if c == '\u{2212}' {
2435 self.advance();
2436 self.add_token(TokenType::Dash);
2437 return Ok(());
2438 }
2439
2440 if c == '\u{2044}' {
2442 self.advance();
2443 self.add_token(TokenType::Slash);
2444 return Ok(());
2445 }
2446
2447 if c == '\u{2018}' || c == '\u{2019}' {
2449 return self.scan_unicode_quoted_string(c);
2451 }
2452 if c == '\u{201C}' || c == '\u{201D}' {
2453 return self.scan_unicode_quoted_identifier(c);
2455 }
2456
2457 self.scan_identifier_or_keyword()
2459 }
2460
2461 fn try_scan_multi_char_operator(&mut self) -> Option<TokenType> {
2462 let c = self.peek();
2463 let next = self.peek_next();
2464 let third = if self.current + 2 < self.size {
2465 self.char_at(self.current + 2)
2466 } else {
2467 '\0'
2468 };
2469
2470 if c == '-' && next == '|' && third == '-' {
2473 self.advance();
2474 self.advance();
2475 self.advance();
2476 return Some(TokenType::Adjacent);
2477 }
2478
2479 if c == '|' && next == '|' && third == '/' {
2481 self.advance();
2482 self.advance();
2483 self.advance();
2484 return Some(TokenType::DPipeSlash);
2485 }
2486
2487 if c == '#' && next == '>' && third == '>' {
2489 self.advance();
2490 self.advance();
2491 self.advance();
2492 return Some(TokenType::DHashArrow);
2493 }
2494
2495 if c == '-' && next == '>' && third == '>' {
2497 self.advance();
2498 self.advance();
2499 self.advance();
2500 return Some(TokenType::DArrow);
2501 }
2502
2503 if c == '<' && next == '=' && third == '>' {
2505 self.advance();
2506 self.advance();
2507 self.advance();
2508 return Some(TokenType::NullsafeEq);
2509 }
2510
2511 if c == '<' && next == '-' && third == '>' {
2513 self.advance();
2514 self.advance();
2515 self.advance();
2516 return Some(TokenType::LrArrow);
2517 }
2518
2519 if c == '<' && next == '@' {
2521 self.advance();
2522 self.advance();
2523 return Some(TokenType::LtAt);
2524 }
2525
2526 if c == '@' && next == '>' {
2528 self.advance();
2529 self.advance();
2530 return Some(TokenType::AtGt);
2531 }
2532
2533 if c == '~' && next == '~' && third == '~' {
2535 self.advance();
2536 self.advance();
2537 self.advance();
2538 return Some(TokenType::Glob);
2539 }
2540
2541 if c == '~' && next == '~' && third == '*' {
2543 self.advance();
2544 self.advance();
2545 self.advance();
2546 return Some(TokenType::ILike);
2547 }
2548
2549 let fourth = if self.current + 3 < self.size {
2551 self.char_at(self.current + 3)
2552 } else {
2553 '\0'
2554 };
2555 if c == '!' && next == '~' && third == '~' && fourth == '*' {
2556 self.advance();
2557 self.advance();
2558 self.advance();
2559 self.advance();
2560 return Some(TokenType::NotILike);
2561 }
2562
2563 if c == '!' && next == '~' && third == '~' {
2565 self.advance();
2566 self.advance();
2567 self.advance();
2568 return Some(TokenType::NotLike);
2569 }
2570
2571 if c == '!' && next == '~' && third == '*' {
2573 self.advance();
2574 self.advance();
2575 self.advance();
2576 return Some(TokenType::NotIRLike);
2577 }
2578
2579 if c == '!' && next == ':' && third == '>' {
2581 self.advance();
2582 self.advance();
2583 self.advance();
2584 return Some(TokenType::NColonGt);
2585 }
2586
2587 if c == '?' && next == ':' && third == ':' {
2589 self.advance();
2590 self.advance();
2591 self.advance();
2592 return Some(TokenType::QDColon);
2593 }
2594
2595 if c == '!' && next == '~' {
2597 self.advance();
2598 self.advance();
2599 return Some(TokenType::NotRLike);
2600 }
2601
2602 if c == '~' && next == '~' {
2604 self.advance();
2605 self.advance();
2606 return Some(TokenType::Like);
2607 }
2608
2609 if c == '~' && next == '*' {
2611 self.advance();
2612 self.advance();
2613 return Some(TokenType::IRLike);
2614 }
2615
2616 if c == ':' && next == ':' && third == '$' {
2619 self.advance();
2620 self.advance();
2621 self.advance();
2622 return Some(TokenType::DColonDollar);
2623 }
2624 if c == ':' && next == ':' && third == '%' {
2625 self.advance();
2626 self.advance();
2627 self.advance();
2628 return Some(TokenType::DColonPercent);
2629 }
2630 if c == ':' && next == ':' && third == '?' {
2631 self.advance();
2632 self.advance();
2633 self.advance();
2634 return Some(TokenType::DColonQMark);
2635 }
2636
2637 let token_type = match (c, next) {
2639 ('.', ':') => Some(TokenType::DotColon),
2640 ('=', '=') => Some(TokenType::Eq), ('<', '=') => Some(TokenType::Lte),
2642 ('>', '=') => Some(TokenType::Gte),
2643 ('!', '=') => Some(TokenType::Neq),
2644 ('<', '>') => Some(TokenType::Neq),
2645 ('^', '=') => Some(TokenType::Neq),
2646 ('<', '<') => Some(TokenType::LtLt),
2647 ('>', '>') => Some(TokenType::GtGt),
2648 ('|', '|') => Some(TokenType::DPipe),
2649 ('|', '/') => Some(TokenType::PipeSlash), ('/', '/') if self.config.double_slash_int_div => Some(TokenType::Div), (':', ':') => Some(TokenType::DColon),
2652 (':', '=') => Some(TokenType::ColonEq), (':', '>') => Some(TokenType::ColonGt), ('-', '>') => Some(TokenType::Arrow), ('=', '>') => Some(TokenType::FArrow), ('&', '&') => Some(TokenType::DAmp),
2657 ('&', '<') => Some(TokenType::AmpLt), ('&', '>') => Some(TokenType::AmpGt), ('@', '@') => Some(TokenType::AtAt), ('@', '?') => Some(TokenType::AtQMark), ('?', '|') => Some(TokenType::QMarkPipe), ('?', '&') => Some(TokenType::QMarkAmp), ('?', '?') => Some(TokenType::DQMark), ('#', '>') => Some(TokenType::HashArrow), ('#', '-') => Some(TokenType::HashDash), ('^', '@') => Some(TokenType::CaretAt), ('*', '*') => Some(TokenType::DStar), ('|', '>') => Some(TokenType::PipeGt), _ => None,
2670 };
2671
2672 if token_type.is_some() {
2673 self.advance();
2674 self.advance();
2675 }
2676
2677 token_type
2678 }
2679
2680 fn scan_string(&mut self) -> Result<()> {
2681 self.advance(); if let Some((text_start, text_end)) =
2683 self.try_scan_simple_quoted_content('\'', self.config.string_escapes.contains(&'\\'))
2684 {
2685 self.add_token_from_source(TokenType::String, text_start, text_end);
2686 return Ok(());
2687 }
2688 let mut value = String::new();
2689
2690 while !self.is_at_end() {
2691 let c = self.peek();
2692 if c == '\'' {
2693 if self.peek_next() == '\'' {
2694 value.push('\'');
2696 self.advance();
2697 self.advance();
2698 } else {
2699 break;
2700 }
2701 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2702 if self.config.recover_terminal_backslash_quote
2703 && self.peek_next() == '\''
2704 && !self.range_contains(self.current + 2, '\'')
2705 {
2706 value.push(self.advance());
2707 break;
2708 }
2709
2710 self.scan_backslash_escape(&mut value);
2711 } else {
2712 value.push(self.advance());
2713 }
2714 }
2715
2716 if self.is_at_end() {
2717 if self.config.recover_unterminated_string {
2718 self.add_token_with_text(TokenType::String, value);
2719 return Ok(());
2720 }
2721
2722 return Err(Error::tokenize(
2723 "Unterminated string",
2724 self.line,
2725 self.column,
2726 self.start,
2727 self.current,
2728 ));
2729 }
2730
2731 self.advance(); self.add_token_with_text(TokenType::String, value);
2733 Ok(())
2734 }
2735
2736 fn scan_double_quoted_string(&mut self) -> Result<()> {
2738 self.advance(); let mut value = String::new();
2740
2741 while !self.is_at_end() {
2742 let c = self.peek();
2743 if c == '"' {
2744 if self.peek_next() == '"' {
2745 value.push('"');
2747 self.advance();
2748 self.advance();
2749 } else {
2750 break;
2751 }
2752 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2753 self.scan_backslash_escape(&mut value);
2754 } else {
2755 value.push(self.advance());
2756 }
2757 }
2758
2759 if self.is_at_end() {
2760 return Err(Error::tokenize(
2761 "Unterminated double-quoted string",
2762 self.line,
2763 self.column,
2764 self.start,
2765 self.current,
2766 ));
2767 }
2768
2769 self.advance(); self.add_token_with_text(TokenType::String, value);
2771 Ok(())
2772 }
2773
2774 fn scan_backslash_escape(&mut self, value: &mut String) {
2775 self.advance(); if self.is_at_end() {
2777 value.push('\\');
2778 return;
2779 }
2780
2781 let escaped = self.advance();
2782 let restricted = !self.config.escape_follow_chars.is_empty();
2783 let always_allowed = matches!(escaped, '\\' | '\'' | '"');
2784 if restricted && !always_allowed && !self.config.escape_follow_chars.contains(&escaped) {
2785 value.push(escaped);
2786 return;
2787 }
2788
2789 let supports_octal =
2790 restricted && ('1'..='7').any(|digit| self.config.escape_follow_chars.contains(&digit));
2791 if supports_octal && escaped.is_digit(8) {
2792 if let Some(codepoint) = self.peek_radix_digits(2, 8).and_then(|suffix| {
2793 let first = escaped.to_digit(8)?;
2794 first.checked_mul(64)?.checked_add(suffix)
2795 }) {
2796 if let Ok(byte) = u8::try_from(codepoint) {
2797 self.advance_count(2);
2798 value.push(byte as char);
2799 return;
2800 }
2801 }
2802
2803 if escaped == '0' {
2804 value.push('\0');
2805 } else {
2806 value.push(escaped);
2807 }
2808 return;
2809 }
2810
2811 match escaped {
2812 'n' => value.push('\n'),
2813 'r' => value.push('\r'),
2814 't' => value.push('\t'),
2815 '0' => value.push('\0'),
2816 'Z' => value.push('\x1A'),
2817 'a' => value.push('\x07'),
2818 'b' => value.push('\x08'),
2819 'f' => value.push('\x0C'),
2820 'v' => value.push('\x0B'),
2821 'x' => {
2822 if let Some(codepoint) = self.peek_radix_digits(2, 16) {
2823 self.advance_count(2);
2824 value.push(codepoint as u8 as char);
2825 } else if restricted {
2826 value.push('x');
2828 } else {
2829 value.push('\\');
2832 value.push('x');
2833 for _ in 0..2 {
2834 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2835 value.push(self.advance());
2836 }
2837 }
2838 }
2839 }
2840 'u' if restricted && self.config.escape_follow_chars.contains(&'u') => {
2841 if let Some(codepoint) = self.peek_radix_digits(4, 16).and_then(char::from_u32) {
2842 self.advance_count(4);
2843 value.push(codepoint);
2844 } else {
2845 value.push('u');
2846 }
2847 }
2848 '\\' => value.push('\\'),
2849 '\'' => value.push('\''),
2850 '"' => value.push('"'),
2851 '%' => value.push('%'),
2852 '_' => value.push('_'),
2853 _ if restricted => value.push(escaped),
2854 _ => {
2855 value.push('\\');
2856 value.push(escaped);
2857 }
2858 }
2859 }
2860
2861 fn peek_radix_digits(&self, count: usize, radix: u32) -> Option<u32> {
2862 if self.current + count > self.size {
2863 return None;
2864 }
2865
2866 let mut value = 0_u32;
2867 for offset in 0..count {
2868 value = value
2869 .checked_mul(radix)?
2870 .checked_add(self.char_at(self.current + offset).to_digit(radix)?)?;
2871 }
2872 Some(value)
2873 }
2874
2875 fn advance_count(&mut self, count: usize) {
2876 for _ in 0..count {
2877 self.advance();
2878 }
2879 }
2880
2881 fn scan_triple_quoted_string(&mut self, quote_char: char) -> Result<()> {
2882 self.advance();
2884 self.advance();
2885 self.advance();
2886 let mut value = String::new();
2887
2888 while !self.is_at_end() {
2889 if self.peek() == quote_char
2891 && self.current + 1 < self.size
2892 && self.char_at(self.current + 1) == quote_char
2893 && self.current + 2 < self.size
2894 && self.char_at(self.current + 2) == quote_char
2895 {
2896 break;
2898 }
2899 if self.peek() == '\\' && self.config.string_escapes.contains(&'\\') {
2900 self.scan_backslash_escape(&mut value);
2901 } else {
2902 value.push(self.advance());
2903 }
2904 }
2905
2906 if self.is_at_end() {
2907 return Err(Error::tokenize(
2908 "Unterminated triple-quoted string",
2909 self.line,
2910 self.column,
2911 self.start,
2912 self.current,
2913 ));
2914 }
2915
2916 self.advance();
2918 self.advance();
2919 self.advance();
2920 let token_type = if quote_char == '"' {
2921 TokenType::TripleDoubleQuotedString
2922 } else {
2923 TokenType::TripleSingleQuotedString
2924 };
2925 self.add_token_with_text(token_type, value);
2926 Ok(())
2927 }
2928
2929 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2930 self.advance(); let mut value = String::new();
2932
2933 loop {
2934 if self.is_at_end() {
2935 return Err(Error::tokenize(
2936 "Unterminated identifier",
2937 self.line,
2938 self.column,
2939 self.start,
2940 self.current,
2941 ));
2942 }
2943 if self.config.identifier_backslash_escapes && self.peek() == '\\' {
2944 if self.peek_next() == end_quote {
2945 value.push(end_quote);
2946 self.advance_count(2);
2947 } else {
2948 self.scan_backslash_escape(&mut value);
2949 }
2950 continue;
2951 }
2952 if self.peek() == end_quote {
2953 if self.peek_next() == end_quote {
2954 value.push(end_quote);
2956 self.advance(); self.advance(); } else {
2959 break;
2961 }
2962 } else {
2963 value.push(self.peek());
2964 self.advance();
2965 }
2966 }
2967
2968 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2970 Ok(())
2971 }
2972
2973 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2978 self.advance(); let start = self.current;
2980 let close_quote = if open_quote == '\u{2018}' {
2982 '\u{2019}' } else {
2984 '\u{2019}' };
2986 while !self.is_at_end() && self.peek() != close_quote {
2987 self.advance();
2988 }
2989 let value = self.text_from_range(start, self.current);
2990 if !self.is_at_end() {
2991 self.advance(); }
2993 self.add_token_with_text(TokenType::String, value);
2994 Ok(())
2995 }
2996
2997 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
3000 self.advance(); let start = self.current;
3002 let close_quote = if open_quote == '\u{201C}' {
3003 '\u{201D}' } else {
3005 '\u{201D}' };
3007 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
3008 self.advance();
3009 }
3010 let value = self.text_from_range(start, self.current);
3011 if !self.is_at_end() {
3012 self.advance(); }
3014 self.add_token_with_text(TokenType::QuotedIdentifier, value);
3015 Ok(())
3016 }
3017
3018 fn starts_numeric_exponent(&self) -> bool {
3019 if !matches!(self.peek(), 'e' | 'E') {
3020 return false;
3021 }
3022 let digits = self.current + 1 + usize::from(matches!(self.peek_next(), '+' | '-'));
3023 digits < self.size && self.char_at(digits).is_ascii_digit()
3024 }
3025
3026 fn scan_number(&mut self) -> Result<()> {
3027 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
3029 let next = if self.current + 1 < self.size {
3030 self.char_at(self.current + 1)
3031 } else {
3032 '\0'
3033 };
3034 if next == 'x' || next == 'X' {
3035 self.advance();
3037 self.advance();
3038 let hex_start = self.current;
3040 if !self.advance_ascii_hex_digits() {
3041 while !self.is_at_end()
3042 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3043 {
3044 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3045 break;
3046 }
3047 self.advance();
3048 }
3049 }
3050 let has_hex_digits = self.current > hex_start;
3051 let next_is_identifier_part = {
3052 let next = self.peek();
3053 next.is_alphanumeric() || matches!(next, '_' | '$' | '#' | '@')
3054 };
3055 let is_empty_hex_string = self.config.allow_empty_hex_string
3056 && !self.config.hex_string_is_integer_type
3057 && !next_is_identifier_part;
3058 if has_hex_digits || is_empty_hex_string {
3059 let mut is_hex_float = false;
3061 if has_hex_digits && !self.is_at_end() && self.peek() == '.' {
3063 let after_dot = if self.current + 1 < self.size {
3064 self.char_at(self.current + 1)
3065 } else {
3066 '\0'
3067 };
3068 if after_dot.is_ascii_hexdigit() {
3069 is_hex_float = true;
3070 self.advance(); if !self.advance_ascii_hex_digits() {
3072 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3073 self.advance();
3074 }
3075 }
3076 }
3077 }
3078 if has_hex_digits
3080 && !self.is_at_end()
3081 && (self.peek() == 'p' || self.peek() == 'P')
3082 {
3083 is_hex_float = true;
3084 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3086 self.advance();
3087 }
3088 if !self.advance_ascii_digits() {
3089 while !self.is_at_end() && self.peek().is_ascii_digit() {
3090 self.advance();
3091 }
3092 }
3093 }
3094 if is_hex_float {
3095 let raw_text = self.text_from_range(self.start, self.current);
3097 let full_text = if self.config.numbers_can_be_underscore_separated
3098 && raw_text.contains('_')
3099 {
3100 raw_text.replace('_', "")
3101 } else {
3102 raw_text
3103 };
3104 self.add_token_with_text(TokenType::Number, full_text);
3105 } else if self.config.hex_string_is_integer_type {
3106 let raw_value = self.text_from_range(hex_start, self.current);
3108 let hex_value = if self.config.numbers_can_be_underscore_separated
3109 && raw_value.contains('_')
3110 {
3111 raw_value.replace('_', "")
3112 } else {
3113 raw_value
3114 };
3115 self.add_token_with_text(TokenType::HexNumber, hex_value);
3116 } else {
3117 let raw_value = self.text_from_range(hex_start, self.current);
3119 let hex_value = if self.config.numbers_can_be_underscore_separated
3120 && raw_value.contains('_')
3121 {
3122 raw_value.replace('_', "")
3123 } else {
3124 raw_value
3125 };
3126 self.add_token_with_text(TokenType::HexString, hex_value);
3127 }
3128 return Ok(());
3129 }
3130 self.current = self.start + 1;
3133 }
3134 }
3135
3136 if !self.advance_ascii_digits() {
3138 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3139 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3141 break;
3142 }
3143 self.advance();
3144 }
3145 }
3146
3147 if self.peek() == '.' {
3151 let next = self.peek_next();
3152 if next != '.' {
3158 self.advance(); if !self.advance_ascii_digits() {
3161 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3162 {
3163 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3164 break;
3165 }
3166 self.advance();
3167 }
3168 }
3169 }
3170 }
3171
3172 if self.starts_numeric_exponent() {
3174 self.advance();
3175 if self.peek() == '+' || self.peek() == '-' {
3176 self.advance();
3177 }
3178 if !self.advance_ascii_digits() {
3179 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3180 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3181 break;
3182 }
3183 self.advance();
3184 }
3185 }
3186 }
3187
3188 let source_text = self
3189 .cursor
3190 .source_range(self.source, self.start, self.current);
3191 let raw_owned = source_text
3192 .is_none()
3193 .then(|| self.text_from_range(self.start, self.current));
3194 let raw_text = source_text.unwrap_or_else(|| {
3195 raw_owned
3196 .as_deref()
3197 .expect("non-ASCII numbers own their text")
3198 });
3199 let normalized = (self.config.numbers_can_be_underscore_separated
3202 && raw_text.contains('_'))
3203 .then(|| raw_text.replace('_', ""));
3204 let text = normalized.as_deref().unwrap_or(raw_text);
3205
3206 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3208 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3209 let suffix_match = if self.current + 1 < self.size {
3211 let two_char: String = [
3212 self.char_at(self.current).to_ascii_uppercase(),
3213 self.char_at(self.current + 1).to_ascii_uppercase(),
3214 ]
3215 .iter()
3216 .collect();
3217 if self.config.numeric_literals.contains_key(&two_char) {
3218 let after_suffix = if self.current + 2 < self.size {
3220 self.char_at(self.current + 2)
3221 } else {
3222 ' '
3223 };
3224 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3225 Some((two_char, 2))
3226 } else {
3227 None
3228 }
3229 } else if self.config.numeric_literals.contains_key(&next_char) {
3230 let after_suffix = if self.current + 1 < self.size {
3232 self.char_at(self.current + 1)
3233 } else {
3234 ' '
3235 };
3236 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3237 Some((next_char, 1))
3238 } else {
3239 None
3240 }
3241 } else {
3242 None
3243 }
3244 } else if self.config.numeric_literals.contains_key(&next_char) {
3245 Some((next_char, 1))
3247 } else {
3248 None
3249 };
3250
3251 if let Some((suffix, len)) = suffix_match {
3252 for _ in 0..len {
3254 self.advance();
3255 }
3256 let type_name = self
3259 .config
3260 .numeric_literals
3261 .get(&suffix)
3262 .expect("suffix verified by contains_key above")
3263 .clone();
3264 let combined = format!("{}::{}", text, type_name);
3265 self.add_token_with_text(TokenType::Number, combined);
3266 return Ok(());
3267 }
3268 }
3269
3270 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3273 let next = self.peek();
3274 if next.is_alphabetic() || next == '_' {
3275 if !self.advance_ascii_identifier() {
3277 while !self.is_at_end() {
3278 let ch = self.peek();
3279 if ch.is_alphanumeric() || ch == '_' {
3280 self.advance();
3281 } else {
3282 break;
3283 }
3284 }
3285 }
3286 self.add_token(TokenType::Identifier);
3287 return Ok(());
3288 }
3289 }
3290
3291 if let Some(text) = normalized.or(raw_owned) {
3292 self.add_token_with_text(TokenType::Number, text);
3293 } else {
3294 self.add_token(TokenType::Number);
3295 }
3296 Ok(())
3297 }
3298
3299 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3301 self.advance();
3303
3304 if !self.advance_ascii_digits() {
3306 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3307 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3308 break;
3309 }
3310 self.advance();
3311 }
3312 }
3313
3314 if self.starts_numeric_exponent() {
3316 self.advance();
3317 if self.peek() == '+' || self.peek() == '-' {
3318 self.advance();
3319 }
3320 if !self.advance_ascii_digits() {
3321 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3322 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3323 break;
3324 }
3325 self.advance();
3326 }
3327 }
3328 }
3329
3330 let source_text = self
3331 .cursor
3332 .source_range(self.source, self.start, self.current);
3333 let raw_owned = source_text
3334 .is_none()
3335 .then(|| self.text_from_range(self.start, self.current));
3336 let raw_text = source_text.unwrap_or_else(|| {
3337 raw_owned
3338 .as_deref()
3339 .expect("non-ASCII numbers own their text")
3340 });
3341 let normalized = (self.config.numbers_can_be_underscore_separated
3344 && raw_text.contains('_'))
3345 .then(|| raw_text.replace('_', ""));
3346 if let Some(text) = normalized.or(raw_owned) {
3347 self.add_token_with_text(TokenType::Number, text);
3348 } else {
3349 self.add_token(TokenType::Number);
3350 }
3351 Ok(())
3352 }
3353
3354 #[inline]
3357 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3358 if text.len() > 128 {
3359 return TokenType::Var;
3360 }
3361 let mut buf = [0u8; 128];
3362 for (i, b) in text.bytes().enumerate() {
3363 buf[i] = b.to_ascii_uppercase();
3364 }
3365 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3366 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3367 } else {
3368 TokenType::Var
3369 }
3370 }
3371
3372 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3373 let first_char = self.peek();
3375 if !first_char.is_alphanumeric() && first_char != '_' {
3376 let c = self.advance();
3378 return Err(Error::tokenize(
3379 format!("Unexpected character: '{}'", c),
3380 self.line,
3381 self.column,
3382 self.start,
3383 self.current,
3384 ));
3385 }
3386
3387 if !self.advance_ascii_identifier() {
3388 while !self.is_at_end() {
3389 let c = self.peek();
3390 if c == '#' {
3394 let next_c = if self.current + 1 < self.size {
3395 self.char_at(self.current + 1)
3396 } else {
3397 '\0'
3398 };
3399 if next_c == '>' || next_c == '-' {
3400 break; }
3402 self.advance();
3403 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3404 self.advance();
3405 } else {
3406 break;
3407 }
3408 }
3409 }
3410
3411 let source_text = self
3412 .cursor
3413 .source_range(self.source, self.start, self.current);
3414 let owned_text = source_text
3415 .is_none()
3416 .then(|| self.text_from_range(self.start, self.current));
3417 let text = source_text.unwrap_or_else(|| {
3418 owned_text
3419 .as_deref()
3420 .expect("non-ASCII identifiers own their text")
3421 });
3422
3423 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3425 self.advance(); self.add_token(TokenType::Neq);
3427 return Ok(());
3428 }
3429
3430 let next_char = self.peek();
3433 let is_single_quote = next_char == '\'';
3434 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3435 let is_double_quote_for_raw = next_char == '"';
3438
3439 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3442 let quote_char = if is_single_quote { '\'' } else { '"' };
3445 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3449 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3453 self.add_token_with_text(TokenType::RawString, string_value);
3454 } else {
3455 let string_value = self.scan_raw_string_content(quote_char)?;
3456 self.add_token_with_text(TokenType::RawString, string_value);
3457 }
3458 return Ok(());
3459 }
3460
3461 if is_single_quote || is_double_quote {
3462 if text.eq_ignore_ascii_case("N") {
3463 self.advance(); let string_value = if is_single_quote {
3466 self.scan_string_content()?
3467 } else {
3468 self.scan_double_quoted_string_content()?
3469 };
3470 self.add_token_with_text(TokenType::NationalString, string_value);
3471 return Ok(());
3472 } else if text.eq_ignore_ascii_case("E") {
3473 let lowercase = text == "e";
3477 let prefix = if lowercase { "e:" } else { "E:" };
3478 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3480 self.add_token_with_text(
3481 TokenType::EscapeString,
3482 format!("{}{}", prefix, string_value),
3483 );
3484 return Ok(());
3485 } else if text.eq_ignore_ascii_case("X") {
3486 self.advance(); let string_value = if is_single_quote {
3489 self.scan_string_content()?
3490 } else {
3491 self.scan_double_quoted_string_content()?
3492 };
3493 self.add_token_with_text(TokenType::HexString, string_value);
3494 return Ok(());
3495 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3496 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3499 self.add_token_with_text(TokenType::ByteString, string_value);
3500 return Ok(());
3501 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3502 self.advance(); let string_value = self.scan_string_content()?;
3506 if self.config.b_prefix_is_byte_string {
3507 self.add_token_with_text(TokenType::ByteString, string_value);
3508 } else {
3509 self.add_token_with_text(TokenType::BitString, string_value);
3510 }
3511 return Ok(());
3512 }
3513 }
3514
3515 if text.eq_ignore_ascii_case("U")
3517 && self.peek() == '&'
3518 && self.current + 1 < self.size
3519 && self.char_at(self.current + 1) == '\''
3520 {
3521 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3524 self.add_token_with_text(TokenType::UnicodeString, string_value);
3525 return Ok(());
3526 }
3527
3528 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3529
3530 if let Some(text) = owned_text {
3531 self.add_token_with_text(token_type, text);
3532 } else {
3533 self.add_token_from_source(token_type, self.start, self.current);
3534 }
3535 Ok(())
3536 }
3537
3538 fn scan_string_content_with_escapes(
3542 &mut self,
3543 force_backslash_escapes: bool,
3544 ) -> Result<String> {
3545 let use_backslash_escapes =
3546 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3547 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3548 {
3549 return Ok(self.text_from_range(start, end));
3550 }
3551 let mut value = String::new();
3552
3553 while !self.is_at_end() {
3554 let c = self.peek();
3555 if c == '\'' {
3556 if self.peek_next() == '\'' {
3557 value.push('\'');
3559 self.advance();
3560 self.advance();
3561 } else {
3562 break;
3563 }
3564 } else if c == '\\' && use_backslash_escapes {
3565 value.push(self.advance());
3567 if !self.is_at_end() {
3568 value.push(self.advance());
3569 }
3570 } else {
3571 value.push(self.advance());
3572 }
3573 }
3574
3575 if self.is_at_end() {
3576 return Err(Error::tokenize(
3577 "Unterminated string",
3578 self.line,
3579 self.column,
3580 self.start,
3581 self.current,
3582 ));
3583 }
3584
3585 self.advance(); Ok(value)
3587 }
3588
3589 fn scan_string_content(&mut self) -> Result<String> {
3591 self.scan_string_content_with_escapes(false)
3592 }
3593
3594 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3597 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3598 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3599 {
3600 return Ok(self.text_from_range(start, end));
3601 }
3602 let mut value = String::new();
3603
3604 while !self.is_at_end() {
3605 let c = self.peek();
3606 if c == '"' {
3607 if self.peek_next() == '"' {
3608 value.push('"');
3610 self.advance();
3611 self.advance();
3612 } else {
3613 break;
3614 }
3615 } else if c == '\\' && use_backslash_escapes {
3616 self.advance(); if !self.is_at_end() {
3619 let escaped = self.advance();
3620 match escaped {
3621 'n' => value.push('\n'),
3622 'r' => value.push('\r'),
3623 't' => value.push('\t'),
3624 '0' => value.push('\0'),
3625 '\\' => value.push('\\'),
3626 '"' => value.push('"'),
3627 '\'' => value.push('\''),
3628 'x' => {
3629 let mut hex = String::new();
3631 for _ in 0..2 {
3632 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3633 hex.push(self.advance());
3634 }
3635 }
3636 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3637 value.push(byte as char);
3638 } else {
3639 value.push('\\');
3641 value.push('x');
3642 value.push_str(&hex);
3643 }
3644 }
3645 _ => {
3646 value.push('\\');
3648 value.push(escaped);
3649 }
3650 }
3651 }
3652 } else {
3653 value.push(self.advance());
3654 }
3655 }
3656
3657 if self.is_at_end() {
3658 return Err(Error::tokenize(
3659 "Unterminated double-quoted string",
3660 self.line,
3661 self.column,
3662 self.start,
3663 self.current,
3664 ));
3665 }
3666
3667 self.advance(); Ok(value)
3669 }
3670
3671 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3676 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3677 quote_char,
3678 self.config.string_escapes_allowed_in_raw_strings,
3679 ) {
3680 return Ok(self.text_from_range(start, end));
3681 }
3682 let mut value = String::new();
3683
3684 while !self.is_at_end() {
3685 let c = self.peek();
3686 if c == quote_char {
3687 if self.peek_next() == quote_char {
3688 value.push(quote_char);
3690 self.advance();
3691 self.advance();
3692 } else {
3693 break;
3694 }
3695 } else if c == '\\'
3696 && self.peek_next() == quote_char
3697 && self.config.string_escapes_allowed_in_raw_strings
3698 {
3699 value.push('\\');
3702 value.push(quote_char);
3703 self.advance(); self.advance(); } else {
3706 value.push(self.advance());
3708 }
3709 }
3710
3711 if self.is_at_end() {
3712 return Err(Error::tokenize(
3713 "Unterminated raw string",
3714 self.line,
3715 self.column,
3716 self.start,
3717 self.current,
3718 ));
3719 }
3720
3721 self.advance(); Ok(value)
3723 }
3724
3725 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3728 let mut value = String::new();
3729
3730 while !self.is_at_end() {
3731 if self.peek() == quote_char {
3732 let mut quote_count = 0;
3733 while self.current + quote_count < self.size
3734 && self.char_at(self.current + quote_count) == quote_char
3735 {
3736 quote_count += 1;
3737 }
3738 if quote_count >= 3 {
3739 for _ in 0..quote_count - 3 {
3742 value.push(quote_char);
3743 }
3744 for _ in 0..quote_count {
3745 self.advance();
3746 }
3747 return Ok(value);
3748 }
3749 }
3750 let ch = self.advance();
3752 value.push(ch);
3753 }
3754
3755 Err(Error::tokenize(
3756 "Unterminated raw triple-quoted string",
3757 self.line,
3758 self.column,
3759 self.start,
3760 self.current,
3761 ))
3762 }
3763
3764 fn scan_dollar_identifier(&mut self) -> Result<()> {
3769 self.advance();
3771
3772 while !self.is_at_end() {
3774 let c = self.peek();
3775 if c.is_alphanumeric() || c == '_' || c == '$' {
3776 self.advance();
3777 } else {
3778 break;
3779 }
3780 }
3781
3782 self.add_token(TokenType::Var);
3783 Ok(())
3784 }
3785
3786 fn scan_tsql_identifier(&mut self) -> Result<()> {
3787 let first = self.advance();
3789
3790 if first == '#' && self.peek() == '#' {
3792 self.advance();
3793 }
3794
3795 if !self.advance_ascii_identifier() {
3797 while !self.is_at_end() {
3798 let c = self.peek();
3799 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3800 self.advance();
3801 } else {
3802 break;
3803 }
3804 }
3805 }
3806
3807 self.add_token(TokenType::Var);
3809 Ok(())
3810 }
3811
3812 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3816 let len = self.tokens.len();
3817 if len < 3 {
3818 return None;
3819 }
3820
3821 let last = &self.tokens[len - 1];
3823 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3824 return None;
3825 }
3826 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3827 return None;
3828 }
3829
3830 let format_tok = &self.tokens[len - 2];
3832 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3833 return None;
3834 }
3835
3836 let has_insert = self.tokens[..len - 2]
3838 .iter()
3839 .rev()
3840 .take(20)
3841 .any(|t| t.token_type() == TokenType::Insert);
3842 if !has_insert {
3843 return None;
3844 }
3845
3846 let raw_start = self.current;
3850 while !self.is_at_end() {
3851 let c = self.peek();
3852 if c == '\n' {
3853 let saved = self.current;
3855 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3858 self.advance();
3859 }
3860 if self.is_at_end() || self.peek() == '\n' {
3861 let raw = self.text_from_range(raw_start, saved);
3864 return Some(raw.trim().to_string());
3865 }
3866 } else {
3868 self.advance();
3869 }
3870 }
3871
3872 let raw = self.text_from_range(raw_start, self.current);
3874 let trimmed = raw.trim().to_string();
3875 if trimmed.is_empty() {
3876 None
3877 } else {
3878 Some(trimmed)
3879 }
3880 }
3881
3882 fn add_token(&mut self, token_type: TokenType) {
3883 self.add_token_from_source(token_type, self.start, self.current);
3884 }
3885
3886 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3887 let span = Span::new(self.start, self.current, self.line, self.column);
3888 if let Some(stats) = &mut self.guard_stats {
3889 stats.observe(token_type, span);
3890 }
3891 let mut token = if self
3892 .cursor
3893 .source_range(self.source, text_start, text_end)
3894 .is_some()
3895 {
3896 T::from_source(
3897 token_type,
3898 self.source,
3899 text_start,
3900 text_end,
3901 span,
3902 self.shared_source.as_ref(),
3903 )
3904 } else {
3905 T::from_owned(
3906 token_type,
3907 self.cursor
3908 .text_from_range(self.source, text_start, text_end),
3909 span,
3910 )
3911 };
3912 token.comments_mut().append(&mut self.comments);
3913 self.tokens.push(token);
3914 }
3915
3916 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3917 let span = Span::new(self.start, self.current, self.line, self.column);
3918 if let Some(stats) = &mut self.guard_stats {
3919 stats.observe(token_type, span);
3920 }
3921 let mut token = T::from_owned(token_type, text, span);
3922 token.comments_mut().append(&mut self.comments);
3923 self.tokens.push(token);
3924 }
3925}
3926
3927#[cfg(test)]
3928mod tests {
3929 use super::*;
3930
3931 #[test]
3932 fn ascii_fast_path_matches_character_buffer_path() {
3933 let tokenizer = Tokenizer::default();
3934 let inputs = [
3935 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3936 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3937 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3938 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3939 ];
3940
3941 for sql in inputs {
3942 assert_eq!(
3943 tokenizer.tokenize(sql).unwrap(),
3944 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3945 "tokenization differs for {sql}"
3946 );
3947 }
3948 }
3949
3950 #[test]
3951 fn parser_tokens_match_public_tokens() {
3952 let tokenizer = Tokenizer::default();
3953 let inputs = [
3954 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3955 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3956 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3957 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3958 ];
3959
3960 for sql in inputs {
3961 let public = tokenizer.tokenize(sql).unwrap();
3962 let source: Arc<str> = Arc::from(sql);
3963 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3964 let materialized = parser
3965 .iter()
3966 .map(|token| Token {
3967 token_type: token.token_type,
3968 text: token.text_owned(),
3969 span: token.span,
3970 comments: token.comments.clone(),
3971 trailing_comments: token.trailing_comments.clone(),
3972 })
3973 .collect::<Vec<_>>();
3974
3975 assert_eq!(
3976 materialized, public,
3977 "parser tokenization differs for {sql}"
3978 );
3979 assert_eq!(stats.token_count, public.len());
3980 }
3981 }
3982
3983 #[test]
3984 fn parser_tokens_borrow_unchanged_ascii_text() {
3985 let tokenizer = Tokenizer::default();
3986 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3987 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3988
3989 assert!(tokens
3990 .iter()
3991 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3992 }
3993
3994 #[test]
3995 fn test_simple_select() {
3996 let tokenizer = Tokenizer::default();
3997 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3998
3999 assert_eq!(tokens.len(), 2);
4000 assert_eq!(tokens[0].token_type, TokenType::Select);
4001 assert_eq!(tokens[1].token_type, TokenType::Number);
4002 assert_eq!(tokens[1].text, "1");
4003 }
4004
4005 #[test]
4006 fn test_select_with_identifier() {
4007 let tokenizer = Tokenizer::default();
4008 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
4009
4010 assert_eq!(tokens.len(), 6);
4011 assert_eq!(tokens[0].token_type, TokenType::Select);
4012 assert_eq!(tokens[1].token_type, TokenType::Var);
4013 assert_eq!(tokens[1].text, "a");
4014 assert_eq!(tokens[2].token_type, TokenType::Comma);
4015 assert_eq!(tokens[3].token_type, TokenType::Var);
4016 assert_eq!(tokens[3].text, "b");
4017 assert_eq!(tokens[4].token_type, TokenType::From);
4018 assert_eq!(tokens[5].token_type, TokenType::Var);
4019 assert_eq!(tokens[5].text, "t");
4020 }
4021
4022 #[test]
4023 fn test_string_literal() {
4024 let tokenizer = Tokenizer::default();
4025 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
4026
4027 assert_eq!(tokens.len(), 2);
4028 assert_eq!(tokens[1].token_type, TokenType::String);
4029 assert_eq!(tokens[1].text, "hello");
4030 }
4031
4032 #[test]
4033 fn test_escaped_string() {
4034 let tokenizer = Tokenizer::default();
4035 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
4036
4037 assert_eq!(tokens.len(), 2);
4038 assert_eq!(tokens[1].token_type, TokenType::String);
4039 assert_eq!(tokens[1].text, "it's");
4040 }
4041
4042 #[test]
4043 fn test_escape_follow_chars_gate_builtin_decoding() {
4044 let mut config = TokenizerConfig::default();
4045 config.string_escapes.push('\\');
4046 config.escape_follow_chars = vec!['n'];
4047 let tokenizer = Tokenizer::new(config);
4048 let tokens = tokenizer.tokenize(r"SELECT '\n\a\f\Z\x21'").unwrap();
4049
4050 assert_eq!(tokens[1].text, "\nafZx21");
4051 }
4052
4053 #[test]
4054 fn test_configured_numeric_escapes_require_complete_sequences() {
4055 let mut config = TokenizerConfig::default();
4056 config.string_escapes.push('\\');
4057 config.escape_follow_chars = vec!['0', '1', '2', '3', '4', '5', '6', '7', 'x', 'u'];
4058 let tokenizer = Tokenizer::new(config);
4059 let tokens = tokenizer
4060 .tokenize(r"SELECT '\041\x21\u26c4-\777\x2\u26c'")
4061 .unwrap();
4062
4063 assert_eq!(tokens[1].text, "!!\u{26c4}-777x2u26c");
4064 }
4065
4066 #[test]
4067 fn test_terminal_backslash_quote_recovery() {
4068 let mut config = TokenizerConfig::default();
4069 config.string_escapes.push('\\');
4070 config.recover_terminal_backslash_quote = true;
4071 let tokenizer = Tokenizer::new(config);
4072 let tokens = tokenizer
4073 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4074 .unwrap();
4075
4076 assert_eq!(tokens.len(), 8);
4077 assert_eq!(tokens[3].token_type, TokenType::String);
4078 assert_eq!(tokens[3].text, "a\\");
4079 assert_eq!(tokens[4].token_type, TokenType::Or);
4080 }
4081
4082 #[test]
4083 fn test_comments() {
4084 let tokenizer = Tokenizer::default();
4085 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4086
4087 assert_eq!(tokens.len(), 2);
4088 assert_eq!(tokens[0].trailing_comments.len(), 1);
4091 assert_eq!(tokens[0].trailing_comments[0], " comment");
4092 }
4093
4094 #[test]
4095 fn test_comment_in_and_chain() {
4096 use crate::generator::Generator;
4097 use crate::parser::Parser;
4098
4099 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4101 let ast = Parser::parse_sql(sql).unwrap();
4102 let mut gen = Generator::default();
4103 let output = gen.generate(&ast[0]).unwrap();
4104 assert_eq!(
4105 output,
4106 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4107 );
4108 }
4109
4110 #[test]
4111 fn test_operators() {
4112 let tokenizer = Tokenizer::default();
4113 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4114
4115 assert_eq!(tokens.len(), 5);
4116 assert_eq!(tokens[0].token_type, TokenType::Number);
4117 assert_eq!(tokens[1].token_type, TokenType::Plus);
4118 assert_eq!(tokens[2].token_type, TokenType::Number);
4119 assert_eq!(tokens[3].token_type, TokenType::Star);
4120 assert_eq!(tokens[4].token_type, TokenType::Number);
4121 }
4122
4123 #[test]
4124 fn test_comparison_operators() {
4125 let tokenizer = Tokenizer::default();
4126 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4127
4128 assert_eq!(tokens[1].token_type, TokenType::Lte);
4129 assert_eq!(tokens[3].token_type, TokenType::Gte);
4130 assert_eq!(tokens[5].token_type, TokenType::Neq);
4131 }
4132
4133 #[test]
4134 fn test_national_string() {
4135 let tokenizer = Tokenizer::default();
4136 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4137
4138 assert_eq!(
4139 tokens.len(),
4140 1,
4141 "Expected 1 token for N'abc', got {:?}",
4142 tokens
4143 );
4144 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4145 assert_eq!(tokens[0].text, "abc");
4146 }
4147
4148 #[test]
4149 fn test_hex_string() {
4150 let tokenizer = Tokenizer::default();
4151 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4152
4153 assert_eq!(
4154 tokens.len(),
4155 1,
4156 "Expected 1 token for X'ABCD', got {:?}",
4157 tokens
4158 );
4159 assert_eq!(tokens[0].token_type, TokenType::HexString);
4160 assert_eq!(tokens[0].text, "ABCD");
4161 }
4162
4163 #[test]
4164 fn test_bit_string() {
4165 let tokenizer = Tokenizer::default();
4166 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4167
4168 assert_eq!(
4169 tokens.len(),
4170 1,
4171 "Expected 1 token for B'01010', got {:?}",
4172 tokens
4173 );
4174 assert_eq!(tokens[0].token_type, TokenType::BitString);
4175 assert_eq!(tokens[0].text, "01010");
4176 }
4177
4178 #[test]
4179 fn test_trailing_dot_number() {
4180 let tokenizer = Tokenizer::default();
4181
4182 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4184 assert_eq!(
4185 tokens.len(),
4186 2,
4187 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4188 tokens
4189 );
4190 assert_eq!(tokens[1].token_type, TokenType::Number);
4191 assert_eq!(tokens[1].text, "1.");
4192
4193 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4195 assert_eq!(tokens[1].text, "1.5");
4196
4197 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4200 assert_eq!(
4201 tokens.len(),
4202 3,
4203 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4204 tokens
4205 );
4206 assert_eq!(tokens[1].token_type, TokenType::Number);
4207 assert_eq!(tokens[1].text, "1.");
4208 assert_eq!(tokens[2].token_type, TokenType::Var);
4209
4210 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4212 assert_eq!(tokens[1].token_type, TokenType::Number);
4213 assert_eq!(tokens[1].text, "1");
4214 assert_eq!(tokens[2].token_type, TokenType::Dot);
4215 assert_eq!(tokens[3].token_type, TokenType::Dot);
4216 assert_eq!(tokens[4].token_type, TokenType::Number);
4217 assert_eq!(tokens[4].text, "2");
4218 }
4219
4220 #[test]
4221 fn test_leading_dot_number() {
4222 let tokenizer = Tokenizer::default();
4223
4224 let tokens = tokenizer.tokenize(".25").unwrap();
4226 assert_eq!(
4227 tokens.len(),
4228 1,
4229 "Expected 1 token for '.25', got {:?}",
4230 tokens
4231 );
4232 assert_eq!(tokens[0].token_type, TokenType::Number);
4233 assert_eq!(tokens[0].text, ".25");
4234
4235 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4237 assert_eq!(
4238 tokens.len(),
4239 4,
4240 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4241 tokens
4242 );
4243 assert_eq!(tokens[0].token_type, TokenType::Sample);
4244 assert_eq!(tokens[1].token_type, TokenType::LParen);
4245 assert_eq!(tokens[2].token_type, TokenType::Number);
4246 assert_eq!(tokens[2].text, ".25");
4247 assert_eq!(tokens[3].token_type, TokenType::RParen);
4248
4249 let tokens = tokenizer.tokenize(".5e10").unwrap();
4251 assert_eq!(
4252 tokens.len(),
4253 1,
4254 "Expected 1 token for '.5e10', got {:?}",
4255 tokens
4256 );
4257 assert_eq!(tokens[0].token_type, TokenType::Number);
4258 assert_eq!(tokens[0].text, ".5e10");
4259
4260 let tokens = tokenizer.tokenize("a.b").unwrap();
4262 assert_eq!(
4263 tokens.len(),
4264 3,
4265 "Expected 3 tokens for 'a.b', got {:?}",
4266 tokens
4267 );
4268 assert_eq!(tokens[1].token_type, TokenType::Dot);
4269 }
4270
4271 #[test]
4272 fn test_unrecognized_character() {
4273 let tokenizer = Tokenizer::default();
4274
4275 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4277 assert!(
4278 result.is_ok(),
4279 "Curly quotes should be tokenized as strings"
4280 );
4281
4282 let result = tokenizer.tokenize("SELECT • FROM t");
4284 assert!(result.is_err());
4285 }
4286
4287 #[test]
4288 fn test_colon_eq_tokenization() {
4289 let tokenizer = Tokenizer::default();
4290
4291 let tokens = tokenizer.tokenize("a := 1").unwrap();
4293 assert_eq!(tokens.len(), 3);
4294 assert_eq!(tokens[0].token_type, TokenType::Var);
4295 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4296 assert_eq!(tokens[2].token_type, TokenType::Number);
4297
4298 let tokens = tokenizer.tokenize("a:b").unwrap();
4300 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4301 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4302
4303 let tokens = tokenizer.tokenize("a::INT").unwrap();
4305 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4306 }
4307
4308 #[test]
4309 fn test_colon_eq_parsing() {
4310 use crate::generator::Generator;
4311 use crate::parser::Parser;
4312
4313 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4315 .expect("Failed to parse MySQL @var := expr");
4316 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4317 assert_eq!(output, "SELECT @var1 := 1, @var2");
4318
4319 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4321 .expect("Failed to parse MySQL @var2 := @var1");
4322 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4323 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4324
4325 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4327 .expect("Failed to parse MySQL @var := COUNT(*)");
4328 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4329 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4330
4331 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4333 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4334 assert_eq!(output, "SET @var1 = 1");
4335
4336 let ast =
4338 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4339 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4340 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4341
4342 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4344 .expect("Failed to parse UNNEST with :=");
4345 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4346 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4347
4348 let ast =
4350 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4351 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4352 assert_eq!(output, "SELECT 1 AS foo");
4353
4354 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4356 .expect("Failed to parse DuckDB multiple prefix aliases");
4357 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4358 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4359 }
4360
4361 #[test]
4362 fn test_colon_eq_dialect_roundtrip() {
4363 use crate::dialects::{Dialect, DialectType};
4364
4365 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4366 let d = Dialect::get(dialect);
4367 let ast = d
4368 .parse(sql)
4369 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4370 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4371 let transformed = d
4372 .transform(ast[0].clone())
4373 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4374 let output = d
4375 .generate(&transformed)
4376 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4377 let expected = expected.unwrap_or(sql);
4378 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4379 }
4380
4381 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4383 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4384 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4385 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4386
4387 check(
4389 DialectType::DuckDB,
4390 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4391 None,
4392 );
4393 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4394
4395 {
4398 let d = Dialect::get(DialectType::DuckDB);
4399 let ast = d
4400 .parse("STRUCT_PACK(a := 'b')::json")
4401 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4402 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4403 }
4404
4405 check(
4407 DialectType::DuckDB,
4408 "SELECT foo: 1",
4409 Some("SELECT 1 AS foo"),
4410 );
4411 check(
4412 DialectType::DuckDB,
4413 "SELECT foo: 1, bar: 2, baz: 3",
4414 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4415 );
4416 }
4417
4418 #[test]
4419 fn test_comment_roundtrip() {
4420 use crate::generator::Generator;
4421 use crate::parser::Parser;
4422
4423 fn check_roundtrip(sql: &str) -> Option<String> {
4424 let ast = match Parser::parse_sql(sql) {
4425 Ok(a) => a,
4426 Err(e) => return Some(format!("Parse error: {:?}", e)),
4427 };
4428 if ast.is_empty() {
4429 return Some("Empty AST".to_string());
4430 }
4431 let mut generator = Generator::default();
4432 let output = match generator.generate(&ast[0]) {
4433 Ok(o) => o,
4434 Err(e) => return Some(format!("Gen error: {:?}", e)),
4435 };
4436 if output == sql {
4437 None
4438 } else {
4439 Some(format!(
4440 "Mismatch:\n input: {}\n output: {}",
4441 sql, output
4442 ))
4443 }
4444 }
4445
4446 let tests = vec![
4447 "SELECT c /* c1 */ AS alias /* c2 */",
4453 "SELECT a /* x */, b /* x */",
4455 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4457 "SELECT * FROM foo /* x */, bla /* x */",
4459 "SELECT 1 /* comment */ + 1",
4461 "SELECT 1 /* c1 */ + 2 /* c2 */",
4462 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4463 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4465 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4467 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4469 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4471 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4473 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4474 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4475 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4476 "/* comment */ CREATE TABLE foo AS SELECT 1",
4477 "INSERT INTO foo SELECT * FROM bar /* comment */",
4479 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4481 ];
4482
4483 let mut failures = Vec::new();
4484 for sql in tests {
4485 if let Some(e) = check_roundtrip(sql) {
4486 failures.push(e);
4487 }
4488 }
4489
4490 if !failures.is_empty() {
4491 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4492 }
4493 }
4494
4495 #[test]
4496 fn test_dollar_quoted_string_parsing() {
4497 use crate::dialects::{Dialect, DialectType};
4498
4499 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4501 assert_eq!(tag, Some("FOO".to_string()));
4502 assert_eq!(content, "content here");
4503
4504 let (tag, content) = super::parse_dollar_string_token("just content");
4505 assert_eq!(tag, None);
4506 assert_eq!(content, "just content");
4507
4508 fn check_databricks(sql: &str, expected: Option<&str>) {
4510 let d = Dialect::get(DialectType::Databricks);
4511 let ast = d
4512 .parse(sql)
4513 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4514 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4515 let transformed = d
4516 .transform(ast[0].clone())
4517 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4518 let output = d
4519 .generate(&transformed)
4520 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4521 let expected = expected.unwrap_or(sql);
4522 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4523 }
4524
4525 check_databricks(
4527 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4528 None
4529 );
4530
4531 check_databricks(
4533 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4534 None
4535 );
4536 }
4537
4538 #[test]
4539 fn test_numeric_underscore_stripping() {
4540 let mut config = TokenizerConfig::default();
4542 config.numbers_can_be_underscore_separated = true;
4543 let tokenizer = Tokenizer::new(config);
4544
4545 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4547 assert_eq!(tokens[1].token_type, TokenType::Number);
4548 assert_eq!(tokens[1].text, "12345");
4549
4550 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4552 assert_eq!(tokens[1].token_type, TokenType::Number);
4553 assert_eq!(tokens[1].text, "20000");
4554
4555 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4557 assert_eq!(tokens[1].token_type, TokenType::Number);
4558 assert_eq!(tokens[1].text, "12E+10");
4559
4560 let default_tokenizer = Tokenizer::default();
4562 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4563 assert_eq!(tokens[1].token_type, TokenType::Number);
4564 assert_eq!(tokens[1].text, "1_2_3_4_5");
4565 }
4566}