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