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 if self.peek() == '\\' && self.config.string_escapes.contains(&'\\') {
2877 self.scan_backslash_escape(&mut value);
2878 } else {
2879 value.push(self.advance());
2880 }
2881 }
2882
2883 if self.is_at_end() {
2884 return Err(Error::tokenize(
2885 "Unterminated triple-quoted string",
2886 self.line,
2887 self.column,
2888 self.start,
2889 self.current,
2890 ));
2891 }
2892
2893 self.advance();
2895 self.advance();
2896 self.advance();
2897 let token_type = if quote_char == '"' {
2898 TokenType::TripleDoubleQuotedString
2899 } else {
2900 TokenType::TripleSingleQuotedString
2901 };
2902 self.add_token_with_text(token_type, value);
2903 Ok(())
2904 }
2905
2906 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2907 self.advance(); let mut value = String::new();
2909
2910 loop {
2911 if self.is_at_end() {
2912 return Err(Error::tokenize(
2913 "Unterminated identifier",
2914 self.line,
2915 self.column,
2916 self.start,
2917 self.current,
2918 ));
2919 }
2920 if end_quote == '`' && self.peek() == '\\' && self.peek_next() == end_quote {
2921 value.push(end_quote);
2923 self.advance(); self.advance(); continue;
2926 }
2927 if self.peek() == end_quote {
2928 if self.peek_next() == end_quote {
2929 value.push(end_quote);
2931 self.advance(); self.advance(); } else {
2934 break;
2936 }
2937 } else {
2938 value.push(self.peek());
2939 self.advance();
2940 }
2941 }
2942
2943 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2945 Ok(())
2946 }
2947
2948 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2953 self.advance(); let start = self.current;
2955 let close_quote = if open_quote == '\u{2018}' {
2957 '\u{2019}' } else {
2959 '\u{2019}' };
2961 while !self.is_at_end() && self.peek() != close_quote {
2962 self.advance();
2963 }
2964 let value = self.text_from_range(start, self.current);
2965 if !self.is_at_end() {
2966 self.advance(); }
2968 self.add_token_with_text(TokenType::String, value);
2969 Ok(())
2970 }
2971
2972 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
2975 self.advance(); let start = self.current;
2977 let close_quote = if open_quote == '\u{201C}' {
2978 '\u{201D}' } else {
2980 '\u{201D}' };
2982 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
2983 self.advance();
2984 }
2985 let value = self.text_from_range(start, self.current);
2986 if !self.is_at_end() {
2987 self.advance(); }
2989 self.add_token_with_text(TokenType::QuotedIdentifier, value);
2990 Ok(())
2991 }
2992
2993 fn scan_number(&mut self) -> Result<()> {
2994 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
2996 let next = if self.current + 1 < self.size {
2997 self.char_at(self.current + 1)
2998 } else {
2999 '\0'
3000 };
3001 if next == 'x' || next == 'X' {
3002 self.advance();
3004 self.advance();
3005 let hex_start = self.current;
3007 if !self.advance_ascii_hex_digits() {
3008 while !self.is_at_end()
3009 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3010 {
3011 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3012 break;
3013 }
3014 self.advance();
3015 }
3016 }
3017 if self.current > hex_start {
3018 let mut is_hex_float = false;
3020 if !self.is_at_end() && self.peek() == '.' {
3022 let after_dot = if self.current + 1 < self.size {
3023 self.char_at(self.current + 1)
3024 } else {
3025 '\0'
3026 };
3027 if after_dot.is_ascii_hexdigit() {
3028 is_hex_float = true;
3029 self.advance(); if !self.advance_ascii_hex_digits() {
3031 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3032 self.advance();
3033 }
3034 }
3035 }
3036 }
3037 if !self.is_at_end() && (self.peek() == 'p' || self.peek() == 'P') {
3039 is_hex_float = true;
3040 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3042 self.advance();
3043 }
3044 if !self.advance_ascii_digits() {
3045 while !self.is_at_end() && self.peek().is_ascii_digit() {
3046 self.advance();
3047 }
3048 }
3049 }
3050 if is_hex_float {
3051 let raw_text = self.text_from_range(self.start, self.current);
3053 let full_text = if self.config.numbers_can_be_underscore_separated
3054 && raw_text.contains('_')
3055 {
3056 raw_text.replace('_', "")
3057 } else {
3058 raw_text
3059 };
3060 self.add_token_with_text(TokenType::Number, full_text);
3061 } else if self.config.hex_string_is_integer_type {
3062 let raw_value = self.text_from_range(hex_start, self.current);
3064 let hex_value = if self.config.numbers_can_be_underscore_separated
3065 && raw_value.contains('_')
3066 {
3067 raw_value.replace('_', "")
3068 } else {
3069 raw_value
3070 };
3071 self.add_token_with_text(TokenType::HexNumber, hex_value);
3072 } else {
3073 let raw_value = self.text_from_range(hex_start, self.current);
3075 let hex_value = if self.config.numbers_can_be_underscore_separated
3076 && raw_value.contains('_')
3077 {
3078 raw_value.replace('_', "")
3079 } else {
3080 raw_value
3081 };
3082 self.add_token_with_text(TokenType::HexString, hex_value);
3083 }
3084 return Ok(());
3085 }
3086 self.current = self.start + 1;
3089 }
3090 }
3091
3092 if !self.advance_ascii_digits() {
3094 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3095 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3097 break;
3098 }
3099 self.advance();
3100 }
3101 }
3102
3103 if self.peek() == '.' {
3107 let next = self.peek_next();
3108 if next != '.' {
3114 self.advance(); if !self.advance_ascii_digits() {
3117 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3118 {
3119 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3120 break;
3121 }
3122 self.advance();
3123 }
3124 }
3125 }
3126 }
3127
3128 if self.peek() == 'e' || self.peek() == 'E' {
3130 self.advance();
3131 if self.peek() == '+' || self.peek() == '-' {
3132 self.advance();
3133 }
3134 if !self.advance_ascii_digits() {
3135 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3136 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3137 break;
3138 }
3139 self.advance();
3140 }
3141 }
3142 }
3143
3144 let source_text = self
3145 .cursor
3146 .source_range(self.source, self.start, self.current);
3147 let raw_owned = source_text
3148 .is_none()
3149 .then(|| self.text_from_range(self.start, self.current));
3150 let raw_text = source_text.unwrap_or_else(|| {
3151 raw_owned
3152 .as_deref()
3153 .expect("non-ASCII numbers own their text")
3154 });
3155 let normalized = (self.config.numbers_can_be_underscore_separated
3158 && raw_text.contains('_'))
3159 .then(|| raw_text.replace('_', ""));
3160 let text = normalized.as_deref().unwrap_or(raw_text);
3161
3162 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3164 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3165 let suffix_match = if self.current + 1 < self.size {
3167 let two_char: String = [
3168 self.char_at(self.current).to_ascii_uppercase(),
3169 self.char_at(self.current + 1).to_ascii_uppercase(),
3170 ]
3171 .iter()
3172 .collect();
3173 if self.config.numeric_literals.contains_key(&two_char) {
3174 let after_suffix = if self.current + 2 < self.size {
3176 self.char_at(self.current + 2)
3177 } else {
3178 ' '
3179 };
3180 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3181 Some((two_char, 2))
3182 } else {
3183 None
3184 }
3185 } else if self.config.numeric_literals.contains_key(&next_char) {
3186 let after_suffix = if self.current + 1 < self.size {
3188 self.char_at(self.current + 1)
3189 } else {
3190 ' '
3191 };
3192 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3193 Some((next_char, 1))
3194 } else {
3195 None
3196 }
3197 } else {
3198 None
3199 }
3200 } else if self.config.numeric_literals.contains_key(&next_char) {
3201 Some((next_char, 1))
3203 } else {
3204 None
3205 };
3206
3207 if let Some((suffix, len)) = suffix_match {
3208 for _ in 0..len {
3210 self.advance();
3211 }
3212 let type_name = self
3215 .config
3216 .numeric_literals
3217 .get(&suffix)
3218 .expect("suffix verified by contains_key above")
3219 .clone();
3220 let combined = format!("{}::{}", text, type_name);
3221 self.add_token_with_text(TokenType::Number, combined);
3222 return Ok(());
3223 }
3224 }
3225
3226 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3229 let next = self.peek();
3230 if next.is_alphabetic() || next == '_' {
3231 if !self.advance_ascii_identifier() {
3233 while !self.is_at_end() {
3234 let ch = self.peek();
3235 if ch.is_alphanumeric() || ch == '_' {
3236 self.advance();
3237 } else {
3238 break;
3239 }
3240 }
3241 }
3242 self.add_token(TokenType::Identifier);
3243 return Ok(());
3244 }
3245 }
3246
3247 if let Some(text) = normalized.or(raw_owned) {
3248 self.add_token_with_text(TokenType::Number, text);
3249 } else {
3250 self.add_token(TokenType::Number);
3251 }
3252 Ok(())
3253 }
3254
3255 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3257 self.advance();
3259
3260 if !self.advance_ascii_digits() {
3262 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3263 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3264 break;
3265 }
3266 self.advance();
3267 }
3268 }
3269
3270 if self.peek() == 'e' || self.peek() == 'E' {
3272 self.advance();
3273 if self.peek() == '+' || self.peek() == '-' {
3274 self.advance();
3275 }
3276 if !self.advance_ascii_digits() {
3277 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3278 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3279 break;
3280 }
3281 self.advance();
3282 }
3283 }
3284 }
3285
3286 let source_text = self
3287 .cursor
3288 .source_range(self.source, self.start, self.current);
3289 let raw_owned = source_text
3290 .is_none()
3291 .then(|| self.text_from_range(self.start, self.current));
3292 let raw_text = source_text.unwrap_or_else(|| {
3293 raw_owned
3294 .as_deref()
3295 .expect("non-ASCII numbers own their text")
3296 });
3297 let normalized = (self.config.numbers_can_be_underscore_separated
3300 && raw_text.contains('_'))
3301 .then(|| raw_text.replace('_', ""));
3302 if let Some(text) = normalized.or(raw_owned) {
3303 self.add_token_with_text(TokenType::Number, text);
3304 } else {
3305 self.add_token(TokenType::Number);
3306 }
3307 Ok(())
3308 }
3309
3310 #[inline]
3313 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3314 if text.len() > 128 {
3315 return TokenType::Var;
3316 }
3317 let mut buf = [0u8; 128];
3318 for (i, b) in text.bytes().enumerate() {
3319 buf[i] = b.to_ascii_uppercase();
3320 }
3321 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3322 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3323 } else {
3324 TokenType::Var
3325 }
3326 }
3327
3328 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3329 let first_char = self.peek();
3331 if !first_char.is_alphanumeric() && first_char != '_' {
3332 let c = self.advance();
3334 return Err(Error::tokenize(
3335 format!("Unexpected character: '{}'", c),
3336 self.line,
3337 self.column,
3338 self.start,
3339 self.current,
3340 ));
3341 }
3342
3343 if !self.advance_ascii_identifier() {
3344 while !self.is_at_end() {
3345 let c = self.peek();
3346 if c == '#' {
3350 let next_c = if self.current + 1 < self.size {
3351 self.char_at(self.current + 1)
3352 } else {
3353 '\0'
3354 };
3355 if next_c == '>' || next_c == '-' {
3356 break; }
3358 self.advance();
3359 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3360 self.advance();
3361 } else {
3362 break;
3363 }
3364 }
3365 }
3366
3367 let source_text = self
3368 .cursor
3369 .source_range(self.source, self.start, self.current);
3370 let owned_text = source_text
3371 .is_none()
3372 .then(|| self.text_from_range(self.start, self.current));
3373 let text = source_text.unwrap_or_else(|| {
3374 owned_text
3375 .as_deref()
3376 .expect("non-ASCII identifiers own their text")
3377 });
3378
3379 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3381 self.advance(); self.add_token(TokenType::Neq);
3383 return Ok(());
3384 }
3385
3386 let next_char = self.peek();
3389 let is_single_quote = next_char == '\'';
3390 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3391 let is_double_quote_for_raw = next_char == '"';
3394
3395 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3398 let quote_char = if is_single_quote { '\'' } else { '"' };
3401 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3405 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3409 self.add_token_with_text(TokenType::RawString, string_value);
3410 } else {
3411 let string_value = self.scan_raw_string_content(quote_char)?;
3412 self.add_token_with_text(TokenType::RawString, string_value);
3413 }
3414 return Ok(());
3415 }
3416
3417 if is_single_quote || is_double_quote {
3418 if text.eq_ignore_ascii_case("N") {
3419 self.advance(); let string_value = if is_single_quote {
3422 self.scan_string_content()?
3423 } else {
3424 self.scan_double_quoted_string_content()?
3425 };
3426 self.add_token_with_text(TokenType::NationalString, string_value);
3427 return Ok(());
3428 } else if text.eq_ignore_ascii_case("E") {
3429 let lowercase = text == "e";
3433 let prefix = if lowercase { "e:" } else { "E:" };
3434 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3436 self.add_token_with_text(
3437 TokenType::EscapeString,
3438 format!("{}{}", prefix, string_value),
3439 );
3440 return Ok(());
3441 } else if text.eq_ignore_ascii_case("X") {
3442 self.advance(); let string_value = if is_single_quote {
3445 self.scan_string_content()?
3446 } else {
3447 self.scan_double_quoted_string_content()?
3448 };
3449 self.add_token_with_text(TokenType::HexString, string_value);
3450 return Ok(());
3451 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3452 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3455 self.add_token_with_text(TokenType::ByteString, string_value);
3456 return Ok(());
3457 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3458 self.advance(); let string_value = self.scan_string_content()?;
3462 if self.config.b_prefix_is_byte_string {
3463 self.add_token_with_text(TokenType::ByteString, string_value);
3464 } else {
3465 self.add_token_with_text(TokenType::BitString, string_value);
3466 }
3467 return Ok(());
3468 }
3469 }
3470
3471 if text.eq_ignore_ascii_case("U")
3473 && self.peek() == '&'
3474 && self.current + 1 < self.size
3475 && self.char_at(self.current + 1) == '\''
3476 {
3477 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3480 self.add_token_with_text(TokenType::UnicodeString, string_value);
3481 return Ok(());
3482 }
3483
3484 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3485
3486 if let Some(text) = owned_text {
3487 self.add_token_with_text(token_type, text);
3488 } else {
3489 self.add_token_from_source(token_type, self.start, self.current);
3490 }
3491 Ok(())
3492 }
3493
3494 fn scan_string_content_with_escapes(
3498 &mut self,
3499 force_backslash_escapes: bool,
3500 ) -> Result<String> {
3501 let use_backslash_escapes =
3502 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3503 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3504 {
3505 return Ok(self.text_from_range(start, end));
3506 }
3507 let mut value = String::new();
3508
3509 while !self.is_at_end() {
3510 let c = self.peek();
3511 if c == '\'' {
3512 if self.peek_next() == '\'' {
3513 value.push('\'');
3515 self.advance();
3516 self.advance();
3517 } else {
3518 break;
3519 }
3520 } else if c == '\\' && use_backslash_escapes {
3521 value.push(self.advance());
3523 if !self.is_at_end() {
3524 value.push(self.advance());
3525 }
3526 } else {
3527 value.push(self.advance());
3528 }
3529 }
3530
3531 if self.is_at_end() {
3532 return Err(Error::tokenize(
3533 "Unterminated string",
3534 self.line,
3535 self.column,
3536 self.start,
3537 self.current,
3538 ));
3539 }
3540
3541 self.advance(); Ok(value)
3543 }
3544
3545 fn scan_string_content(&mut self) -> Result<String> {
3547 self.scan_string_content_with_escapes(false)
3548 }
3549
3550 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3553 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3554 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3555 {
3556 return Ok(self.text_from_range(start, end));
3557 }
3558 let mut value = String::new();
3559
3560 while !self.is_at_end() {
3561 let c = self.peek();
3562 if c == '"' {
3563 if self.peek_next() == '"' {
3564 value.push('"');
3566 self.advance();
3567 self.advance();
3568 } else {
3569 break;
3570 }
3571 } else if c == '\\' && use_backslash_escapes {
3572 self.advance(); if !self.is_at_end() {
3575 let escaped = self.advance();
3576 match escaped {
3577 'n' => value.push('\n'),
3578 'r' => value.push('\r'),
3579 't' => value.push('\t'),
3580 '0' => value.push('\0'),
3581 '\\' => value.push('\\'),
3582 '"' => value.push('"'),
3583 '\'' => value.push('\''),
3584 'x' => {
3585 let mut hex = String::new();
3587 for _ in 0..2 {
3588 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3589 hex.push(self.advance());
3590 }
3591 }
3592 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3593 value.push(byte as char);
3594 } else {
3595 value.push('\\');
3597 value.push('x');
3598 value.push_str(&hex);
3599 }
3600 }
3601 _ => {
3602 value.push('\\');
3604 value.push(escaped);
3605 }
3606 }
3607 }
3608 } else {
3609 value.push(self.advance());
3610 }
3611 }
3612
3613 if self.is_at_end() {
3614 return Err(Error::tokenize(
3615 "Unterminated double-quoted string",
3616 self.line,
3617 self.column,
3618 self.start,
3619 self.current,
3620 ));
3621 }
3622
3623 self.advance(); Ok(value)
3625 }
3626
3627 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3632 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3633 quote_char,
3634 self.config.string_escapes_allowed_in_raw_strings,
3635 ) {
3636 return Ok(self.text_from_range(start, end));
3637 }
3638 let mut value = String::new();
3639
3640 while !self.is_at_end() {
3641 let c = self.peek();
3642 if c == quote_char {
3643 if self.peek_next() == quote_char {
3644 value.push(quote_char);
3646 self.advance();
3647 self.advance();
3648 } else {
3649 break;
3650 }
3651 } else if c == '\\'
3652 && self.peek_next() == quote_char
3653 && self.config.string_escapes_allowed_in_raw_strings
3654 {
3655 value.push('\\');
3658 value.push(quote_char);
3659 self.advance(); self.advance(); } else {
3662 value.push(self.advance());
3664 }
3665 }
3666
3667 if self.is_at_end() {
3668 return Err(Error::tokenize(
3669 "Unterminated raw string",
3670 self.line,
3671 self.column,
3672 self.start,
3673 self.current,
3674 ));
3675 }
3676
3677 self.advance(); Ok(value)
3679 }
3680
3681 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3684 let mut value = String::new();
3685
3686 while !self.is_at_end() {
3687 if self.peek() == quote_char {
3688 let mut quote_count = 0;
3689 while self.current + quote_count < self.size
3690 && self.char_at(self.current + quote_count) == quote_char
3691 {
3692 quote_count += 1;
3693 }
3694 if quote_count >= 3 {
3695 for _ in 0..quote_count - 3 {
3698 value.push(quote_char);
3699 }
3700 for _ in 0..quote_count {
3701 self.advance();
3702 }
3703 return Ok(value);
3704 }
3705 }
3706 let ch = self.advance();
3708 value.push(ch);
3709 }
3710
3711 Err(Error::tokenize(
3712 "Unterminated raw triple-quoted string",
3713 self.line,
3714 self.column,
3715 self.start,
3716 self.current,
3717 ))
3718 }
3719
3720 fn scan_dollar_identifier(&mut self) -> Result<()> {
3725 self.advance();
3727
3728 while !self.is_at_end() {
3730 let c = self.peek();
3731 if c.is_alphanumeric() || c == '_' || c == '$' {
3732 self.advance();
3733 } else {
3734 break;
3735 }
3736 }
3737
3738 self.add_token(TokenType::Var);
3739 Ok(())
3740 }
3741
3742 fn scan_tsql_identifier(&mut self) -> Result<()> {
3743 let first = self.advance();
3745
3746 if first == '#' && self.peek() == '#' {
3748 self.advance();
3749 }
3750
3751 if !self.advance_ascii_identifier() {
3753 while !self.is_at_end() {
3754 let c = self.peek();
3755 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3756 self.advance();
3757 } else {
3758 break;
3759 }
3760 }
3761 }
3762
3763 self.add_token(TokenType::Var);
3765 Ok(())
3766 }
3767
3768 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3772 let len = self.tokens.len();
3773 if len < 3 {
3774 return None;
3775 }
3776
3777 let last = &self.tokens[len - 1];
3779 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3780 return None;
3781 }
3782 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3783 return None;
3784 }
3785
3786 let format_tok = &self.tokens[len - 2];
3788 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3789 return None;
3790 }
3791
3792 let has_insert = self.tokens[..len - 2]
3794 .iter()
3795 .rev()
3796 .take(20)
3797 .any(|t| t.token_type() == TokenType::Insert);
3798 if !has_insert {
3799 return None;
3800 }
3801
3802 let raw_start = self.current;
3806 while !self.is_at_end() {
3807 let c = self.peek();
3808 if c == '\n' {
3809 let saved = self.current;
3811 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3814 self.advance();
3815 }
3816 if self.is_at_end() || self.peek() == '\n' {
3817 let raw = self.text_from_range(raw_start, saved);
3820 return Some(raw.trim().to_string());
3821 }
3822 } else {
3824 self.advance();
3825 }
3826 }
3827
3828 let raw = self.text_from_range(raw_start, self.current);
3830 let trimmed = raw.trim().to_string();
3831 if trimmed.is_empty() {
3832 None
3833 } else {
3834 Some(trimmed)
3835 }
3836 }
3837
3838 fn add_token(&mut self, token_type: TokenType) {
3839 self.add_token_from_source(token_type, self.start, self.current);
3840 }
3841
3842 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3843 let span = Span::new(self.start, self.current, self.line, self.column);
3844 if let Some(stats) = &mut self.guard_stats {
3845 stats.observe(token_type, span);
3846 }
3847 let mut token = if self
3848 .cursor
3849 .source_range(self.source, text_start, text_end)
3850 .is_some()
3851 {
3852 T::from_source(
3853 token_type,
3854 self.source,
3855 text_start,
3856 text_end,
3857 span,
3858 self.shared_source.as_ref(),
3859 )
3860 } else {
3861 T::from_owned(
3862 token_type,
3863 self.cursor
3864 .text_from_range(self.source, text_start, text_end),
3865 span,
3866 )
3867 };
3868 token.comments_mut().append(&mut self.comments);
3869 self.tokens.push(token);
3870 }
3871
3872 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3873 let span = Span::new(self.start, self.current, self.line, self.column);
3874 if let Some(stats) = &mut self.guard_stats {
3875 stats.observe(token_type, span);
3876 }
3877 let mut token = T::from_owned(token_type, text, span);
3878 token.comments_mut().append(&mut self.comments);
3879 self.tokens.push(token);
3880 }
3881}
3882
3883#[cfg(test)]
3884mod tests {
3885 use super::*;
3886
3887 #[test]
3888 fn ascii_fast_path_matches_character_buffer_path() {
3889 let tokenizer = Tokenizer::default();
3890 let inputs = [
3891 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3892 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3893 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3894 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3895 ];
3896
3897 for sql in inputs {
3898 assert_eq!(
3899 tokenizer.tokenize(sql).unwrap(),
3900 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3901 "tokenization differs for {sql}"
3902 );
3903 }
3904 }
3905
3906 #[test]
3907 fn parser_tokens_match_public_tokens() {
3908 let tokenizer = Tokenizer::default();
3909 let inputs = [
3910 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3911 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3912 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3913 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3914 ];
3915
3916 for sql in inputs {
3917 let public = tokenizer.tokenize(sql).unwrap();
3918 let source: Arc<str> = Arc::from(sql);
3919 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3920 let materialized = parser
3921 .iter()
3922 .map(|token| Token {
3923 token_type: token.token_type,
3924 text: token.text_owned(),
3925 span: token.span,
3926 comments: token.comments.clone(),
3927 trailing_comments: token.trailing_comments.clone(),
3928 })
3929 .collect::<Vec<_>>();
3930
3931 assert_eq!(
3932 materialized, public,
3933 "parser tokenization differs for {sql}"
3934 );
3935 assert_eq!(stats.token_count, public.len());
3936 }
3937 }
3938
3939 #[test]
3940 fn parser_tokens_borrow_unchanged_ascii_text() {
3941 let tokenizer = Tokenizer::default();
3942 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3943 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3944
3945 assert!(tokens
3946 .iter()
3947 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3948 }
3949
3950 #[test]
3951 fn test_simple_select() {
3952 let tokenizer = Tokenizer::default();
3953 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3954
3955 assert_eq!(tokens.len(), 2);
3956 assert_eq!(tokens[0].token_type, TokenType::Select);
3957 assert_eq!(tokens[1].token_type, TokenType::Number);
3958 assert_eq!(tokens[1].text, "1");
3959 }
3960
3961 #[test]
3962 fn test_select_with_identifier() {
3963 let tokenizer = Tokenizer::default();
3964 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
3965
3966 assert_eq!(tokens.len(), 6);
3967 assert_eq!(tokens[0].token_type, TokenType::Select);
3968 assert_eq!(tokens[1].token_type, TokenType::Var);
3969 assert_eq!(tokens[1].text, "a");
3970 assert_eq!(tokens[2].token_type, TokenType::Comma);
3971 assert_eq!(tokens[3].token_type, TokenType::Var);
3972 assert_eq!(tokens[3].text, "b");
3973 assert_eq!(tokens[4].token_type, TokenType::From);
3974 assert_eq!(tokens[5].token_type, TokenType::Var);
3975 assert_eq!(tokens[5].text, "t");
3976 }
3977
3978 #[test]
3979 fn test_string_literal() {
3980 let tokenizer = Tokenizer::default();
3981 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
3982
3983 assert_eq!(tokens.len(), 2);
3984 assert_eq!(tokens[1].token_type, TokenType::String);
3985 assert_eq!(tokens[1].text, "hello");
3986 }
3987
3988 #[test]
3989 fn test_escaped_string() {
3990 let tokenizer = Tokenizer::default();
3991 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
3992
3993 assert_eq!(tokens.len(), 2);
3994 assert_eq!(tokens[1].token_type, TokenType::String);
3995 assert_eq!(tokens[1].text, "it's");
3996 }
3997
3998 #[test]
3999 fn test_escape_follow_chars_gate_builtin_decoding() {
4000 let mut config = TokenizerConfig::default();
4001 config.string_escapes.push('\\');
4002 config.escape_follow_chars = vec!['n'];
4003 let tokenizer = Tokenizer::new(config);
4004 let tokens = tokenizer.tokenize(r"SELECT '\n\a\f\Z\x21'").unwrap();
4005
4006 assert_eq!(tokens[1].text, "\nafZx21");
4007 }
4008
4009 #[test]
4010 fn test_configured_numeric_escapes_require_complete_sequences() {
4011 let mut config = TokenizerConfig::default();
4012 config.string_escapes.push('\\');
4013 config.escape_follow_chars = vec!['0', '1', '2', '3', '4', '5', '6', '7', 'x', 'u'];
4014 let tokenizer = Tokenizer::new(config);
4015 let tokens = tokenizer
4016 .tokenize(r"SELECT '\041\x21\u26c4-\777\x2\u26c'")
4017 .unwrap();
4018
4019 assert_eq!(tokens[1].text, "!!\u{26c4}-777x2u26c");
4020 }
4021
4022 #[test]
4023 fn test_terminal_backslash_quote_recovery() {
4024 let mut config = TokenizerConfig::default();
4025 config.string_escapes.push('\\');
4026 config.recover_terminal_backslash_quote = true;
4027 let tokenizer = Tokenizer::new(config);
4028 let tokens = tokenizer
4029 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4030 .unwrap();
4031
4032 assert_eq!(tokens.len(), 8);
4033 assert_eq!(tokens[3].token_type, TokenType::String);
4034 assert_eq!(tokens[3].text, "a\\");
4035 assert_eq!(tokens[4].token_type, TokenType::Or);
4036 }
4037
4038 #[test]
4039 fn test_comments() {
4040 let tokenizer = Tokenizer::default();
4041 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4042
4043 assert_eq!(tokens.len(), 2);
4044 assert_eq!(tokens[0].trailing_comments.len(), 1);
4047 assert_eq!(tokens[0].trailing_comments[0], " comment");
4048 }
4049
4050 #[test]
4051 fn test_comment_in_and_chain() {
4052 use crate::generator::Generator;
4053 use crate::parser::Parser;
4054
4055 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4057 let ast = Parser::parse_sql(sql).unwrap();
4058 let mut gen = Generator::default();
4059 let output = gen.generate(&ast[0]).unwrap();
4060 assert_eq!(
4061 output,
4062 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4063 );
4064 }
4065
4066 #[test]
4067 fn test_operators() {
4068 let tokenizer = Tokenizer::default();
4069 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4070
4071 assert_eq!(tokens.len(), 5);
4072 assert_eq!(tokens[0].token_type, TokenType::Number);
4073 assert_eq!(tokens[1].token_type, TokenType::Plus);
4074 assert_eq!(tokens[2].token_type, TokenType::Number);
4075 assert_eq!(tokens[3].token_type, TokenType::Star);
4076 assert_eq!(tokens[4].token_type, TokenType::Number);
4077 }
4078
4079 #[test]
4080 fn test_comparison_operators() {
4081 let tokenizer = Tokenizer::default();
4082 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4083
4084 assert_eq!(tokens[1].token_type, TokenType::Lte);
4085 assert_eq!(tokens[3].token_type, TokenType::Gte);
4086 assert_eq!(tokens[5].token_type, TokenType::Neq);
4087 }
4088
4089 #[test]
4090 fn test_national_string() {
4091 let tokenizer = Tokenizer::default();
4092 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4093
4094 assert_eq!(
4095 tokens.len(),
4096 1,
4097 "Expected 1 token for N'abc', got {:?}",
4098 tokens
4099 );
4100 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4101 assert_eq!(tokens[0].text, "abc");
4102 }
4103
4104 #[test]
4105 fn test_hex_string() {
4106 let tokenizer = Tokenizer::default();
4107 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4108
4109 assert_eq!(
4110 tokens.len(),
4111 1,
4112 "Expected 1 token for X'ABCD', got {:?}",
4113 tokens
4114 );
4115 assert_eq!(tokens[0].token_type, TokenType::HexString);
4116 assert_eq!(tokens[0].text, "ABCD");
4117 }
4118
4119 #[test]
4120 fn test_bit_string() {
4121 let tokenizer = Tokenizer::default();
4122 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4123
4124 assert_eq!(
4125 tokens.len(),
4126 1,
4127 "Expected 1 token for B'01010', got {:?}",
4128 tokens
4129 );
4130 assert_eq!(tokens[0].token_type, TokenType::BitString);
4131 assert_eq!(tokens[0].text, "01010");
4132 }
4133
4134 #[test]
4135 fn test_trailing_dot_number() {
4136 let tokenizer = Tokenizer::default();
4137
4138 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4140 assert_eq!(
4141 tokens.len(),
4142 2,
4143 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4144 tokens
4145 );
4146 assert_eq!(tokens[1].token_type, TokenType::Number);
4147 assert_eq!(tokens[1].text, "1.");
4148
4149 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4151 assert_eq!(tokens[1].text, "1.5");
4152
4153 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4156 assert_eq!(
4157 tokens.len(),
4158 3,
4159 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4160 tokens
4161 );
4162 assert_eq!(tokens[1].token_type, TokenType::Number);
4163 assert_eq!(tokens[1].text, "1.");
4164 assert_eq!(tokens[2].token_type, TokenType::Var);
4165
4166 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4168 assert_eq!(tokens[1].token_type, TokenType::Number);
4169 assert_eq!(tokens[1].text, "1");
4170 assert_eq!(tokens[2].token_type, TokenType::Dot);
4171 assert_eq!(tokens[3].token_type, TokenType::Dot);
4172 assert_eq!(tokens[4].token_type, TokenType::Number);
4173 assert_eq!(tokens[4].text, "2");
4174 }
4175
4176 #[test]
4177 fn test_leading_dot_number() {
4178 let tokenizer = Tokenizer::default();
4179
4180 let tokens = tokenizer.tokenize(".25").unwrap();
4182 assert_eq!(
4183 tokens.len(),
4184 1,
4185 "Expected 1 token for '.25', got {:?}",
4186 tokens
4187 );
4188 assert_eq!(tokens[0].token_type, TokenType::Number);
4189 assert_eq!(tokens[0].text, ".25");
4190
4191 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4193 assert_eq!(
4194 tokens.len(),
4195 4,
4196 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4197 tokens
4198 );
4199 assert_eq!(tokens[0].token_type, TokenType::Sample);
4200 assert_eq!(tokens[1].token_type, TokenType::LParen);
4201 assert_eq!(tokens[2].token_type, TokenType::Number);
4202 assert_eq!(tokens[2].text, ".25");
4203 assert_eq!(tokens[3].token_type, TokenType::RParen);
4204
4205 let tokens = tokenizer.tokenize(".5e10").unwrap();
4207 assert_eq!(
4208 tokens.len(),
4209 1,
4210 "Expected 1 token for '.5e10', got {:?}",
4211 tokens
4212 );
4213 assert_eq!(tokens[0].token_type, TokenType::Number);
4214 assert_eq!(tokens[0].text, ".5e10");
4215
4216 let tokens = tokenizer.tokenize("a.b").unwrap();
4218 assert_eq!(
4219 tokens.len(),
4220 3,
4221 "Expected 3 tokens for 'a.b', got {:?}",
4222 tokens
4223 );
4224 assert_eq!(tokens[1].token_type, TokenType::Dot);
4225 }
4226
4227 #[test]
4228 fn test_unrecognized_character() {
4229 let tokenizer = Tokenizer::default();
4230
4231 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4233 assert!(
4234 result.is_ok(),
4235 "Curly quotes should be tokenized as strings"
4236 );
4237
4238 let result = tokenizer.tokenize("SELECT • FROM t");
4240 assert!(result.is_err());
4241 }
4242
4243 #[test]
4244 fn test_colon_eq_tokenization() {
4245 let tokenizer = Tokenizer::default();
4246
4247 let tokens = tokenizer.tokenize("a := 1").unwrap();
4249 assert_eq!(tokens.len(), 3);
4250 assert_eq!(tokens[0].token_type, TokenType::Var);
4251 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4252 assert_eq!(tokens[2].token_type, TokenType::Number);
4253
4254 let tokens = tokenizer.tokenize("a:b").unwrap();
4256 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4257 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4258
4259 let tokens = tokenizer.tokenize("a::INT").unwrap();
4261 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4262 }
4263
4264 #[test]
4265 fn test_colon_eq_parsing() {
4266 use crate::generator::Generator;
4267 use crate::parser::Parser;
4268
4269 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4271 .expect("Failed to parse MySQL @var := expr");
4272 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4273 assert_eq!(output, "SELECT @var1 := 1, @var2");
4274
4275 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4277 .expect("Failed to parse MySQL @var2 := @var1");
4278 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4279 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4280
4281 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4283 .expect("Failed to parse MySQL @var := COUNT(*)");
4284 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4285 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4286
4287 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4289 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4290 assert_eq!(output, "SET @var1 = 1");
4291
4292 let ast =
4294 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4295 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4296 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4297
4298 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4300 .expect("Failed to parse UNNEST with :=");
4301 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4302 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4303
4304 let ast =
4306 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4307 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4308 assert_eq!(output, "SELECT 1 AS foo");
4309
4310 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4312 .expect("Failed to parse DuckDB multiple prefix aliases");
4313 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4314 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4315 }
4316
4317 #[test]
4318 fn test_colon_eq_dialect_roundtrip() {
4319 use crate::dialects::{Dialect, DialectType};
4320
4321 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4322 let d = Dialect::get(dialect);
4323 let ast = d
4324 .parse(sql)
4325 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4326 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4327 let transformed = d
4328 .transform(ast[0].clone())
4329 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4330 let output = d
4331 .generate(&transformed)
4332 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4333 let expected = expected.unwrap_or(sql);
4334 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4335 }
4336
4337 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4339 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4340 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4341 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4342
4343 check(
4345 DialectType::DuckDB,
4346 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4347 None,
4348 );
4349 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4350
4351 {
4354 let d = Dialect::get(DialectType::DuckDB);
4355 let ast = d
4356 .parse("STRUCT_PACK(a := 'b')::json")
4357 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4358 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4359 }
4360
4361 check(
4363 DialectType::DuckDB,
4364 "SELECT foo: 1",
4365 Some("SELECT 1 AS foo"),
4366 );
4367 check(
4368 DialectType::DuckDB,
4369 "SELECT foo: 1, bar: 2, baz: 3",
4370 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4371 );
4372 }
4373
4374 #[test]
4375 fn test_comment_roundtrip() {
4376 use crate::generator::Generator;
4377 use crate::parser::Parser;
4378
4379 fn check_roundtrip(sql: &str) -> Option<String> {
4380 let ast = match Parser::parse_sql(sql) {
4381 Ok(a) => a,
4382 Err(e) => return Some(format!("Parse error: {:?}", e)),
4383 };
4384 if ast.is_empty() {
4385 return Some("Empty AST".to_string());
4386 }
4387 let mut generator = Generator::default();
4388 let output = match generator.generate(&ast[0]) {
4389 Ok(o) => o,
4390 Err(e) => return Some(format!("Gen error: {:?}", e)),
4391 };
4392 if output == sql {
4393 None
4394 } else {
4395 Some(format!(
4396 "Mismatch:\n input: {}\n output: {}",
4397 sql, output
4398 ))
4399 }
4400 }
4401
4402 let tests = vec![
4403 "SELECT c /* c1 */ AS alias /* c2 */",
4409 "SELECT a /* x */, b /* x */",
4411 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4413 "SELECT * FROM foo /* x */, bla /* x */",
4415 "SELECT 1 /* comment */ + 1",
4417 "SELECT 1 /* c1 */ + 2 /* c2 */",
4418 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4419 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4421 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4423 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4425 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4427 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4429 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4430 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4431 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4432 "/* comment */ CREATE TABLE foo AS SELECT 1",
4433 "INSERT INTO foo SELECT * FROM bar /* comment */",
4435 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4437 ];
4438
4439 let mut failures = Vec::new();
4440 for sql in tests {
4441 if let Some(e) = check_roundtrip(sql) {
4442 failures.push(e);
4443 }
4444 }
4445
4446 if !failures.is_empty() {
4447 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4448 }
4449 }
4450
4451 #[test]
4452 fn test_dollar_quoted_string_parsing() {
4453 use crate::dialects::{Dialect, DialectType};
4454
4455 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4457 assert_eq!(tag, Some("FOO".to_string()));
4458 assert_eq!(content, "content here");
4459
4460 let (tag, content) = super::parse_dollar_string_token("just content");
4461 assert_eq!(tag, None);
4462 assert_eq!(content, "just content");
4463
4464 fn check_databricks(sql: &str, expected: Option<&str>) {
4466 let d = Dialect::get(DialectType::Databricks);
4467 let ast = d
4468 .parse(sql)
4469 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4470 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4471 let transformed = d
4472 .transform(ast[0].clone())
4473 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4474 let output = d
4475 .generate(&transformed)
4476 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4477 let expected = expected.unwrap_or(sql);
4478 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4479 }
4480
4481 check_databricks(
4483 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4484 None
4485 );
4486
4487 check_databricks(
4489 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4490 None
4491 );
4492 }
4493
4494 #[test]
4495 fn test_numeric_underscore_stripping() {
4496 let mut config = TokenizerConfig::default();
4498 config.numbers_can_be_underscore_separated = true;
4499 let tokenizer = Tokenizer::new(config);
4500
4501 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4503 assert_eq!(tokens[1].token_type, TokenType::Number);
4504 assert_eq!(tokens[1].text, "12345");
4505
4506 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4508 assert_eq!(tokens[1].token_type, TokenType::Number);
4509 assert_eq!(tokens[1].text, "20000");
4510
4511 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4513 assert_eq!(tokens[1].token_type, TokenType::Number);
4514 assert_eq!(tokens[1].text, "12E+10");
4515
4516 let default_tokenizer = Tokenizer::default();
4518 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4519 assert_eq!(tokens[1].token_type, TokenType::Number);
4520 assert_eq!(tokens[1].text, "1_2_3_4_5");
4521 }
4522}