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 allow_empty_hex_string: bool,
1513 pub string_escapes_allowed_in_raw_strings: bool,
1518 pub hash_comments: bool,
1520 pub dollar_sign_is_identifier: bool,
1524 pub insert_format_raw_data: bool,
1528 pub numbers_can_be_underscore_separated: bool,
1532 pub recover_terminal_backslash_quote: bool,
1536 pub recover_unterminated_string: bool,
1540}
1541
1542impl Default for TokenizerConfig {
1543 fn default() -> Self {
1544 Self {
1545 keywords: DEFAULT_KEYWORDS.clone(),
1546 single_tokens: DEFAULT_SINGLE_TOKENS.clone(),
1547 quotes: DEFAULT_QUOTES.clone(),
1548 identifiers: DEFAULT_IDENTIFIERS.clone(),
1549 comments: DEFAULT_COMMENTS.clone(),
1550 string_escapes: vec!['\''],
1553 nested_comments: true,
1554 escape_follow_chars: vec![],
1556 b_prefix_is_byte_string: false,
1558 numeric_literals: HashMap::new(),
1559 identifiers_can_start_with_digit: false,
1560 hex_number_strings: false,
1561 hex_string_is_integer_type: false,
1562 allow_empty_hex_string: false,
1563 string_escapes_allowed_in_raw_strings: true,
1566 hash_comments: false,
1567 dollar_sign_is_identifier: false,
1568 insert_format_raw_data: false,
1569 numbers_can_be_underscore_separated: false,
1570 recover_terminal_backslash_quote: false,
1571 recover_unterminated_string: false,
1572 }
1573 }
1574}
1575
1576pub struct Tokenizer {
1578 config: Arc<TokenizerConfig>,
1579}
1580
1581impl Tokenizer {
1582 pub fn new(config: TokenizerConfig) -> Self {
1584 Self {
1585 config: Arc::new(config),
1586 }
1587 }
1588
1589 pub(crate) fn from_shared_config(config: Arc<TokenizerConfig>) -> Self {
1590 Self { config }
1591 }
1592
1593 pub fn default_config() -> Self {
1595 Self::new(TokenizerConfig::default())
1596 }
1597
1598 pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
1600 if sql.is_ascii() {
1601 TokenizerState::<_, Token>::new(sql, &self.config, AsciiCursor(sql.as_bytes()))
1602 .tokenize()
1603 } else {
1604 TokenizerState::<_, Token>::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1605 }
1606 }
1607
1608 pub(crate) fn tokenize_for_parser(
1609 &self,
1610 sql: &Arc<str>,
1611 ) -> Result<(Vec<ParserToken>, TokenGuardStats)> {
1612 if sql.is_ascii() {
1613 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1614 sql,
1615 Arc::clone(sql),
1616 &self.config,
1617 AsciiCursor(sql.as_bytes()),
1618 );
1619 let tokens = state.tokenize()?;
1620 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1621 } else {
1622 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1623 sql,
1624 Arc::clone(sql),
1625 &self.config,
1626 UnicodeCursor::new(sql),
1627 );
1628 let tokens = state.tokenize()?;
1629 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1630 }
1631 }
1632
1633 #[cfg(test)]
1634 fn tokenize_without_ascii_fast_path(&self, sql: &str) -> Result<Vec<Token>> {
1635 TokenizerState::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1636 }
1637
1638 #[cfg(test)]
1639 pub(crate) fn shares_config_with(&self, other: &Self) -> bool {
1640 Arc::ptr_eq(&self.config, &other.config)
1641 }
1642}
1643
1644impl Default for Tokenizer {
1645 fn default() -> Self {
1646 Self::default_config()
1647 }
1648}
1649
1650trait TokenizerCursor {
1651 fn len(&self) -> usize;
1652 fn char_at(&self, index: usize) -> char;
1653 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String;
1654
1655 fn source_range<'a>(&self, _source: &'a str, _start: usize, _end: usize) -> Option<&'a str> {
1656 None
1657 }
1658
1659 fn range_contains(&self, start: usize, needle: char) -> bool {
1660 (start..self.len()).any(|index| self.char_at(index) == needle)
1661 }
1662}
1663
1664struct AsciiCursor<'a>(&'a [u8]);
1665
1666impl TokenizerCursor for AsciiCursor<'_> {
1667 #[inline]
1668 fn len(&self) -> usize {
1669 self.0.len()
1670 }
1671
1672 #[inline]
1673 fn char_at(&self, index: usize) -> char {
1674 self.0[index] as char
1675 }
1676
1677 #[inline]
1678 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String {
1679 source[start..end].to_string()
1680 }
1681
1682 #[inline]
1683 fn source_range<'a>(&self, source: &'a str, start: usize, end: usize) -> Option<&'a str> {
1684 Some(&source[start..end])
1685 }
1686}
1687
1688struct UnicodeCursor(Vec<char>);
1689
1690impl UnicodeCursor {
1691 fn new(source: &str) -> Self {
1692 Self(source.chars().collect())
1693 }
1694}
1695
1696impl TokenizerCursor for UnicodeCursor {
1697 #[inline]
1698 fn len(&self) -> usize {
1699 self.0.len()
1700 }
1701
1702 #[inline]
1703 fn char_at(&self, index: usize) -> char {
1704 self.0[index]
1705 }
1706
1707 #[inline]
1708 fn text_from_range(&self, _source: &str, start: usize, end: usize) -> String {
1709 self.0[start..end].iter().collect()
1710 }
1711}
1712
1713struct TokenizerState<'a, C, T> {
1715 source: &'a str,
1716 shared_source: Option<Arc<str>>,
1717 cursor: C,
1718 size: usize,
1719 tokens: Vec<T>,
1720 start: usize,
1721 current: usize,
1722 line: usize,
1723 column: usize,
1724 comments: Vec<String>,
1725 guard_stats: Option<TokenGuardStats>,
1726 config: &'a TokenizerConfig,
1727}
1728
1729impl<'a, C: TokenizerCursor, T: TokenOutput> TokenizerState<'a, C, T> {
1730 fn new(sql: &'a str, config: &'a TokenizerConfig, cursor: C) -> Self {
1731 let size = cursor.len();
1732 Self {
1733 source: sql,
1734 shared_source: None,
1735 cursor,
1736 size,
1737 tokens: Vec::new(),
1738 start: 0,
1739 current: 0,
1740 line: 1,
1741 column: 1,
1742 comments: Vec::new(),
1743 guard_stats: None,
1744 config,
1745 }
1746 }
1747
1748 fn new_shared(sql: &'a str, source: Arc<str>, config: &'a TokenizerConfig, cursor: C) -> Self {
1749 let size = cursor.len();
1750 Self {
1751 source: sql,
1752 shared_source: Some(source),
1753 cursor,
1754 size,
1755 tokens: Vec::new(),
1756 start: 0,
1757 current: 0,
1758 line: 1,
1759 column: 1,
1760 comments: Vec::new(),
1761 guard_stats: Some(TokenGuardStats::default()),
1762 config,
1763 }
1764 }
1765
1766 fn tokenize(&mut self) -> Result<Vec<T>> {
1767 while !self.is_at_end() {
1768 self.skip_whitespace();
1769 if self.is_at_end() {
1770 break;
1771 }
1772
1773 self.start = self.current;
1774 self.scan_token()?;
1775
1776 if self.config.insert_format_raw_data {
1779 if let Some(raw) = self.try_scan_insert_format_raw_data() {
1780 if !raw.is_empty() {
1781 self.start = self.current;
1782 self.add_token_with_text(TokenType::Var, raw);
1783 }
1784 }
1785 }
1786 }
1787
1788 if !self.comments.is_empty() {
1793 if let Some(last) = self.tokens.last_mut() {
1794 last.trailing_comments_mut().extend(self.comments.drain(..));
1795 }
1796 }
1797
1798 Ok(std::mem::take(&mut self.tokens))
1799 }
1800
1801 #[inline]
1802 fn is_at_end(&self) -> bool {
1803 self.current >= self.size
1804 }
1805
1806 #[inline]
1807 fn text_from_range(&self, start: usize, end: usize) -> String {
1808 self.cursor.text_from_range(self.source, start, end)
1809 }
1810
1811 #[inline]
1812 fn char_at(&self, index: usize) -> char {
1813 self.cursor.char_at(index)
1814 }
1815
1816 #[inline]
1817 fn range_contains(&self, start: usize, needle: char) -> bool {
1818 self.cursor.range_contains(start, needle)
1819 }
1820
1821 #[inline]
1822 fn peek(&self) -> char {
1823 if self.is_at_end() {
1824 '\0'
1825 } else {
1826 self.char_at(self.current)
1827 }
1828 }
1829
1830 #[inline]
1831 fn peek_next(&self) -> char {
1832 if self.current + 1 >= self.size {
1833 '\0'
1834 } else {
1835 self.char_at(self.current + 1)
1836 }
1837 }
1838
1839 #[inline]
1840 fn advance(&mut self) -> char {
1841 let c = self.peek();
1842 self.current += 1;
1843 if c == '\n' {
1844 self.line += 1;
1845 self.column = 1;
1846 } else {
1847 self.column += 1;
1848 }
1849 c
1850 }
1851
1852 #[inline]
1853 fn advance_ascii_to(&mut self, end: usize) -> bool {
1854 let Some(text) = self.cursor.source_range(self.source, self.current, end) else {
1855 return false;
1856 };
1857
1858 let newline_count = text
1859 .as_bytes()
1860 .iter()
1861 .filter(|&&byte| byte == b'\n')
1862 .count();
1863 if newline_count == 0 {
1864 self.column += end - self.current;
1865 } else {
1866 self.line += newline_count;
1867 let last_newline = text
1868 .as_bytes()
1869 .iter()
1870 .rposition(|&byte| byte == b'\n')
1871 .expect("newline count is non-zero");
1872 self.column = text.len() - last_newline;
1873 }
1874 self.current = end;
1875 true
1876 }
1877
1878 #[inline]
1879 fn advance_ascii_digits(&mut self) -> bool {
1880 let Some(rest) = self
1881 .cursor
1882 .source_range(self.source, self.current, self.size)
1883 else {
1884 return false;
1885 };
1886 let bytes = rest.as_bytes();
1887 let mut length = 0;
1888 while length < bytes.len() {
1889 match bytes[length] {
1890 b'0'..=b'9' => length += 1,
1891 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_digit) => length += 1,
1892 _ => break,
1893 }
1894 }
1895 self.current += length;
1896 self.column += length;
1897 true
1898 }
1899
1900 #[inline]
1901 fn advance_ascii_hex_digits(&mut self) -> bool {
1902 let Some(rest) = self
1903 .cursor
1904 .source_range(self.source, self.current, self.size)
1905 else {
1906 return false;
1907 };
1908 let bytes = rest.as_bytes();
1909 let mut length = 0;
1910 while length < bytes.len() {
1911 match bytes[length] {
1912 byte if byte.is_ascii_hexdigit() => length += 1,
1913 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_hexdigit) => length += 1,
1914 _ => break,
1915 }
1916 }
1917 self.current += length;
1918 self.column += length;
1919 true
1920 }
1921
1922 #[inline]
1923 fn advance_ascii_identifier(&mut self) -> bool {
1924 let Some(rest) = self
1925 .cursor
1926 .source_range(self.source, self.current, self.size)
1927 else {
1928 return false;
1929 };
1930 let bytes = rest.as_bytes();
1931 let mut length = 0;
1932 while length < bytes.len() {
1933 let byte = bytes[length];
1934 if byte == b'#' && matches!(bytes.get(length + 1), Some(b'>') | Some(b'-')) {
1935 break;
1936 }
1937 if byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$' | b'#' | b'@') {
1938 length += 1;
1939 } else {
1940 break;
1941 }
1942 }
1943 self.current += length;
1944 self.column += length;
1945 true
1946 }
1947
1948 fn try_scan_simple_quoted_content(
1949 &mut self,
1950 quote: char,
1951 backslash_is_escape: bool,
1952 ) -> Option<(usize, usize)> {
1953 let content_start = self.current;
1954 let rest = self
1955 .cursor
1956 .source_range(self.source, content_start, self.size)?;
1957 let quote_offset = rest.find(quote)?;
1958 let content_end = content_start + quote_offset;
1959
1960 if (content_end + 1 < self.size && self.char_at(content_end + 1) == quote)
1961 || (backslash_is_escape && rest[..quote_offset].contains('\\'))
1962 {
1963 return None;
1964 }
1965
1966 self.advance_ascii_to(content_end);
1967 self.advance();
1968 Some((content_start, content_end))
1969 }
1970
1971 fn skip_whitespace(&mut self) {
1972 let mut saw_newline = false;
1977 while !self.is_at_end() {
1978 let c = self.peek();
1979 match c {
1980 ' ' | '\t' | '\r' => {
1981 self.advance();
1982 }
1983 '\n' => {
1984 saw_newline = true;
1985 self.advance();
1986 }
1987 '\u{00A0}' | '\u{2000}'..='\u{200B}' | '\u{3000}' | '\u{FEFF}' => {
1992 self.advance();
1993 }
1994 '-' if self.peek_next() == '-' => {
1995 self.scan_line_comment(saw_newline);
1996 saw_newline = true;
1998 }
1999 '/' if self.peek_next() == '/' && self.config.hash_comments => {
2000 self.scan_double_slash_comment();
2002 }
2003 '/' if self.peek_next() == '*' => {
2004 if self.current + 2 < self.size && self.char_at(self.current + 2) == '+' {
2006 break;
2008 }
2009 if self.scan_block_comment(saw_newline).is_err() {
2010 return;
2011 }
2012 }
2014 '/' if self.peek_next() == '/' && self.config.comments.contains_key("//") => {
2015 let prev_non_ws = if self.current > 0 {
2019 let mut i = self.current - 1;
2020 while i > 0 && (self.char_at(i) == ' ' || self.char_at(i) == '\t') {
2021 i -= 1;
2022 }
2023 self.char_at(i)
2024 } else {
2025 '\0'
2026 };
2027 if prev_non_ws == ':' || prev_non_ws == '/' {
2028 break;
2030 }
2031 self.scan_line_comment(saw_newline);
2032 saw_newline = true;
2034 }
2035 '#' if self.config.hash_comments => {
2036 self.scan_hash_line_comment();
2037 }
2038 _ => break,
2039 }
2040 }
2041 }
2042
2043 fn scan_hash_line_comment(&mut self) {
2044 self.advance(); let start = self.current;
2046 while !self.is_at_end() && self.peek() != '\n' {
2047 self.advance();
2048 }
2049 let comment = self.text_from_range(start, self.current);
2050 let comment_text = comment.trim().to_string();
2051 if let Some(last) = self.tokens.last_mut() {
2052 last.trailing_comments_mut().push(comment_text);
2053 } else {
2054 self.comments.push(comment_text);
2055 }
2056 }
2057
2058 fn scan_double_slash_comment(&mut self) {
2059 self.advance(); self.advance(); let start = self.current;
2062 while !self.is_at_end() && self.peek() != '\n' {
2063 self.advance();
2064 }
2065 let comment = self.text_from_range(start, self.current);
2066 let comment_text = comment.trim().to_string();
2067 if let Some(last) = self.tokens.last_mut() {
2068 last.trailing_comments_mut().push(comment_text);
2069 } else {
2070 self.comments.push(comment_text);
2071 }
2072 }
2073
2074 fn scan_line_comment(&mut self, after_newline: bool) {
2075 self.advance(); self.advance(); let start = self.current;
2078 while !self.is_at_end() && self.peek() != '\n' {
2079 self.advance();
2080 }
2081 let comment_text = self.text_from_range(start, self.current);
2082
2083 if after_newline || self.tokens.is_empty() {
2086 self.comments.push(comment_text);
2087 } else if let Some(last) = self.tokens.last_mut() {
2088 last.trailing_comments_mut().push(comment_text);
2089 }
2090 }
2091
2092 fn scan_block_comment(&mut self, after_newline: bool) -> Result<()> {
2093 self.advance(); self.advance(); let content_start = self.current;
2096 let mut depth = 1;
2097
2098 while !self.is_at_end() && depth > 0 {
2099 if self.peek() == '/' && self.peek_next() == '*' && self.config.nested_comments {
2100 self.advance();
2101 self.advance();
2102 depth += 1;
2103 } else if self.peek() == '*' && self.peek_next() == '/' {
2104 depth -= 1;
2105 if depth > 0 {
2106 self.advance();
2107 self.advance();
2108 }
2109 } else {
2110 self.advance();
2111 }
2112 }
2113
2114 if depth > 0 {
2115 return Err(Error::tokenize(
2116 "Unterminated block comment",
2117 self.line,
2118 self.column,
2119 self.start,
2120 self.current,
2121 ));
2122 }
2123
2124 let content = self.text_from_range(content_start, self.current);
2126 self.advance(); self.advance(); let comment_text = format!("/*{}*/", content);
2131
2132 if after_newline || self.tokens.is_empty() {
2135 self.comments.push(comment_text);
2136 } else if let Some(last) = self.tokens.last_mut() {
2137 last.trailing_comments_mut().push(comment_text);
2138 }
2139
2140 Ok(())
2141 }
2142
2143 fn scan_hint(&mut self) -> Result<()> {
2145 self.advance(); self.advance(); self.advance(); let hint_start = self.current;
2149
2150 while !self.is_at_end() {
2152 if self.peek() == '*' && self.peek_next() == '/' {
2153 break;
2154 }
2155 self.advance();
2156 }
2157
2158 if self.is_at_end() {
2159 return Err(Error::tokenize(
2160 "Unterminated hint comment",
2161 self.line,
2162 self.column,
2163 self.start,
2164 self.current,
2165 ));
2166 }
2167
2168 let hint_text = self.text_from_range(hint_start, self.current);
2169 self.advance(); self.advance(); self.add_token_with_text(TokenType::Hint, hint_text.trim().to_string());
2173
2174 Ok(())
2175 }
2176
2177 fn scan_positional_parameter(&mut self) -> Result<()> {
2179 self.advance(); let start = self.current;
2181
2182 while !self.is_at_end() && self.peek().is_ascii_digit() {
2183 self.advance();
2184 }
2185
2186 let number = self.text_from_range(start, self.current);
2187 self.add_token_with_text(TokenType::Parameter, number);
2188 Ok(())
2189 }
2190
2191 fn try_scan_tagged_dollar_string(&mut self) -> Result<Option<()>> {
2196 let saved_pos = self.current;
2197
2198 self.advance(); let tag_start = self.current;
2204 while !self.is_at_end()
2205 && (self.peek().is_alphanumeric() || self.peek() == '_' || !self.peek().is_ascii())
2206 {
2207 self.advance();
2208 }
2209 let tag = self.text_from_range(tag_start, self.current);
2210
2211 if self.is_at_end() || self.peek() != '$' {
2213 self.current = saved_pos;
2215 return Ok(None);
2216 }
2217 self.advance(); let content_start = self.current;
2221 let closing_tag = format!("${}$", tag);
2222 let closing_chars: Vec<char> = closing_tag.chars().collect();
2223
2224 loop {
2225 if self.is_at_end() {
2226 self.current = saved_pos;
2228 return Ok(None);
2229 }
2230
2231 if self.peek() == '$' && self.current + closing_chars.len() <= self.size {
2233 let matches = closing_chars.iter().enumerate().all(|(j, &ch)| {
2234 self.current + j < self.size && self.char_at(self.current + j) == ch
2235 });
2236 if matches {
2237 let content = self.text_from_range(content_start, self.current);
2238 for _ in 0..closing_chars.len() {
2240 self.advance();
2241 }
2242 let token_text = format!("{}\x00{}", tag, content);
2244 self.add_token_with_text(TokenType::DollarString, token_text);
2245 return Ok(Some(()));
2246 }
2247 }
2248 self.advance();
2249 }
2250 }
2251
2252 fn scan_dollar_quoted_string(&mut self) -> Result<()> {
2257 self.advance(); self.advance(); let start = self.current;
2262 while !self.is_at_end() {
2263 if self.peek() == '$'
2264 && self.current + 1 < self.size
2265 && self.char_at(self.current + 1) == '$'
2266 {
2267 break;
2268 }
2269 self.advance();
2270 }
2271
2272 let content = self.text_from_range(start, self.current);
2273
2274 if !self.is_at_end() {
2275 self.advance(); self.advance(); }
2278
2279 self.add_token_with_text(TokenType::DollarString, content);
2280 Ok(())
2281 }
2282
2283 fn scan_token(&mut self) -> Result<()> {
2284 let c = self.peek();
2285
2286 if c == '\'' {
2288 if self.config.quotes.contains_key("'''")
2290 && self.peek_next() == '\''
2291 && self.current + 2 < self.size
2292 && self.char_at(self.current + 2) == '\''
2293 {
2294 return self.scan_triple_quoted_string('\'');
2295 }
2296 return self.scan_string();
2297 }
2298
2299 if c == '"'
2301 && self.config.quotes.contains_key("\"\"\"")
2302 && self.peek_next() == '"'
2303 && self.current + 2 < self.size
2304 && self.char_at(self.current + 2) == '"'
2305 {
2306 return self.scan_triple_quoted_string('"');
2307 }
2308
2309 if c == '"'
2312 && self.config.quotes.contains_key("\"")
2313 && !self.config.identifiers.contains_key(&'"')
2314 {
2315 return self.scan_double_quoted_string();
2316 }
2317
2318 if let Some(&end_quote) = self.config.identifiers.get(&c) {
2320 return self.scan_quoted_identifier(end_quote);
2321 }
2322
2323 if c.is_ascii_digit() {
2325 return self.scan_number();
2326 }
2327
2328 if c == '.' && self.peek_next().is_ascii_digit() {
2335 let prev_char = if self.current > 0 {
2336 self.char_at(self.current - 1)
2337 } else {
2338 '\0'
2339 };
2340 let is_after_ident = prev_char.is_alphanumeric()
2341 || prev_char == '_'
2342 || prev_char == '`'
2343 || prev_char == '"'
2344 || prev_char == ']'
2345 || prev_char == ')';
2346 if prev_char != '.' && !is_after_ident {
2347 return self.scan_number_starting_with_dot();
2348 }
2349 }
2350
2351 if c == '/'
2353 && self.peek_next() == '*'
2354 && self.current + 2 < self.size
2355 && self.char_at(self.current + 2) == '+'
2356 {
2357 return self.scan_hint();
2358 }
2359
2360 if let Some(token_type) = self.try_scan_multi_char_operator() {
2362 self.add_token(token_type);
2363 return Ok(());
2364 }
2365
2366 if c == '$'
2369 && (self.peek_next().is_alphanumeric()
2370 || self.peek_next() == '_'
2371 || !self.peek_next().is_ascii())
2372 {
2373 if let Some(()) = self.try_scan_tagged_dollar_string()? {
2374 return Ok(());
2375 }
2376 if self.config.dollar_sign_is_identifier {
2379 return self.scan_dollar_identifier();
2380 }
2381 }
2382
2383 if c == '$' && self.peek_next() == '$' {
2385 return self.scan_dollar_quoted_string();
2386 }
2387
2388 if c == '$' && self.peek_next().is_ascii_digit() {
2390 return self.scan_positional_parameter();
2391 }
2392
2393 if c == '$' && self.config.dollar_sign_is_identifier {
2395 return self.scan_dollar_identifier();
2396 }
2397
2398 if (c == '#' || c == '@')
2401 && (self.peek_next().is_alphanumeric()
2402 || self.peek_next() == '_'
2403 || self.peek_next() == '#')
2404 {
2405 return self.scan_tsql_identifier();
2406 }
2407
2408 if let Some(&token_type) = self.config.single_tokens.get(&c) {
2410 self.advance();
2411 self.add_token(token_type);
2412 return Ok(());
2413 }
2414
2415 if c == '\u{2212}' {
2417 self.advance();
2418 self.add_token(TokenType::Dash);
2419 return Ok(());
2420 }
2421
2422 if c == '\u{2044}' {
2424 self.advance();
2425 self.add_token(TokenType::Slash);
2426 return Ok(());
2427 }
2428
2429 if c == '\u{2018}' || c == '\u{2019}' {
2431 return self.scan_unicode_quoted_string(c);
2433 }
2434 if c == '\u{201C}' || c == '\u{201D}' {
2435 return self.scan_unicode_quoted_identifier(c);
2437 }
2438
2439 self.scan_identifier_or_keyword()
2441 }
2442
2443 fn try_scan_multi_char_operator(&mut self) -> Option<TokenType> {
2444 let c = self.peek();
2445 let next = self.peek_next();
2446 let third = if self.current + 2 < self.size {
2447 self.char_at(self.current + 2)
2448 } else {
2449 '\0'
2450 };
2451
2452 if c == '-' && next == '|' && third == '-' {
2455 self.advance();
2456 self.advance();
2457 self.advance();
2458 return Some(TokenType::Adjacent);
2459 }
2460
2461 if c == '|' && next == '|' && third == '/' {
2463 self.advance();
2464 self.advance();
2465 self.advance();
2466 return Some(TokenType::DPipeSlash);
2467 }
2468
2469 if c == '#' && next == '>' && third == '>' {
2471 self.advance();
2472 self.advance();
2473 self.advance();
2474 return Some(TokenType::DHashArrow);
2475 }
2476
2477 if c == '-' && next == '>' && third == '>' {
2479 self.advance();
2480 self.advance();
2481 self.advance();
2482 return Some(TokenType::DArrow);
2483 }
2484
2485 if c == '<' && next == '=' && third == '>' {
2487 self.advance();
2488 self.advance();
2489 self.advance();
2490 return Some(TokenType::NullsafeEq);
2491 }
2492
2493 if c == '<' && next == '-' && third == '>' {
2495 self.advance();
2496 self.advance();
2497 self.advance();
2498 return Some(TokenType::LrArrow);
2499 }
2500
2501 if c == '<' && next == '@' {
2503 self.advance();
2504 self.advance();
2505 return Some(TokenType::LtAt);
2506 }
2507
2508 if c == '@' && next == '>' {
2510 self.advance();
2511 self.advance();
2512 return Some(TokenType::AtGt);
2513 }
2514
2515 if c == '~' && next == '~' && third == '~' {
2517 self.advance();
2518 self.advance();
2519 self.advance();
2520 return Some(TokenType::Glob);
2521 }
2522
2523 if c == '~' && next == '~' && third == '*' {
2525 self.advance();
2526 self.advance();
2527 self.advance();
2528 return Some(TokenType::ILike);
2529 }
2530
2531 let fourth = if self.current + 3 < self.size {
2533 self.char_at(self.current + 3)
2534 } else {
2535 '\0'
2536 };
2537 if c == '!' && next == '~' && third == '~' && fourth == '*' {
2538 self.advance();
2539 self.advance();
2540 self.advance();
2541 self.advance();
2542 return Some(TokenType::NotILike);
2543 }
2544
2545 if c == '!' && next == '~' && third == '~' {
2547 self.advance();
2548 self.advance();
2549 self.advance();
2550 return Some(TokenType::NotLike);
2551 }
2552
2553 if c == '!' && next == '~' && third == '*' {
2555 self.advance();
2556 self.advance();
2557 self.advance();
2558 return Some(TokenType::NotIRLike);
2559 }
2560
2561 if c == '!' && next == ':' && third == '>' {
2563 self.advance();
2564 self.advance();
2565 self.advance();
2566 return Some(TokenType::NColonGt);
2567 }
2568
2569 if c == '?' && next == ':' && third == ':' {
2571 self.advance();
2572 self.advance();
2573 self.advance();
2574 return Some(TokenType::QDColon);
2575 }
2576
2577 if c == '!' && next == '~' {
2579 self.advance();
2580 self.advance();
2581 return Some(TokenType::NotRLike);
2582 }
2583
2584 if c == '~' && next == '~' {
2586 self.advance();
2587 self.advance();
2588 return Some(TokenType::Like);
2589 }
2590
2591 if c == '~' && next == '*' {
2593 self.advance();
2594 self.advance();
2595 return Some(TokenType::IRLike);
2596 }
2597
2598 if c == ':' && next == ':' && third == '$' {
2601 self.advance();
2602 self.advance();
2603 self.advance();
2604 return Some(TokenType::DColonDollar);
2605 }
2606 if c == ':' && next == ':' && third == '%' {
2607 self.advance();
2608 self.advance();
2609 self.advance();
2610 return Some(TokenType::DColonPercent);
2611 }
2612 if c == ':' && next == ':' && third == '?' {
2613 self.advance();
2614 self.advance();
2615 self.advance();
2616 return Some(TokenType::DColonQMark);
2617 }
2618
2619 let token_type = match (c, next) {
2621 ('.', ':') => Some(TokenType::DotColon),
2622 ('=', '=') => Some(TokenType::Eq), ('<', '=') => Some(TokenType::Lte),
2624 ('>', '=') => Some(TokenType::Gte),
2625 ('!', '=') => Some(TokenType::Neq),
2626 ('<', '>') => Some(TokenType::Neq),
2627 ('^', '=') => Some(TokenType::Neq),
2628 ('<', '<') => Some(TokenType::LtLt),
2629 ('>', '>') => Some(TokenType::GtGt),
2630 ('|', '|') => Some(TokenType::DPipe),
2631 ('|', '/') => Some(TokenType::PipeSlash), (':', ':') => Some(TokenType::DColon),
2633 (':', '=') => Some(TokenType::ColonEq), (':', '>') => Some(TokenType::ColonGt), ('-', '>') => Some(TokenType::Arrow), ('=', '>') => Some(TokenType::FArrow), ('&', '&') => Some(TokenType::DAmp),
2638 ('&', '<') => 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,
2651 };
2652
2653 if token_type.is_some() {
2654 self.advance();
2655 self.advance();
2656 }
2657
2658 token_type
2659 }
2660
2661 fn scan_string(&mut self) -> Result<()> {
2662 self.advance(); if let Some((text_start, text_end)) =
2664 self.try_scan_simple_quoted_content('\'', self.config.string_escapes.contains(&'\\'))
2665 {
2666 self.add_token_from_source(TokenType::String, text_start, text_end);
2667 return Ok(());
2668 }
2669 let mut value = String::new();
2670
2671 while !self.is_at_end() {
2672 let c = self.peek();
2673 if c == '\'' {
2674 if self.peek_next() == '\'' {
2675 value.push('\'');
2677 self.advance();
2678 self.advance();
2679 } else {
2680 break;
2681 }
2682 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2683 if self.config.recover_terminal_backslash_quote
2684 && self.peek_next() == '\''
2685 && !self.range_contains(self.current + 2, '\'')
2686 {
2687 value.push(self.advance());
2688 break;
2689 }
2690
2691 self.scan_backslash_escape(&mut value);
2692 } else {
2693 value.push(self.advance());
2694 }
2695 }
2696
2697 if self.is_at_end() {
2698 if self.config.recover_unterminated_string {
2699 self.add_token_with_text(TokenType::String, value);
2700 return Ok(());
2701 }
2702
2703 return Err(Error::tokenize(
2704 "Unterminated string",
2705 self.line,
2706 self.column,
2707 self.start,
2708 self.current,
2709 ));
2710 }
2711
2712 self.advance(); self.add_token_with_text(TokenType::String, value);
2714 Ok(())
2715 }
2716
2717 fn scan_double_quoted_string(&mut self) -> Result<()> {
2719 self.advance(); let mut value = String::new();
2721
2722 while !self.is_at_end() {
2723 let c = self.peek();
2724 if c == '"' {
2725 if self.peek_next() == '"' {
2726 value.push('"');
2728 self.advance();
2729 self.advance();
2730 } else {
2731 break;
2732 }
2733 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2734 self.scan_backslash_escape(&mut value);
2735 } else {
2736 value.push(self.advance());
2737 }
2738 }
2739
2740 if self.is_at_end() {
2741 return Err(Error::tokenize(
2742 "Unterminated double-quoted string",
2743 self.line,
2744 self.column,
2745 self.start,
2746 self.current,
2747 ));
2748 }
2749
2750 self.advance(); self.add_token_with_text(TokenType::String, value);
2752 Ok(())
2753 }
2754
2755 fn scan_backslash_escape(&mut self, value: &mut String) {
2756 self.advance(); if self.is_at_end() {
2758 value.push('\\');
2759 return;
2760 }
2761
2762 let escaped = self.advance();
2763 let restricted = !self.config.escape_follow_chars.is_empty();
2764 let always_allowed = matches!(escaped, '\\' | '\'' | '"');
2765 if restricted && !always_allowed && !self.config.escape_follow_chars.contains(&escaped) {
2766 value.push(escaped);
2767 return;
2768 }
2769
2770 let supports_octal =
2771 restricted && ('1'..='7').any(|digit| self.config.escape_follow_chars.contains(&digit));
2772 if supports_octal && escaped.is_digit(8) {
2773 if let Some(codepoint) = self.peek_radix_digits(2, 8).and_then(|suffix| {
2774 let first = escaped.to_digit(8)?;
2775 first.checked_mul(64)?.checked_add(suffix)
2776 }) {
2777 if let Ok(byte) = u8::try_from(codepoint) {
2778 self.advance_count(2);
2779 value.push(byte as char);
2780 return;
2781 }
2782 }
2783
2784 if escaped == '0' {
2785 value.push('\0');
2786 } else {
2787 value.push(escaped);
2788 }
2789 return;
2790 }
2791
2792 match escaped {
2793 'n' => value.push('\n'),
2794 'r' => value.push('\r'),
2795 't' => value.push('\t'),
2796 '0' => value.push('\0'),
2797 'Z' => value.push('\x1A'),
2798 'a' => value.push('\x07'),
2799 'b' => value.push('\x08'),
2800 'f' => value.push('\x0C'),
2801 'v' => value.push('\x0B'),
2802 'x' => {
2803 if let Some(codepoint) = self.peek_radix_digits(2, 16) {
2804 self.advance_count(2);
2805 value.push(codepoint as u8 as char);
2806 } else if restricted {
2807 value.push('x');
2809 } else {
2810 value.push('\\');
2813 value.push('x');
2814 for _ in 0..2 {
2815 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2816 value.push(self.advance());
2817 }
2818 }
2819 }
2820 }
2821 'u' if restricted && self.config.escape_follow_chars.contains(&'u') => {
2822 if let Some(codepoint) = self.peek_radix_digits(4, 16).and_then(char::from_u32) {
2823 self.advance_count(4);
2824 value.push(codepoint);
2825 } else {
2826 value.push('u');
2827 }
2828 }
2829 '\\' => value.push('\\'),
2830 '\'' => value.push('\''),
2831 '"' => value.push('"'),
2832 '%' => value.push('%'),
2833 '_' => value.push('_'),
2834 _ if restricted => value.push(escaped),
2835 _ => {
2836 value.push('\\');
2837 value.push(escaped);
2838 }
2839 }
2840 }
2841
2842 fn peek_radix_digits(&self, count: usize, radix: u32) -> Option<u32> {
2843 if self.current + count > self.size {
2844 return None;
2845 }
2846
2847 let mut value = 0_u32;
2848 for offset in 0..count {
2849 value = value
2850 .checked_mul(radix)?
2851 .checked_add(self.char_at(self.current + offset).to_digit(radix)?)?;
2852 }
2853 Some(value)
2854 }
2855
2856 fn advance_count(&mut self, count: usize) {
2857 for _ in 0..count {
2858 self.advance();
2859 }
2860 }
2861
2862 fn scan_triple_quoted_string(&mut self, quote_char: char) -> Result<()> {
2863 self.advance();
2865 self.advance();
2866 self.advance();
2867 let mut value = String::new();
2868
2869 while !self.is_at_end() {
2870 if self.peek() == quote_char
2872 && self.current + 1 < self.size
2873 && self.char_at(self.current + 1) == quote_char
2874 && self.current + 2 < self.size
2875 && self.char_at(self.current + 2) == quote_char
2876 {
2877 break;
2879 }
2880 if self.peek() == '\\' && self.config.string_escapes.contains(&'\\') {
2881 self.scan_backslash_escape(&mut value);
2882 } else {
2883 value.push(self.advance());
2884 }
2885 }
2886
2887 if self.is_at_end() {
2888 return Err(Error::tokenize(
2889 "Unterminated triple-quoted string",
2890 self.line,
2891 self.column,
2892 self.start,
2893 self.current,
2894 ));
2895 }
2896
2897 self.advance();
2899 self.advance();
2900 self.advance();
2901 let token_type = if quote_char == '"' {
2902 TokenType::TripleDoubleQuotedString
2903 } else {
2904 TokenType::TripleSingleQuotedString
2905 };
2906 self.add_token_with_text(token_type, value);
2907 Ok(())
2908 }
2909
2910 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2911 self.advance(); let mut value = String::new();
2913
2914 loop {
2915 if self.is_at_end() {
2916 return Err(Error::tokenize(
2917 "Unterminated identifier",
2918 self.line,
2919 self.column,
2920 self.start,
2921 self.current,
2922 ));
2923 }
2924 if end_quote == '`' && self.peek() == '\\' && self.peek_next() == end_quote {
2925 value.push(end_quote);
2927 self.advance(); self.advance(); continue;
2930 }
2931 if self.peek() == end_quote {
2932 if self.peek_next() == end_quote {
2933 value.push(end_quote);
2935 self.advance(); self.advance(); } else {
2938 break;
2940 }
2941 } else {
2942 value.push(self.peek());
2943 self.advance();
2944 }
2945 }
2946
2947 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2949 Ok(())
2950 }
2951
2952 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2957 self.advance(); let start = self.current;
2959 let close_quote = if open_quote == '\u{2018}' {
2961 '\u{2019}' } else {
2963 '\u{2019}' };
2965 while !self.is_at_end() && self.peek() != close_quote {
2966 self.advance();
2967 }
2968 let value = self.text_from_range(start, self.current);
2969 if !self.is_at_end() {
2970 self.advance(); }
2972 self.add_token_with_text(TokenType::String, value);
2973 Ok(())
2974 }
2975
2976 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
2979 self.advance(); let start = self.current;
2981 let close_quote = if open_quote == '\u{201C}' {
2982 '\u{201D}' } else {
2984 '\u{201D}' };
2986 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
2987 self.advance();
2988 }
2989 let value = self.text_from_range(start, self.current);
2990 if !self.is_at_end() {
2991 self.advance(); }
2993 self.add_token_with_text(TokenType::QuotedIdentifier, value);
2994 Ok(())
2995 }
2996
2997 fn scan_number(&mut self) -> Result<()> {
2998 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
3000 let next = if self.current + 1 < self.size {
3001 self.char_at(self.current + 1)
3002 } else {
3003 '\0'
3004 };
3005 if next == 'x' || next == 'X' {
3006 self.advance();
3008 self.advance();
3009 let hex_start = self.current;
3011 if !self.advance_ascii_hex_digits() {
3012 while !self.is_at_end()
3013 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3014 {
3015 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3016 break;
3017 }
3018 self.advance();
3019 }
3020 }
3021 let has_hex_digits = self.current > hex_start;
3022 let next_is_identifier_part = {
3023 let next = self.peek();
3024 next.is_alphanumeric() || matches!(next, '_' | '$' | '#' | '@')
3025 };
3026 let is_empty_hex_string = self.config.allow_empty_hex_string
3027 && !self.config.hex_string_is_integer_type
3028 && !next_is_identifier_part;
3029 if has_hex_digits || is_empty_hex_string {
3030 let mut is_hex_float = false;
3032 if has_hex_digits && !self.is_at_end() && self.peek() == '.' {
3034 let after_dot = if self.current + 1 < self.size {
3035 self.char_at(self.current + 1)
3036 } else {
3037 '\0'
3038 };
3039 if after_dot.is_ascii_hexdigit() {
3040 is_hex_float = true;
3041 self.advance(); if !self.advance_ascii_hex_digits() {
3043 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3044 self.advance();
3045 }
3046 }
3047 }
3048 }
3049 if has_hex_digits
3051 && !self.is_at_end()
3052 && (self.peek() == 'p' || self.peek() == 'P')
3053 {
3054 is_hex_float = true;
3055 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3057 self.advance();
3058 }
3059 if !self.advance_ascii_digits() {
3060 while !self.is_at_end() && self.peek().is_ascii_digit() {
3061 self.advance();
3062 }
3063 }
3064 }
3065 if is_hex_float {
3066 let raw_text = self.text_from_range(self.start, self.current);
3068 let full_text = if self.config.numbers_can_be_underscore_separated
3069 && raw_text.contains('_')
3070 {
3071 raw_text.replace('_', "")
3072 } else {
3073 raw_text
3074 };
3075 self.add_token_with_text(TokenType::Number, full_text);
3076 } else if self.config.hex_string_is_integer_type {
3077 let raw_value = self.text_from_range(hex_start, self.current);
3079 let hex_value = if self.config.numbers_can_be_underscore_separated
3080 && raw_value.contains('_')
3081 {
3082 raw_value.replace('_', "")
3083 } else {
3084 raw_value
3085 };
3086 self.add_token_with_text(TokenType::HexNumber, hex_value);
3087 } else {
3088 let raw_value = self.text_from_range(hex_start, self.current);
3090 let hex_value = if self.config.numbers_can_be_underscore_separated
3091 && raw_value.contains('_')
3092 {
3093 raw_value.replace('_', "")
3094 } else {
3095 raw_value
3096 };
3097 self.add_token_with_text(TokenType::HexString, hex_value);
3098 }
3099 return Ok(());
3100 }
3101 self.current = self.start + 1;
3104 }
3105 }
3106
3107 if !self.advance_ascii_digits() {
3109 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3110 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3112 break;
3113 }
3114 self.advance();
3115 }
3116 }
3117
3118 if self.peek() == '.' {
3122 let next = self.peek_next();
3123 if next != '.' {
3129 self.advance(); if !self.advance_ascii_digits() {
3132 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3133 {
3134 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3135 break;
3136 }
3137 self.advance();
3138 }
3139 }
3140 }
3141 }
3142
3143 if self.peek() == 'e' || self.peek() == 'E' {
3145 self.advance();
3146 if self.peek() == '+' || self.peek() == '-' {
3147 self.advance();
3148 }
3149 if !self.advance_ascii_digits() {
3150 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3151 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3152 break;
3153 }
3154 self.advance();
3155 }
3156 }
3157 }
3158
3159 let source_text = self
3160 .cursor
3161 .source_range(self.source, self.start, self.current);
3162 let raw_owned = source_text
3163 .is_none()
3164 .then(|| self.text_from_range(self.start, self.current));
3165 let raw_text = source_text.unwrap_or_else(|| {
3166 raw_owned
3167 .as_deref()
3168 .expect("non-ASCII numbers own their text")
3169 });
3170 let normalized = (self.config.numbers_can_be_underscore_separated
3173 && raw_text.contains('_'))
3174 .then(|| raw_text.replace('_', ""));
3175 let text = normalized.as_deref().unwrap_or(raw_text);
3176
3177 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3179 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3180 let suffix_match = if self.current + 1 < self.size {
3182 let two_char: String = [
3183 self.char_at(self.current).to_ascii_uppercase(),
3184 self.char_at(self.current + 1).to_ascii_uppercase(),
3185 ]
3186 .iter()
3187 .collect();
3188 if self.config.numeric_literals.contains_key(&two_char) {
3189 let after_suffix = if self.current + 2 < self.size {
3191 self.char_at(self.current + 2)
3192 } else {
3193 ' '
3194 };
3195 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3196 Some((two_char, 2))
3197 } else {
3198 None
3199 }
3200 } else if self.config.numeric_literals.contains_key(&next_char) {
3201 let after_suffix = if self.current + 1 < self.size {
3203 self.char_at(self.current + 1)
3204 } else {
3205 ' '
3206 };
3207 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3208 Some((next_char, 1))
3209 } else {
3210 None
3211 }
3212 } else {
3213 None
3214 }
3215 } else if self.config.numeric_literals.contains_key(&next_char) {
3216 Some((next_char, 1))
3218 } else {
3219 None
3220 };
3221
3222 if let Some((suffix, len)) = suffix_match {
3223 for _ in 0..len {
3225 self.advance();
3226 }
3227 let type_name = self
3230 .config
3231 .numeric_literals
3232 .get(&suffix)
3233 .expect("suffix verified by contains_key above")
3234 .clone();
3235 let combined = format!("{}::{}", text, type_name);
3236 self.add_token_with_text(TokenType::Number, combined);
3237 return Ok(());
3238 }
3239 }
3240
3241 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3244 let next = self.peek();
3245 if next.is_alphabetic() || next == '_' {
3246 if !self.advance_ascii_identifier() {
3248 while !self.is_at_end() {
3249 let ch = self.peek();
3250 if ch.is_alphanumeric() || ch == '_' {
3251 self.advance();
3252 } else {
3253 break;
3254 }
3255 }
3256 }
3257 self.add_token(TokenType::Identifier);
3258 return Ok(());
3259 }
3260 }
3261
3262 if let Some(text) = normalized.or(raw_owned) {
3263 self.add_token_with_text(TokenType::Number, text);
3264 } else {
3265 self.add_token(TokenType::Number);
3266 }
3267 Ok(())
3268 }
3269
3270 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3272 self.advance();
3274
3275 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 if self.peek() == 'e' || self.peek() == 'E' {
3287 self.advance();
3288 if self.peek() == '+' || self.peek() == '-' {
3289 self.advance();
3290 }
3291 if !self.advance_ascii_digits() {
3292 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3293 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3294 break;
3295 }
3296 self.advance();
3297 }
3298 }
3299 }
3300
3301 let source_text = self
3302 .cursor
3303 .source_range(self.source, self.start, self.current);
3304 let raw_owned = source_text
3305 .is_none()
3306 .then(|| self.text_from_range(self.start, self.current));
3307 let raw_text = source_text.unwrap_or_else(|| {
3308 raw_owned
3309 .as_deref()
3310 .expect("non-ASCII numbers own their text")
3311 });
3312 let normalized = (self.config.numbers_can_be_underscore_separated
3315 && raw_text.contains('_'))
3316 .then(|| raw_text.replace('_', ""));
3317 if let Some(text) = normalized.or(raw_owned) {
3318 self.add_token_with_text(TokenType::Number, text);
3319 } else {
3320 self.add_token(TokenType::Number);
3321 }
3322 Ok(())
3323 }
3324
3325 #[inline]
3328 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3329 if text.len() > 128 {
3330 return TokenType::Var;
3331 }
3332 let mut buf = [0u8; 128];
3333 for (i, b) in text.bytes().enumerate() {
3334 buf[i] = b.to_ascii_uppercase();
3335 }
3336 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3337 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3338 } else {
3339 TokenType::Var
3340 }
3341 }
3342
3343 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3344 let first_char = self.peek();
3346 if !first_char.is_alphanumeric() && first_char != '_' {
3347 let c = self.advance();
3349 return Err(Error::tokenize(
3350 format!("Unexpected character: '{}'", c),
3351 self.line,
3352 self.column,
3353 self.start,
3354 self.current,
3355 ));
3356 }
3357
3358 if !self.advance_ascii_identifier() {
3359 while !self.is_at_end() {
3360 let c = self.peek();
3361 if c == '#' {
3365 let next_c = if self.current + 1 < self.size {
3366 self.char_at(self.current + 1)
3367 } else {
3368 '\0'
3369 };
3370 if next_c == '>' || next_c == '-' {
3371 break; }
3373 self.advance();
3374 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3375 self.advance();
3376 } else {
3377 break;
3378 }
3379 }
3380 }
3381
3382 let source_text = self
3383 .cursor
3384 .source_range(self.source, self.start, self.current);
3385 let owned_text = source_text
3386 .is_none()
3387 .then(|| self.text_from_range(self.start, self.current));
3388 let text = source_text.unwrap_or_else(|| {
3389 owned_text
3390 .as_deref()
3391 .expect("non-ASCII identifiers own their text")
3392 });
3393
3394 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3396 self.advance(); self.add_token(TokenType::Neq);
3398 return Ok(());
3399 }
3400
3401 let next_char = self.peek();
3404 let is_single_quote = next_char == '\'';
3405 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3406 let is_double_quote_for_raw = next_char == '"';
3409
3410 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3413 let quote_char = if is_single_quote { '\'' } else { '"' };
3416 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3420 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3424 self.add_token_with_text(TokenType::RawString, string_value);
3425 } else {
3426 let string_value = self.scan_raw_string_content(quote_char)?;
3427 self.add_token_with_text(TokenType::RawString, string_value);
3428 }
3429 return Ok(());
3430 }
3431
3432 if is_single_quote || is_double_quote {
3433 if text.eq_ignore_ascii_case("N") {
3434 self.advance(); let string_value = if is_single_quote {
3437 self.scan_string_content()?
3438 } else {
3439 self.scan_double_quoted_string_content()?
3440 };
3441 self.add_token_with_text(TokenType::NationalString, string_value);
3442 return Ok(());
3443 } else if text.eq_ignore_ascii_case("E") {
3444 let lowercase = text == "e";
3448 let prefix = if lowercase { "e:" } else { "E:" };
3449 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3451 self.add_token_with_text(
3452 TokenType::EscapeString,
3453 format!("{}{}", prefix, string_value),
3454 );
3455 return Ok(());
3456 } else if text.eq_ignore_ascii_case("X") {
3457 self.advance(); let string_value = if is_single_quote {
3460 self.scan_string_content()?
3461 } else {
3462 self.scan_double_quoted_string_content()?
3463 };
3464 self.add_token_with_text(TokenType::HexString, string_value);
3465 return Ok(());
3466 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3467 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3470 self.add_token_with_text(TokenType::ByteString, string_value);
3471 return Ok(());
3472 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3473 self.advance(); let string_value = self.scan_string_content()?;
3477 if self.config.b_prefix_is_byte_string {
3478 self.add_token_with_text(TokenType::ByteString, string_value);
3479 } else {
3480 self.add_token_with_text(TokenType::BitString, string_value);
3481 }
3482 return Ok(());
3483 }
3484 }
3485
3486 if text.eq_ignore_ascii_case("U")
3488 && self.peek() == '&'
3489 && self.current + 1 < self.size
3490 && self.char_at(self.current + 1) == '\''
3491 {
3492 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3495 self.add_token_with_text(TokenType::UnicodeString, string_value);
3496 return Ok(());
3497 }
3498
3499 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3500
3501 if let Some(text) = owned_text {
3502 self.add_token_with_text(token_type, text);
3503 } else {
3504 self.add_token_from_source(token_type, self.start, self.current);
3505 }
3506 Ok(())
3507 }
3508
3509 fn scan_string_content_with_escapes(
3513 &mut self,
3514 force_backslash_escapes: bool,
3515 ) -> Result<String> {
3516 let use_backslash_escapes =
3517 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3518 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3519 {
3520 return Ok(self.text_from_range(start, end));
3521 }
3522 let mut value = String::new();
3523
3524 while !self.is_at_end() {
3525 let c = self.peek();
3526 if c == '\'' {
3527 if self.peek_next() == '\'' {
3528 value.push('\'');
3530 self.advance();
3531 self.advance();
3532 } else {
3533 break;
3534 }
3535 } else if c == '\\' && use_backslash_escapes {
3536 value.push(self.advance());
3538 if !self.is_at_end() {
3539 value.push(self.advance());
3540 }
3541 } else {
3542 value.push(self.advance());
3543 }
3544 }
3545
3546 if self.is_at_end() {
3547 return Err(Error::tokenize(
3548 "Unterminated string",
3549 self.line,
3550 self.column,
3551 self.start,
3552 self.current,
3553 ));
3554 }
3555
3556 self.advance(); Ok(value)
3558 }
3559
3560 fn scan_string_content(&mut self) -> Result<String> {
3562 self.scan_string_content_with_escapes(false)
3563 }
3564
3565 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3568 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3569 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3570 {
3571 return Ok(self.text_from_range(start, end));
3572 }
3573 let mut value = String::new();
3574
3575 while !self.is_at_end() {
3576 let c = self.peek();
3577 if c == '"' {
3578 if self.peek_next() == '"' {
3579 value.push('"');
3581 self.advance();
3582 self.advance();
3583 } else {
3584 break;
3585 }
3586 } else if c == '\\' && use_backslash_escapes {
3587 self.advance(); if !self.is_at_end() {
3590 let escaped = self.advance();
3591 match escaped {
3592 'n' => value.push('\n'),
3593 'r' => value.push('\r'),
3594 't' => value.push('\t'),
3595 '0' => value.push('\0'),
3596 '\\' => value.push('\\'),
3597 '"' => value.push('"'),
3598 '\'' => value.push('\''),
3599 'x' => {
3600 let mut hex = String::new();
3602 for _ in 0..2 {
3603 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3604 hex.push(self.advance());
3605 }
3606 }
3607 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3608 value.push(byte as char);
3609 } else {
3610 value.push('\\');
3612 value.push('x');
3613 value.push_str(&hex);
3614 }
3615 }
3616 _ => {
3617 value.push('\\');
3619 value.push(escaped);
3620 }
3621 }
3622 }
3623 } else {
3624 value.push(self.advance());
3625 }
3626 }
3627
3628 if self.is_at_end() {
3629 return Err(Error::tokenize(
3630 "Unterminated double-quoted string",
3631 self.line,
3632 self.column,
3633 self.start,
3634 self.current,
3635 ));
3636 }
3637
3638 self.advance(); Ok(value)
3640 }
3641
3642 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3647 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3648 quote_char,
3649 self.config.string_escapes_allowed_in_raw_strings,
3650 ) {
3651 return Ok(self.text_from_range(start, end));
3652 }
3653 let mut value = String::new();
3654
3655 while !self.is_at_end() {
3656 let c = self.peek();
3657 if c == quote_char {
3658 if self.peek_next() == quote_char {
3659 value.push(quote_char);
3661 self.advance();
3662 self.advance();
3663 } else {
3664 break;
3665 }
3666 } else if c == '\\'
3667 && self.peek_next() == quote_char
3668 && self.config.string_escapes_allowed_in_raw_strings
3669 {
3670 value.push('\\');
3673 value.push(quote_char);
3674 self.advance(); self.advance(); } else {
3677 value.push(self.advance());
3679 }
3680 }
3681
3682 if self.is_at_end() {
3683 return Err(Error::tokenize(
3684 "Unterminated raw string",
3685 self.line,
3686 self.column,
3687 self.start,
3688 self.current,
3689 ));
3690 }
3691
3692 self.advance(); Ok(value)
3694 }
3695
3696 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3699 let mut value = String::new();
3700
3701 while !self.is_at_end() {
3702 if self.peek() == quote_char {
3703 let mut quote_count = 0;
3704 while self.current + quote_count < self.size
3705 && self.char_at(self.current + quote_count) == quote_char
3706 {
3707 quote_count += 1;
3708 }
3709 if quote_count >= 3 {
3710 for _ in 0..quote_count - 3 {
3713 value.push(quote_char);
3714 }
3715 for _ in 0..quote_count {
3716 self.advance();
3717 }
3718 return Ok(value);
3719 }
3720 }
3721 let ch = self.advance();
3723 value.push(ch);
3724 }
3725
3726 Err(Error::tokenize(
3727 "Unterminated raw triple-quoted string",
3728 self.line,
3729 self.column,
3730 self.start,
3731 self.current,
3732 ))
3733 }
3734
3735 fn scan_dollar_identifier(&mut self) -> Result<()> {
3740 self.advance();
3742
3743 while !self.is_at_end() {
3745 let c = self.peek();
3746 if c.is_alphanumeric() || c == '_' || c == '$' {
3747 self.advance();
3748 } else {
3749 break;
3750 }
3751 }
3752
3753 self.add_token(TokenType::Var);
3754 Ok(())
3755 }
3756
3757 fn scan_tsql_identifier(&mut self) -> Result<()> {
3758 let first = self.advance();
3760
3761 if first == '#' && self.peek() == '#' {
3763 self.advance();
3764 }
3765
3766 if !self.advance_ascii_identifier() {
3768 while !self.is_at_end() {
3769 let c = self.peek();
3770 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3771 self.advance();
3772 } else {
3773 break;
3774 }
3775 }
3776 }
3777
3778 self.add_token(TokenType::Var);
3780 Ok(())
3781 }
3782
3783 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3787 let len = self.tokens.len();
3788 if len < 3 {
3789 return None;
3790 }
3791
3792 let last = &self.tokens[len - 1];
3794 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3795 return None;
3796 }
3797 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3798 return None;
3799 }
3800
3801 let format_tok = &self.tokens[len - 2];
3803 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3804 return None;
3805 }
3806
3807 let has_insert = self.tokens[..len - 2]
3809 .iter()
3810 .rev()
3811 .take(20)
3812 .any(|t| t.token_type() == TokenType::Insert);
3813 if !has_insert {
3814 return None;
3815 }
3816
3817 let raw_start = self.current;
3821 while !self.is_at_end() {
3822 let c = self.peek();
3823 if c == '\n' {
3824 let saved = self.current;
3826 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3829 self.advance();
3830 }
3831 if self.is_at_end() || self.peek() == '\n' {
3832 let raw = self.text_from_range(raw_start, saved);
3835 return Some(raw.trim().to_string());
3836 }
3837 } else {
3839 self.advance();
3840 }
3841 }
3842
3843 let raw = self.text_from_range(raw_start, self.current);
3845 let trimmed = raw.trim().to_string();
3846 if trimmed.is_empty() {
3847 None
3848 } else {
3849 Some(trimmed)
3850 }
3851 }
3852
3853 fn add_token(&mut self, token_type: TokenType) {
3854 self.add_token_from_source(token_type, self.start, self.current);
3855 }
3856
3857 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3858 let span = Span::new(self.start, self.current, self.line, self.column);
3859 if let Some(stats) = &mut self.guard_stats {
3860 stats.observe(token_type, span);
3861 }
3862 let mut token = if self
3863 .cursor
3864 .source_range(self.source, text_start, text_end)
3865 .is_some()
3866 {
3867 T::from_source(
3868 token_type,
3869 self.source,
3870 text_start,
3871 text_end,
3872 span,
3873 self.shared_source.as_ref(),
3874 )
3875 } else {
3876 T::from_owned(
3877 token_type,
3878 self.cursor
3879 .text_from_range(self.source, text_start, text_end),
3880 span,
3881 )
3882 };
3883 token.comments_mut().append(&mut self.comments);
3884 self.tokens.push(token);
3885 }
3886
3887 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3888 let span = Span::new(self.start, self.current, self.line, self.column);
3889 if let Some(stats) = &mut self.guard_stats {
3890 stats.observe(token_type, span);
3891 }
3892 let mut token = T::from_owned(token_type, text, span);
3893 token.comments_mut().append(&mut self.comments);
3894 self.tokens.push(token);
3895 }
3896}
3897
3898#[cfg(test)]
3899mod tests {
3900 use super::*;
3901
3902 #[test]
3903 fn ascii_fast_path_matches_character_buffer_path() {
3904 let tokenizer = Tokenizer::default();
3905 let inputs = [
3906 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3907 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3908 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3909 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3910 ];
3911
3912 for sql in inputs {
3913 assert_eq!(
3914 tokenizer.tokenize(sql).unwrap(),
3915 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3916 "tokenization differs for {sql}"
3917 );
3918 }
3919 }
3920
3921 #[test]
3922 fn parser_tokens_match_public_tokens() {
3923 let tokenizer = Tokenizer::default();
3924 let inputs = [
3925 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3926 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3927 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3928 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3929 ];
3930
3931 for sql in inputs {
3932 let public = tokenizer.tokenize(sql).unwrap();
3933 let source: Arc<str> = Arc::from(sql);
3934 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3935 let materialized = parser
3936 .iter()
3937 .map(|token| Token {
3938 token_type: token.token_type,
3939 text: token.text_owned(),
3940 span: token.span,
3941 comments: token.comments.clone(),
3942 trailing_comments: token.trailing_comments.clone(),
3943 })
3944 .collect::<Vec<_>>();
3945
3946 assert_eq!(
3947 materialized, public,
3948 "parser tokenization differs for {sql}"
3949 );
3950 assert_eq!(stats.token_count, public.len());
3951 }
3952 }
3953
3954 #[test]
3955 fn parser_tokens_borrow_unchanged_ascii_text() {
3956 let tokenizer = Tokenizer::default();
3957 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3958 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3959
3960 assert!(tokens
3961 .iter()
3962 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3963 }
3964
3965 #[test]
3966 fn test_simple_select() {
3967 let tokenizer = Tokenizer::default();
3968 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3969
3970 assert_eq!(tokens.len(), 2);
3971 assert_eq!(tokens[0].token_type, TokenType::Select);
3972 assert_eq!(tokens[1].token_type, TokenType::Number);
3973 assert_eq!(tokens[1].text, "1");
3974 }
3975
3976 #[test]
3977 fn test_select_with_identifier() {
3978 let tokenizer = Tokenizer::default();
3979 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
3980
3981 assert_eq!(tokens.len(), 6);
3982 assert_eq!(tokens[0].token_type, TokenType::Select);
3983 assert_eq!(tokens[1].token_type, TokenType::Var);
3984 assert_eq!(tokens[1].text, "a");
3985 assert_eq!(tokens[2].token_type, TokenType::Comma);
3986 assert_eq!(tokens[3].token_type, TokenType::Var);
3987 assert_eq!(tokens[3].text, "b");
3988 assert_eq!(tokens[4].token_type, TokenType::From);
3989 assert_eq!(tokens[5].token_type, TokenType::Var);
3990 assert_eq!(tokens[5].text, "t");
3991 }
3992
3993 #[test]
3994 fn test_string_literal() {
3995 let tokenizer = Tokenizer::default();
3996 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
3997
3998 assert_eq!(tokens.len(), 2);
3999 assert_eq!(tokens[1].token_type, TokenType::String);
4000 assert_eq!(tokens[1].text, "hello");
4001 }
4002
4003 #[test]
4004 fn test_escaped_string() {
4005 let tokenizer = Tokenizer::default();
4006 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
4007
4008 assert_eq!(tokens.len(), 2);
4009 assert_eq!(tokens[1].token_type, TokenType::String);
4010 assert_eq!(tokens[1].text, "it's");
4011 }
4012
4013 #[test]
4014 fn test_escape_follow_chars_gate_builtin_decoding() {
4015 let mut config = TokenizerConfig::default();
4016 config.string_escapes.push('\\');
4017 config.escape_follow_chars = vec!['n'];
4018 let tokenizer = Tokenizer::new(config);
4019 let tokens = tokenizer.tokenize(r"SELECT '\n\a\f\Z\x21'").unwrap();
4020
4021 assert_eq!(tokens[1].text, "\nafZx21");
4022 }
4023
4024 #[test]
4025 fn test_configured_numeric_escapes_require_complete_sequences() {
4026 let mut config = TokenizerConfig::default();
4027 config.string_escapes.push('\\');
4028 config.escape_follow_chars = vec!['0', '1', '2', '3', '4', '5', '6', '7', 'x', 'u'];
4029 let tokenizer = Tokenizer::new(config);
4030 let tokens = tokenizer
4031 .tokenize(r"SELECT '\041\x21\u26c4-\777\x2\u26c'")
4032 .unwrap();
4033
4034 assert_eq!(tokens[1].text, "!!\u{26c4}-777x2u26c");
4035 }
4036
4037 #[test]
4038 fn test_terminal_backslash_quote_recovery() {
4039 let mut config = TokenizerConfig::default();
4040 config.string_escapes.push('\\');
4041 config.recover_terminal_backslash_quote = true;
4042 let tokenizer = Tokenizer::new(config);
4043 let tokens = tokenizer
4044 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4045 .unwrap();
4046
4047 assert_eq!(tokens.len(), 8);
4048 assert_eq!(tokens[3].token_type, TokenType::String);
4049 assert_eq!(tokens[3].text, "a\\");
4050 assert_eq!(tokens[4].token_type, TokenType::Or);
4051 }
4052
4053 #[test]
4054 fn test_comments() {
4055 let tokenizer = Tokenizer::default();
4056 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4057
4058 assert_eq!(tokens.len(), 2);
4059 assert_eq!(tokens[0].trailing_comments.len(), 1);
4062 assert_eq!(tokens[0].trailing_comments[0], " comment");
4063 }
4064
4065 #[test]
4066 fn test_comment_in_and_chain() {
4067 use crate::generator::Generator;
4068 use crate::parser::Parser;
4069
4070 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4072 let ast = Parser::parse_sql(sql).unwrap();
4073 let mut gen = Generator::default();
4074 let output = gen.generate(&ast[0]).unwrap();
4075 assert_eq!(
4076 output,
4077 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4078 );
4079 }
4080
4081 #[test]
4082 fn test_operators() {
4083 let tokenizer = Tokenizer::default();
4084 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4085
4086 assert_eq!(tokens.len(), 5);
4087 assert_eq!(tokens[0].token_type, TokenType::Number);
4088 assert_eq!(tokens[1].token_type, TokenType::Plus);
4089 assert_eq!(tokens[2].token_type, TokenType::Number);
4090 assert_eq!(tokens[3].token_type, TokenType::Star);
4091 assert_eq!(tokens[4].token_type, TokenType::Number);
4092 }
4093
4094 #[test]
4095 fn test_comparison_operators() {
4096 let tokenizer = Tokenizer::default();
4097 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4098
4099 assert_eq!(tokens[1].token_type, TokenType::Lte);
4100 assert_eq!(tokens[3].token_type, TokenType::Gte);
4101 assert_eq!(tokens[5].token_type, TokenType::Neq);
4102 }
4103
4104 #[test]
4105 fn test_national_string() {
4106 let tokenizer = Tokenizer::default();
4107 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4108
4109 assert_eq!(
4110 tokens.len(),
4111 1,
4112 "Expected 1 token for N'abc', got {:?}",
4113 tokens
4114 );
4115 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4116 assert_eq!(tokens[0].text, "abc");
4117 }
4118
4119 #[test]
4120 fn test_hex_string() {
4121 let tokenizer = Tokenizer::default();
4122 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4123
4124 assert_eq!(
4125 tokens.len(),
4126 1,
4127 "Expected 1 token for X'ABCD', got {:?}",
4128 tokens
4129 );
4130 assert_eq!(tokens[0].token_type, TokenType::HexString);
4131 assert_eq!(tokens[0].text, "ABCD");
4132 }
4133
4134 #[test]
4135 fn test_bit_string() {
4136 let tokenizer = Tokenizer::default();
4137 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4138
4139 assert_eq!(
4140 tokens.len(),
4141 1,
4142 "Expected 1 token for B'01010', got {:?}",
4143 tokens
4144 );
4145 assert_eq!(tokens[0].token_type, TokenType::BitString);
4146 assert_eq!(tokens[0].text, "01010");
4147 }
4148
4149 #[test]
4150 fn test_trailing_dot_number() {
4151 let tokenizer = Tokenizer::default();
4152
4153 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4155 assert_eq!(
4156 tokens.len(),
4157 2,
4158 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4159 tokens
4160 );
4161 assert_eq!(tokens[1].token_type, TokenType::Number);
4162 assert_eq!(tokens[1].text, "1.");
4163
4164 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4166 assert_eq!(tokens[1].text, "1.5");
4167
4168 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4171 assert_eq!(
4172 tokens.len(),
4173 3,
4174 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4175 tokens
4176 );
4177 assert_eq!(tokens[1].token_type, TokenType::Number);
4178 assert_eq!(tokens[1].text, "1.");
4179 assert_eq!(tokens[2].token_type, TokenType::Var);
4180
4181 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4183 assert_eq!(tokens[1].token_type, TokenType::Number);
4184 assert_eq!(tokens[1].text, "1");
4185 assert_eq!(tokens[2].token_type, TokenType::Dot);
4186 assert_eq!(tokens[3].token_type, TokenType::Dot);
4187 assert_eq!(tokens[4].token_type, TokenType::Number);
4188 assert_eq!(tokens[4].text, "2");
4189 }
4190
4191 #[test]
4192 fn test_leading_dot_number() {
4193 let tokenizer = Tokenizer::default();
4194
4195 let tokens = tokenizer.tokenize(".25").unwrap();
4197 assert_eq!(
4198 tokens.len(),
4199 1,
4200 "Expected 1 token for '.25', got {:?}",
4201 tokens
4202 );
4203 assert_eq!(tokens[0].token_type, TokenType::Number);
4204 assert_eq!(tokens[0].text, ".25");
4205
4206 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4208 assert_eq!(
4209 tokens.len(),
4210 4,
4211 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4212 tokens
4213 );
4214 assert_eq!(tokens[0].token_type, TokenType::Sample);
4215 assert_eq!(tokens[1].token_type, TokenType::LParen);
4216 assert_eq!(tokens[2].token_type, TokenType::Number);
4217 assert_eq!(tokens[2].text, ".25");
4218 assert_eq!(tokens[3].token_type, TokenType::RParen);
4219
4220 let tokens = tokenizer.tokenize(".5e10").unwrap();
4222 assert_eq!(
4223 tokens.len(),
4224 1,
4225 "Expected 1 token for '.5e10', got {:?}",
4226 tokens
4227 );
4228 assert_eq!(tokens[0].token_type, TokenType::Number);
4229 assert_eq!(tokens[0].text, ".5e10");
4230
4231 let tokens = tokenizer.tokenize("a.b").unwrap();
4233 assert_eq!(
4234 tokens.len(),
4235 3,
4236 "Expected 3 tokens for 'a.b', got {:?}",
4237 tokens
4238 );
4239 assert_eq!(tokens[1].token_type, TokenType::Dot);
4240 }
4241
4242 #[test]
4243 fn test_unrecognized_character() {
4244 let tokenizer = Tokenizer::default();
4245
4246 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4248 assert!(
4249 result.is_ok(),
4250 "Curly quotes should be tokenized as strings"
4251 );
4252
4253 let result = tokenizer.tokenize("SELECT • FROM t");
4255 assert!(result.is_err());
4256 }
4257
4258 #[test]
4259 fn test_colon_eq_tokenization() {
4260 let tokenizer = Tokenizer::default();
4261
4262 let tokens = tokenizer.tokenize("a := 1").unwrap();
4264 assert_eq!(tokens.len(), 3);
4265 assert_eq!(tokens[0].token_type, TokenType::Var);
4266 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4267 assert_eq!(tokens[2].token_type, TokenType::Number);
4268
4269 let tokens = tokenizer.tokenize("a:b").unwrap();
4271 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4272 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4273
4274 let tokens = tokenizer.tokenize("a::INT").unwrap();
4276 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4277 }
4278
4279 #[test]
4280 fn test_colon_eq_parsing() {
4281 use crate::generator::Generator;
4282 use crate::parser::Parser;
4283
4284 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4286 .expect("Failed to parse MySQL @var := expr");
4287 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4288 assert_eq!(output, "SELECT @var1 := 1, @var2");
4289
4290 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4292 .expect("Failed to parse MySQL @var2 := @var1");
4293 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4294 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4295
4296 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4298 .expect("Failed to parse MySQL @var := COUNT(*)");
4299 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4300 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4301
4302 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4304 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4305 assert_eq!(output, "SET @var1 = 1");
4306
4307 let ast =
4309 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4310 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4311 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4312
4313 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4315 .expect("Failed to parse UNNEST with :=");
4316 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4317 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4318
4319 let ast =
4321 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4322 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4323 assert_eq!(output, "SELECT 1 AS foo");
4324
4325 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4327 .expect("Failed to parse DuckDB multiple prefix aliases");
4328 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4329 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4330 }
4331
4332 #[test]
4333 fn test_colon_eq_dialect_roundtrip() {
4334 use crate::dialects::{Dialect, DialectType};
4335
4336 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4337 let d = Dialect::get(dialect);
4338 let ast = d
4339 .parse(sql)
4340 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4341 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4342 let transformed = d
4343 .transform(ast[0].clone())
4344 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4345 let output = d
4346 .generate(&transformed)
4347 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4348 let expected = expected.unwrap_or(sql);
4349 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4350 }
4351
4352 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4354 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4355 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4356 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4357
4358 check(
4360 DialectType::DuckDB,
4361 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4362 None,
4363 );
4364 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4365
4366 {
4369 let d = Dialect::get(DialectType::DuckDB);
4370 let ast = d
4371 .parse("STRUCT_PACK(a := 'b')::json")
4372 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4373 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4374 }
4375
4376 check(
4378 DialectType::DuckDB,
4379 "SELECT foo: 1",
4380 Some("SELECT 1 AS foo"),
4381 );
4382 check(
4383 DialectType::DuckDB,
4384 "SELECT foo: 1, bar: 2, baz: 3",
4385 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4386 );
4387 }
4388
4389 #[test]
4390 fn test_comment_roundtrip() {
4391 use crate::generator::Generator;
4392 use crate::parser::Parser;
4393
4394 fn check_roundtrip(sql: &str) -> Option<String> {
4395 let ast = match Parser::parse_sql(sql) {
4396 Ok(a) => a,
4397 Err(e) => return Some(format!("Parse error: {:?}", e)),
4398 };
4399 if ast.is_empty() {
4400 return Some("Empty AST".to_string());
4401 }
4402 let mut generator = Generator::default();
4403 let output = match generator.generate(&ast[0]) {
4404 Ok(o) => o,
4405 Err(e) => return Some(format!("Gen error: {:?}", e)),
4406 };
4407 if output == sql {
4408 None
4409 } else {
4410 Some(format!(
4411 "Mismatch:\n input: {}\n output: {}",
4412 sql, output
4413 ))
4414 }
4415 }
4416
4417 let tests = vec![
4418 "SELECT c /* c1 */ AS alias /* c2 */",
4424 "SELECT a /* x */, b /* x */",
4426 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4428 "SELECT * FROM foo /* x */, bla /* x */",
4430 "SELECT 1 /* comment */ + 1",
4432 "SELECT 1 /* c1 */ + 2 /* c2 */",
4433 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4434 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4436 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4438 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4440 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4442 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4444 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4445 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4446 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4447 "/* comment */ CREATE TABLE foo AS SELECT 1",
4448 "INSERT INTO foo SELECT * FROM bar /* comment */",
4450 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4452 ];
4453
4454 let mut failures = Vec::new();
4455 for sql in tests {
4456 if let Some(e) = check_roundtrip(sql) {
4457 failures.push(e);
4458 }
4459 }
4460
4461 if !failures.is_empty() {
4462 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4463 }
4464 }
4465
4466 #[test]
4467 fn test_dollar_quoted_string_parsing() {
4468 use crate::dialects::{Dialect, DialectType};
4469
4470 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4472 assert_eq!(tag, Some("FOO".to_string()));
4473 assert_eq!(content, "content here");
4474
4475 let (tag, content) = super::parse_dollar_string_token("just content");
4476 assert_eq!(tag, None);
4477 assert_eq!(content, "just content");
4478
4479 fn check_databricks(sql: &str, expected: Option<&str>) {
4481 let d = Dialect::get(DialectType::Databricks);
4482 let ast = d
4483 .parse(sql)
4484 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4485 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4486 let transformed = d
4487 .transform(ast[0].clone())
4488 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4489 let output = d
4490 .generate(&transformed)
4491 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4492 let expected = expected.unwrap_or(sql);
4493 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4494 }
4495
4496 check_databricks(
4498 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4499 None
4500 );
4501
4502 check_databricks(
4504 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4505 None
4506 );
4507 }
4508
4509 #[test]
4510 fn test_numeric_underscore_stripping() {
4511 let mut config = TokenizerConfig::default();
4513 config.numbers_can_be_underscore_separated = true;
4514 let tokenizer = Tokenizer::new(config);
4515
4516 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4518 assert_eq!(tokens[1].token_type, TokenType::Number);
4519 assert_eq!(tokens[1].text, "12345");
4520
4521 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4523 assert_eq!(tokens[1].token_type, TokenType::Number);
4524 assert_eq!(tokens[1].text, "20000");
4525
4526 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4528 assert_eq!(tokens[1].token_type, TokenType::Number);
4529 assert_eq!(tokens[1].text, "12E+10");
4530
4531 let default_tokenizer = Tokenizer::default();
4533 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4534 assert_eq!(tokens[1].token_type, TokenType::Number);
4535 assert_eq!(tokens[1].text, "1_2_3_4_5");
4536 }
4537}