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("SEED".to_string(), TokenType::Seed);
1305 keywords.insert("REPEATABLE".to_string(), TokenType::Repeatable);
1306 keywords.insert("TIES".to_string(), TokenType::Ties);
1307 keywords.insert("LATERAL".to_string(), TokenType::Lateral);
1308 keywords.insert("LAMBDA".to_string(), TokenType::Lambda);
1309 keywords.insert("APPLY".to_string(), TokenType::Apply);
1310 keywords.insert("CONNECT".to_string(), TokenType::Connect);
1312 keywords.insert("CLUSTER".to_string(), TokenType::Cluster);
1314 keywords.insert("DISTRIBUTE".to_string(), TokenType::Distribute);
1315 keywords.insert("SORT".to_string(), TokenType::Sort);
1316 keywords.insert("PIVOT".to_string(), TokenType::Pivot);
1317 keywords.insert("PREWHERE".to_string(), TokenType::Prewhere);
1318 keywords.insert("UNPIVOT".to_string(), TokenType::Unpivot);
1319 keywords.insert("FOR".to_string(), TokenType::For);
1320 keywords.insert("ANY".to_string(), TokenType::Any);
1321 keywords.insert("SOME".to_string(), TokenType::Some);
1322 keywords.insert("ASOF".to_string(), TokenType::AsOf);
1323 keywords.insert("PERCENT".to_string(), TokenType::Percent);
1324 keywords.insert("EXCLUDE".to_string(), TokenType::Exclude);
1325 keywords.insert("NO".to_string(), TokenType::No);
1326 keywords.insert("OTHERS".to_string(), TokenType::Others);
1327 keywords.insert("OPERATOR".to_string(), TokenType::Operator);
1329 keywords.insert("SCHEMA".to_string(), TokenType::Schema);
1331 keywords.insert("NAMESPACE".to_string(), TokenType::Namespace);
1332 keywords.insert("DATABASE".to_string(), TokenType::Database);
1333 keywords.insert("FUNCTION".to_string(), TokenType::Function);
1334 keywords.insert("PROCEDURE".to_string(), TokenType::Procedure);
1335 keywords.insert("PROC".to_string(), TokenType::Procedure);
1336 keywords.insert("SEQUENCE".to_string(), TokenType::Sequence);
1337 keywords.insert("TRIGGER".to_string(), TokenType::Trigger);
1338 keywords.insert("TYPE".to_string(), TokenType::Type);
1339 keywords.insert("DOMAIN".to_string(), TokenType::Domain);
1340 keywords.insert("RETURNS".to_string(), TokenType::Returns);
1341 keywords.insert("RETURNING".to_string(), TokenType::Returning);
1342 keywords.insert("LANGUAGE".to_string(), TokenType::Language);
1343 keywords.insert("ROLLBACK".to_string(), TokenType::Rollback);
1344 keywords.insert("COMMIT".to_string(), TokenType::Commit);
1345 keywords.insert("BEGIN".to_string(), TokenType::Begin);
1346 keywords.insert("DESCRIBE".to_string(), TokenType::Describe);
1347 keywords.insert("PREPARE".to_string(), TokenType::Prepare);
1348 keywords.insert("PRESERVE".to_string(), TokenType::Preserve);
1349 keywords.insert("TRANSACTION".to_string(), TokenType::Transaction);
1350 keywords.insert("SAVEPOINT".to_string(), TokenType::Savepoint);
1351 keywords.insert("BODY".to_string(), TokenType::Body);
1352 keywords.insert("INCREMENT".to_string(), TokenType::Increment);
1353 keywords.insert("MINVALUE".to_string(), TokenType::Minvalue);
1354 keywords.insert("MAXVALUE".to_string(), TokenType::Maxvalue);
1355 keywords.insert("CYCLE".to_string(), TokenType::Cycle);
1356 keywords.insert("NOCYCLE".to_string(), TokenType::NoCycle);
1357 keywords.insert("PRIOR".to_string(), TokenType::Prior);
1358 keywords.insert("MATCH".to_string(), TokenType::Match);
1360 keywords.insert("MATCH_RECOGNIZE".to_string(), TokenType::MatchRecognize);
1361 keywords.insert("MEASURES".to_string(), TokenType::Measures);
1362 keywords.insert("PATTERN".to_string(), TokenType::Pattern);
1363 keywords.insert("DEFINE".to_string(), TokenType::Define);
1364 keywords.insert("RUNNING".to_string(), TokenType::Running);
1365 keywords.insert("FINAL".to_string(), TokenType::Final);
1366 keywords.insert("OWNED".to_string(), TokenType::Owned);
1367 keywords.insert("AFTER".to_string(), TokenType::After);
1368 keywords.insert("BEFORE".to_string(), TokenType::Before);
1369 keywords.insert("INSTEAD".to_string(), TokenType::Instead);
1370 keywords.insert("EACH".to_string(), TokenType::Each);
1371 keywords.insert("STATEMENT".to_string(), TokenType::Statement);
1372 keywords.insert("REFERENCING".to_string(), TokenType::Referencing);
1373 keywords.insert("OLD".to_string(), TokenType::Old);
1374 keywords.insert("NEW".to_string(), TokenType::New);
1375 keywords.insert("OF".to_string(), TokenType::Of);
1376 keywords.insert("CHECK".to_string(), TokenType::Check);
1377 keywords.insert("START".to_string(), TokenType::Start);
1378 keywords.insert("ENUM".to_string(), TokenType::Enum);
1379 keywords.insert("AUTHORIZATION".to_string(), TokenType::Authorization);
1380 keywords.insert("RESTART".to_string(), TokenType::Restart);
1381 keywords.insert("DATE".to_string(), TokenType::Date);
1383 keywords.insert("TIME".to_string(), TokenType::Time);
1384 keywords.insert("TIMESTAMP".to_string(), TokenType::Timestamp);
1385 keywords.insert("DATETIME".to_string(), TokenType::DateTime);
1386 keywords.insert("GENERATED".to_string(), TokenType::Generated);
1387 keywords.insert("IDENTITY".to_string(), TokenType::Identity);
1388 keywords.insert("ALWAYS".to_string(), TokenType::Always);
1389 keywords.insert("LOAD".to_string(), TokenType::Load);
1391 keywords.insert("LOCAL".to_string(), TokenType::Local);
1392 keywords.insert("INPATH".to_string(), TokenType::Inpath);
1393 keywords.insert("INPUTFORMAT".to_string(), TokenType::InputFormat);
1394 keywords.insert("SERDE".to_string(), TokenType::Serde);
1395 keywords.insert("SERDEPROPERTIES".to_string(), TokenType::SerdeProperties);
1396 keywords.insert("FORMAT".to_string(), TokenType::Format);
1397 keywords.insert("PRAGMA".to_string(), TokenType::Pragma);
1399 keywords.insert("SHOW".to_string(), TokenType::Show);
1401 keywords.insert("SIBLINGS".to_string(), TokenType::Siblings);
1403 keywords.insert("COPY".to_string(), TokenType::Copy);
1405 keywords.insert("PUT".to_string(), TokenType::Put);
1406 keywords.insert("GET".to_string(), TokenType::Get);
1407 keywords.insert("EXEC".to_string(), TokenType::Execute);
1409 keywords.insert("EXECUTE".to_string(), TokenType::Execute);
1410 keywords.insert("ISNULL".to_string(), TokenType::IsNull);
1412 keywords.insert("NOTNULL".to_string(), TokenType::NotNull);
1413 keywords
1414});
1415
1416static DEFAULT_SINGLE_TOKENS: LazyLock<HashMap<char, TokenType>> = LazyLock::new(|| {
1417 let mut single_tokens = HashMap::with_capacity(30);
1418 single_tokens.insert('(', TokenType::LParen);
1419 single_tokens.insert(')', TokenType::RParen);
1420 single_tokens.insert('[', TokenType::LBracket);
1421 single_tokens.insert(']', TokenType::RBracket);
1422 single_tokens.insert('{', TokenType::LBrace);
1423 single_tokens.insert('}', TokenType::RBrace);
1424 single_tokens.insert(',', TokenType::Comma);
1425 single_tokens.insert('.', TokenType::Dot);
1426 single_tokens.insert(';', TokenType::Semicolon);
1427 single_tokens.insert('+', TokenType::Plus);
1428 single_tokens.insert('-', TokenType::Dash);
1429 single_tokens.insert('*', TokenType::Star);
1430 single_tokens.insert('/', TokenType::Slash);
1431 single_tokens.insert('%', TokenType::Percent);
1432 single_tokens.insert('&', TokenType::Amp);
1433 single_tokens.insert('|', TokenType::Pipe);
1434 single_tokens.insert('^', TokenType::Caret);
1435 single_tokens.insert('~', TokenType::Tilde);
1436 single_tokens.insert('<', TokenType::Lt);
1437 single_tokens.insert('>', TokenType::Gt);
1438 single_tokens.insert('=', TokenType::Eq);
1439 single_tokens.insert('!', TokenType::Exclamation);
1440 single_tokens.insert(':', TokenType::Colon);
1441 single_tokens.insert('@', TokenType::DAt);
1442 single_tokens.insert('#', TokenType::Hash);
1443 single_tokens.insert('$', TokenType::Dollar);
1444 single_tokens.insert('?', TokenType::Parameter);
1445 single_tokens
1446});
1447
1448static DEFAULT_QUOTES: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
1449 let mut quotes = HashMap::with_capacity(4);
1450 quotes.insert("'".to_string(), "'".to_string());
1451 quotes.insert("\"\"\"".to_string(), "\"\"\"".to_string());
1453 quotes
1454});
1455
1456static DEFAULT_IDENTIFIERS: LazyLock<HashMap<char, char>> = LazyLock::new(|| {
1457 let mut identifiers = HashMap::with_capacity(4);
1458 identifiers.insert('"', '"');
1459 identifiers.insert('`', '`');
1460 identifiers
1463});
1464
1465static DEFAULT_COMMENTS: LazyLock<HashMap<String, Option<String>>> = LazyLock::new(|| {
1466 let mut comments = HashMap::with_capacity(4);
1467 comments.insert("--".to_string(), None);
1468 comments.insert("/*".to_string(), Some("*/".to_string()));
1469 comments
1470});
1471
1472#[derive(Debug, Clone)]
1474pub struct TokenizerConfig {
1475 pub keywords: HashMap<String, TokenType>,
1477 pub single_tokens: HashMap<char, TokenType>,
1479 pub quotes: HashMap<String, String>,
1481 pub identifiers: HashMap<char, char>,
1483 pub comments: HashMap<String, Option<String>>,
1485 pub string_escapes: Vec<char>,
1487 pub nested_comments: bool,
1489 pub escape_follow_chars: Vec<char>,
1494 pub b_prefix_is_byte_string: bool,
1497 pub numeric_literals: HashMap<String, String>,
1500 pub identifiers_can_start_with_digit: bool,
1504 pub hex_number_strings: bool,
1508 pub hex_string_is_integer_type: bool,
1512 pub string_escapes_allowed_in_raw_strings: bool,
1517 pub hash_comments: bool,
1519 pub dollar_sign_is_identifier: bool,
1523 pub insert_format_raw_data: bool,
1527 pub numbers_can_be_underscore_separated: bool,
1531 pub recover_terminal_backslash_quote: bool,
1535 pub recover_unterminated_string: bool,
1539}
1540
1541impl Default for TokenizerConfig {
1542 fn default() -> Self {
1543 Self {
1544 keywords: DEFAULT_KEYWORDS.clone(),
1545 single_tokens: DEFAULT_SINGLE_TOKENS.clone(),
1546 quotes: DEFAULT_QUOTES.clone(),
1547 identifiers: DEFAULT_IDENTIFIERS.clone(),
1548 comments: DEFAULT_COMMENTS.clone(),
1549 string_escapes: vec!['\''],
1552 nested_comments: true,
1553 escape_follow_chars: vec![],
1555 b_prefix_is_byte_string: false,
1557 numeric_literals: HashMap::new(),
1558 identifiers_can_start_with_digit: false,
1559 hex_number_strings: false,
1560 hex_string_is_integer_type: false,
1561 string_escapes_allowed_in_raw_strings: true,
1564 hash_comments: false,
1565 dollar_sign_is_identifier: false,
1566 insert_format_raw_data: false,
1567 numbers_can_be_underscore_separated: false,
1568 recover_terminal_backslash_quote: false,
1569 recover_unterminated_string: false,
1570 }
1571 }
1572}
1573
1574pub struct Tokenizer {
1576 config: Arc<TokenizerConfig>,
1577}
1578
1579impl Tokenizer {
1580 pub fn new(config: TokenizerConfig) -> Self {
1582 Self {
1583 config: Arc::new(config),
1584 }
1585 }
1586
1587 pub(crate) fn from_shared_config(config: Arc<TokenizerConfig>) -> Self {
1588 Self { config }
1589 }
1590
1591 pub fn default_config() -> Self {
1593 Self::new(TokenizerConfig::default())
1594 }
1595
1596 pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
1598 if sql.is_ascii() {
1599 TokenizerState::<_, Token>::new(sql, &self.config, AsciiCursor(sql.as_bytes()))
1600 .tokenize()
1601 } else {
1602 TokenizerState::<_, Token>::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1603 }
1604 }
1605
1606 pub(crate) fn tokenize_for_parser(
1607 &self,
1608 sql: &Arc<str>,
1609 ) -> Result<(Vec<ParserToken>, TokenGuardStats)> {
1610 if sql.is_ascii() {
1611 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1612 sql,
1613 Arc::clone(sql),
1614 &self.config,
1615 AsciiCursor(sql.as_bytes()),
1616 );
1617 let tokens = state.tokenize()?;
1618 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1619 } else {
1620 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1621 sql,
1622 Arc::clone(sql),
1623 &self.config,
1624 UnicodeCursor::new(sql),
1625 );
1626 let tokens = state.tokenize()?;
1627 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1628 }
1629 }
1630
1631 #[cfg(test)]
1632 fn tokenize_without_ascii_fast_path(&self, sql: &str) -> Result<Vec<Token>> {
1633 TokenizerState::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1634 }
1635
1636 #[cfg(test)]
1637 pub(crate) fn shares_config_with(&self, other: &Self) -> bool {
1638 Arc::ptr_eq(&self.config, &other.config)
1639 }
1640}
1641
1642impl Default for Tokenizer {
1643 fn default() -> Self {
1644 Self::default_config()
1645 }
1646}
1647
1648trait TokenizerCursor {
1649 fn len(&self) -> usize;
1650 fn char_at(&self, index: usize) -> char;
1651 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String;
1652
1653 fn source_range<'a>(&self, _source: &'a str, _start: usize, _end: usize) -> Option<&'a str> {
1654 None
1655 }
1656
1657 fn range_contains(&self, start: usize, needle: char) -> bool {
1658 (start..self.len()).any(|index| self.char_at(index) == needle)
1659 }
1660}
1661
1662struct AsciiCursor<'a>(&'a [u8]);
1663
1664impl TokenizerCursor for AsciiCursor<'_> {
1665 #[inline]
1666 fn len(&self) -> usize {
1667 self.0.len()
1668 }
1669
1670 #[inline]
1671 fn char_at(&self, index: usize) -> char {
1672 self.0[index] as char
1673 }
1674
1675 #[inline]
1676 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String {
1677 source[start..end].to_string()
1678 }
1679
1680 #[inline]
1681 fn source_range<'a>(&self, source: &'a str, start: usize, end: usize) -> Option<&'a str> {
1682 Some(&source[start..end])
1683 }
1684}
1685
1686struct UnicodeCursor(Vec<char>);
1687
1688impl UnicodeCursor {
1689 fn new(source: &str) -> Self {
1690 Self(source.chars().collect())
1691 }
1692}
1693
1694impl TokenizerCursor for UnicodeCursor {
1695 #[inline]
1696 fn len(&self) -> usize {
1697 self.0.len()
1698 }
1699
1700 #[inline]
1701 fn char_at(&self, index: usize) -> char {
1702 self.0[index]
1703 }
1704
1705 #[inline]
1706 fn text_from_range(&self, _source: &str, start: usize, end: usize) -> String {
1707 self.0[start..end].iter().collect()
1708 }
1709}
1710
1711struct TokenizerState<'a, C, T> {
1713 source: &'a str,
1714 shared_source: Option<Arc<str>>,
1715 cursor: C,
1716 size: usize,
1717 tokens: Vec<T>,
1718 start: usize,
1719 current: usize,
1720 line: usize,
1721 column: usize,
1722 comments: Vec<String>,
1723 guard_stats: Option<TokenGuardStats>,
1724 config: &'a TokenizerConfig,
1725}
1726
1727impl<'a, C: TokenizerCursor, T: TokenOutput> TokenizerState<'a, C, T> {
1728 fn new(sql: &'a str, config: &'a TokenizerConfig, cursor: C) -> Self {
1729 let size = cursor.len();
1730 Self {
1731 source: sql,
1732 shared_source: None,
1733 cursor,
1734 size,
1735 tokens: Vec::new(),
1736 start: 0,
1737 current: 0,
1738 line: 1,
1739 column: 1,
1740 comments: Vec::new(),
1741 guard_stats: None,
1742 config,
1743 }
1744 }
1745
1746 fn new_shared(sql: &'a str, source: Arc<str>, config: &'a TokenizerConfig, cursor: C) -> Self {
1747 let size = cursor.len();
1748 Self {
1749 source: sql,
1750 shared_source: Some(source),
1751 cursor,
1752 size,
1753 tokens: Vec::new(),
1754 start: 0,
1755 current: 0,
1756 line: 1,
1757 column: 1,
1758 comments: Vec::new(),
1759 guard_stats: Some(TokenGuardStats::default()),
1760 config,
1761 }
1762 }
1763
1764 fn tokenize(&mut self) -> Result<Vec<T>> {
1765 while !self.is_at_end() {
1766 self.skip_whitespace();
1767 if self.is_at_end() {
1768 break;
1769 }
1770
1771 self.start = self.current;
1772 self.scan_token()?;
1773
1774 if self.config.insert_format_raw_data {
1777 if let Some(raw) = self.try_scan_insert_format_raw_data() {
1778 if !raw.is_empty() {
1779 self.start = self.current;
1780 self.add_token_with_text(TokenType::Var, raw);
1781 }
1782 }
1783 }
1784 }
1785
1786 if !self.comments.is_empty() {
1791 if let Some(last) = self.tokens.last_mut() {
1792 last.trailing_comments_mut().extend(self.comments.drain(..));
1793 }
1794 }
1795
1796 Ok(std::mem::take(&mut self.tokens))
1797 }
1798
1799 #[inline]
1800 fn is_at_end(&self) -> bool {
1801 self.current >= self.size
1802 }
1803
1804 #[inline]
1805 fn text_from_range(&self, start: usize, end: usize) -> String {
1806 self.cursor.text_from_range(self.source, start, end)
1807 }
1808
1809 #[inline]
1810 fn char_at(&self, index: usize) -> char {
1811 self.cursor.char_at(index)
1812 }
1813
1814 #[inline]
1815 fn range_contains(&self, start: usize, needle: char) -> bool {
1816 self.cursor.range_contains(start, needle)
1817 }
1818
1819 #[inline]
1820 fn peek(&self) -> char {
1821 if self.is_at_end() {
1822 '\0'
1823 } else {
1824 self.char_at(self.current)
1825 }
1826 }
1827
1828 #[inline]
1829 fn peek_next(&self) -> char {
1830 if self.current + 1 >= self.size {
1831 '\0'
1832 } else {
1833 self.char_at(self.current + 1)
1834 }
1835 }
1836
1837 #[inline]
1838 fn advance(&mut self) -> char {
1839 let c = self.peek();
1840 self.current += 1;
1841 if c == '\n' {
1842 self.line += 1;
1843 self.column = 1;
1844 } else {
1845 self.column += 1;
1846 }
1847 c
1848 }
1849
1850 #[inline]
1851 fn advance_ascii_to(&mut self, end: usize) -> bool {
1852 let Some(text) = self.cursor.source_range(self.source, self.current, end) else {
1853 return false;
1854 };
1855
1856 let newline_count = text
1857 .as_bytes()
1858 .iter()
1859 .filter(|&&byte| byte == b'\n')
1860 .count();
1861 if newline_count == 0 {
1862 self.column += end - self.current;
1863 } else {
1864 self.line += newline_count;
1865 let last_newline = text
1866 .as_bytes()
1867 .iter()
1868 .rposition(|&byte| byte == b'\n')
1869 .expect("newline count is non-zero");
1870 self.column = text.len() - last_newline;
1871 }
1872 self.current = end;
1873 true
1874 }
1875
1876 #[inline]
1877 fn advance_ascii_digits(&mut self) -> bool {
1878 let Some(rest) = self
1879 .cursor
1880 .source_range(self.source, self.current, self.size)
1881 else {
1882 return false;
1883 };
1884 let bytes = rest.as_bytes();
1885 let mut length = 0;
1886 while length < bytes.len() {
1887 match bytes[length] {
1888 b'0'..=b'9' => length += 1,
1889 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_digit) => length += 1,
1890 _ => break,
1891 }
1892 }
1893 self.current += length;
1894 self.column += length;
1895 true
1896 }
1897
1898 #[inline]
1899 fn advance_ascii_hex_digits(&mut self) -> bool {
1900 let Some(rest) = self
1901 .cursor
1902 .source_range(self.source, self.current, self.size)
1903 else {
1904 return false;
1905 };
1906 let bytes = rest.as_bytes();
1907 let mut length = 0;
1908 while length < bytes.len() {
1909 match bytes[length] {
1910 byte if byte.is_ascii_hexdigit() => length += 1,
1911 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_hexdigit) => length += 1,
1912 _ => break,
1913 }
1914 }
1915 self.current += length;
1916 self.column += length;
1917 true
1918 }
1919
1920 #[inline]
1921 fn advance_ascii_identifier(&mut self) -> bool {
1922 let Some(rest) = self
1923 .cursor
1924 .source_range(self.source, self.current, self.size)
1925 else {
1926 return false;
1927 };
1928 let bytes = rest.as_bytes();
1929 let mut length = 0;
1930 while length < bytes.len() {
1931 let byte = bytes[length];
1932 if byte == b'#' && matches!(bytes.get(length + 1), Some(b'>') | Some(b'-')) {
1933 break;
1934 }
1935 if byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$' | b'#' | b'@') {
1936 length += 1;
1937 } else {
1938 break;
1939 }
1940 }
1941 self.current += length;
1942 self.column += length;
1943 true
1944 }
1945
1946 fn try_scan_simple_quoted_content(
1947 &mut self,
1948 quote: char,
1949 backslash_is_escape: bool,
1950 ) -> Option<(usize, usize)> {
1951 let content_start = self.current;
1952 let rest = self
1953 .cursor
1954 .source_range(self.source, content_start, self.size)?;
1955 let quote_offset = rest.find(quote)?;
1956 let content_end = content_start + quote_offset;
1957
1958 if (content_end + 1 < self.size && self.char_at(content_end + 1) == quote)
1959 || (backslash_is_escape && rest[..quote_offset].contains('\\'))
1960 {
1961 return None;
1962 }
1963
1964 self.advance_ascii_to(content_end);
1965 self.advance();
1966 Some((content_start, content_end))
1967 }
1968
1969 fn skip_whitespace(&mut self) {
1970 let mut saw_newline = false;
1975 while !self.is_at_end() {
1976 let c = self.peek();
1977 match c {
1978 ' ' | '\t' | '\r' => {
1979 self.advance();
1980 }
1981 '\n' => {
1982 saw_newline = true;
1983 self.advance();
1984 }
1985 '\u{00A0}' | '\u{2000}'..='\u{200B}' | '\u{3000}' | '\u{FEFF}' => {
1990 self.advance();
1991 }
1992 '-' if self.peek_next() == '-' => {
1993 self.scan_line_comment(saw_newline);
1994 saw_newline = true;
1996 }
1997 '/' if self.peek_next() == '/' && self.config.hash_comments => {
1998 self.scan_double_slash_comment();
2000 }
2001 '/' if self.peek_next() == '*' => {
2002 if self.current + 2 < self.size && self.char_at(self.current + 2) == '+' {
2004 break;
2006 }
2007 if self.scan_block_comment(saw_newline).is_err() {
2008 return;
2009 }
2010 }
2012 '/' if self.peek_next() == '/' && self.config.comments.contains_key("//") => {
2013 let prev_non_ws = if self.current > 0 {
2017 let mut i = self.current - 1;
2018 while i > 0 && (self.char_at(i) == ' ' || self.char_at(i) == '\t') {
2019 i -= 1;
2020 }
2021 self.char_at(i)
2022 } else {
2023 '\0'
2024 };
2025 if prev_non_ws == ':' || prev_non_ws == '/' {
2026 break;
2028 }
2029 self.scan_line_comment(saw_newline);
2030 saw_newline = true;
2032 }
2033 '#' if self.config.hash_comments => {
2034 self.scan_hash_line_comment();
2035 }
2036 _ => break,
2037 }
2038 }
2039 }
2040
2041 fn scan_hash_line_comment(&mut self) {
2042 self.advance(); let start = self.current;
2044 while !self.is_at_end() && self.peek() != '\n' {
2045 self.advance();
2046 }
2047 let comment = self.text_from_range(start, self.current);
2048 let comment_text = comment.trim().to_string();
2049 if let Some(last) = self.tokens.last_mut() {
2050 last.trailing_comments_mut().push(comment_text);
2051 } else {
2052 self.comments.push(comment_text);
2053 }
2054 }
2055
2056 fn scan_double_slash_comment(&mut self) {
2057 self.advance(); self.advance(); let start = self.current;
2060 while !self.is_at_end() && self.peek() != '\n' {
2061 self.advance();
2062 }
2063 let comment = self.text_from_range(start, self.current);
2064 let comment_text = comment.trim().to_string();
2065 if let Some(last) = self.tokens.last_mut() {
2066 last.trailing_comments_mut().push(comment_text);
2067 } else {
2068 self.comments.push(comment_text);
2069 }
2070 }
2071
2072 fn scan_line_comment(&mut self, after_newline: bool) {
2073 self.advance(); self.advance(); let start = self.current;
2076 while !self.is_at_end() && self.peek() != '\n' {
2077 self.advance();
2078 }
2079 let comment_text = self.text_from_range(start, self.current);
2080
2081 if after_newline || self.tokens.is_empty() {
2084 self.comments.push(comment_text);
2085 } else if let Some(last) = self.tokens.last_mut() {
2086 last.trailing_comments_mut().push(comment_text);
2087 }
2088 }
2089
2090 fn scan_block_comment(&mut self, after_newline: bool) -> Result<()> {
2091 self.advance(); self.advance(); let content_start = self.current;
2094 let mut depth = 1;
2095
2096 while !self.is_at_end() && depth > 0 {
2097 if self.peek() == '/' && self.peek_next() == '*' && self.config.nested_comments {
2098 self.advance();
2099 self.advance();
2100 depth += 1;
2101 } else if self.peek() == '*' && self.peek_next() == '/' {
2102 depth -= 1;
2103 if depth > 0 {
2104 self.advance();
2105 self.advance();
2106 }
2107 } else {
2108 self.advance();
2109 }
2110 }
2111
2112 if depth > 0 {
2113 return Err(Error::tokenize(
2114 "Unterminated block comment",
2115 self.line,
2116 self.column,
2117 self.start,
2118 self.current,
2119 ));
2120 }
2121
2122 let content = self.text_from_range(content_start, self.current);
2124 self.advance(); self.advance(); let comment_text = format!("/*{}*/", content);
2129
2130 if after_newline || self.tokens.is_empty() {
2133 self.comments.push(comment_text);
2134 } else if let Some(last) = self.tokens.last_mut() {
2135 last.trailing_comments_mut().push(comment_text);
2136 }
2137
2138 Ok(())
2139 }
2140
2141 fn scan_hint(&mut self) -> Result<()> {
2143 self.advance(); self.advance(); self.advance(); let hint_start = self.current;
2147
2148 while !self.is_at_end() {
2150 if self.peek() == '*' && self.peek_next() == '/' {
2151 break;
2152 }
2153 self.advance();
2154 }
2155
2156 if self.is_at_end() {
2157 return Err(Error::tokenize(
2158 "Unterminated hint comment",
2159 self.line,
2160 self.column,
2161 self.start,
2162 self.current,
2163 ));
2164 }
2165
2166 let hint_text = self.text_from_range(hint_start, self.current);
2167 self.advance(); self.advance(); self.add_token_with_text(TokenType::Hint, hint_text.trim().to_string());
2171
2172 Ok(())
2173 }
2174
2175 fn scan_positional_parameter(&mut self) -> Result<()> {
2177 self.advance(); let start = self.current;
2179
2180 while !self.is_at_end() && self.peek().is_ascii_digit() {
2181 self.advance();
2182 }
2183
2184 let number = self.text_from_range(start, self.current);
2185 self.add_token_with_text(TokenType::Parameter, number);
2186 Ok(())
2187 }
2188
2189 fn try_scan_tagged_dollar_string(&mut self) -> Result<Option<()>> {
2194 let saved_pos = self.current;
2195
2196 self.advance(); let tag_start = self.current;
2202 while !self.is_at_end()
2203 && (self.peek().is_alphanumeric() || self.peek() == '_' || !self.peek().is_ascii())
2204 {
2205 self.advance();
2206 }
2207 let tag = self.text_from_range(tag_start, self.current);
2208
2209 if self.is_at_end() || self.peek() != '$' {
2211 self.current = saved_pos;
2213 return Ok(None);
2214 }
2215 self.advance(); let content_start = self.current;
2219 let closing_tag = format!("${}$", tag);
2220 let closing_chars: Vec<char> = closing_tag.chars().collect();
2221
2222 loop {
2223 if self.is_at_end() {
2224 self.current = saved_pos;
2226 return Ok(None);
2227 }
2228
2229 if self.peek() == '$' && self.current + closing_chars.len() <= self.size {
2231 let matches = closing_chars.iter().enumerate().all(|(j, &ch)| {
2232 self.current + j < self.size && self.char_at(self.current + j) == ch
2233 });
2234 if matches {
2235 let content = self.text_from_range(content_start, self.current);
2236 for _ in 0..closing_chars.len() {
2238 self.advance();
2239 }
2240 let token_text = format!("{}\x00{}", tag, content);
2242 self.add_token_with_text(TokenType::DollarString, token_text);
2243 return Ok(Some(()));
2244 }
2245 }
2246 self.advance();
2247 }
2248 }
2249
2250 fn scan_dollar_quoted_string(&mut self) -> Result<()> {
2255 self.advance(); self.advance(); let start = self.current;
2260 while !self.is_at_end() {
2261 if self.peek() == '$'
2262 && self.current + 1 < self.size
2263 && self.char_at(self.current + 1) == '$'
2264 {
2265 break;
2266 }
2267 self.advance();
2268 }
2269
2270 let content = self.text_from_range(start, self.current);
2271
2272 if !self.is_at_end() {
2273 self.advance(); self.advance(); }
2276
2277 self.add_token_with_text(TokenType::DollarString, content);
2278 Ok(())
2279 }
2280
2281 fn scan_token(&mut self) -> Result<()> {
2282 let c = self.peek();
2283
2284 if c == '\'' {
2286 if self.config.quotes.contains_key("'''")
2288 && self.peek_next() == '\''
2289 && self.current + 2 < self.size
2290 && self.char_at(self.current + 2) == '\''
2291 {
2292 return self.scan_triple_quoted_string('\'');
2293 }
2294 return self.scan_string();
2295 }
2296
2297 if c == '"'
2299 && self.config.quotes.contains_key("\"\"\"")
2300 && self.peek_next() == '"'
2301 && self.current + 2 < self.size
2302 && self.char_at(self.current + 2) == '"'
2303 {
2304 return self.scan_triple_quoted_string('"');
2305 }
2306
2307 if c == '"'
2310 && self.config.quotes.contains_key("\"")
2311 && !self.config.identifiers.contains_key(&'"')
2312 {
2313 return self.scan_double_quoted_string();
2314 }
2315
2316 if let Some(&end_quote) = self.config.identifiers.get(&c) {
2318 return self.scan_quoted_identifier(end_quote);
2319 }
2320
2321 if c.is_ascii_digit() {
2323 return self.scan_number();
2324 }
2325
2326 if c == '.' && self.peek_next().is_ascii_digit() {
2333 let prev_char = if self.current > 0 {
2334 self.char_at(self.current - 1)
2335 } else {
2336 '\0'
2337 };
2338 let is_after_ident = prev_char.is_alphanumeric()
2339 || prev_char == '_'
2340 || prev_char == '`'
2341 || prev_char == '"'
2342 || prev_char == ']'
2343 || prev_char == ')';
2344 if prev_char != '.' && !is_after_ident {
2345 return self.scan_number_starting_with_dot();
2346 }
2347 }
2348
2349 if c == '/'
2351 && self.peek_next() == '*'
2352 && self.current + 2 < self.size
2353 && self.char_at(self.current + 2) == '+'
2354 {
2355 return self.scan_hint();
2356 }
2357
2358 if let Some(token_type) = self.try_scan_multi_char_operator() {
2360 self.add_token(token_type);
2361 return Ok(());
2362 }
2363
2364 if c == '$'
2367 && (self.peek_next().is_alphanumeric()
2368 || self.peek_next() == '_'
2369 || !self.peek_next().is_ascii())
2370 {
2371 if let Some(()) = self.try_scan_tagged_dollar_string()? {
2372 return Ok(());
2373 }
2374 if self.config.dollar_sign_is_identifier {
2377 return self.scan_dollar_identifier();
2378 }
2379 }
2380
2381 if c == '$' && self.peek_next() == '$' {
2383 return self.scan_dollar_quoted_string();
2384 }
2385
2386 if c == '$' && self.peek_next().is_ascii_digit() {
2388 return self.scan_positional_parameter();
2389 }
2390
2391 if c == '$' && self.config.dollar_sign_is_identifier {
2393 return self.scan_dollar_identifier();
2394 }
2395
2396 if (c == '#' || c == '@')
2399 && (self.peek_next().is_alphanumeric()
2400 || self.peek_next() == '_'
2401 || self.peek_next() == '#')
2402 {
2403 return self.scan_tsql_identifier();
2404 }
2405
2406 if let Some(&token_type) = self.config.single_tokens.get(&c) {
2408 self.advance();
2409 self.add_token(token_type);
2410 return Ok(());
2411 }
2412
2413 if c == '\u{2212}' {
2415 self.advance();
2416 self.add_token(TokenType::Dash);
2417 return Ok(());
2418 }
2419
2420 if c == '\u{2044}' {
2422 self.advance();
2423 self.add_token(TokenType::Slash);
2424 return Ok(());
2425 }
2426
2427 if c == '\u{2018}' || c == '\u{2019}' {
2429 return self.scan_unicode_quoted_string(c);
2431 }
2432 if c == '\u{201C}' || c == '\u{201D}' {
2433 return self.scan_unicode_quoted_identifier(c);
2435 }
2436
2437 self.scan_identifier_or_keyword()
2439 }
2440
2441 fn try_scan_multi_char_operator(&mut self) -> Option<TokenType> {
2442 let c = self.peek();
2443 let next = self.peek_next();
2444 let third = if self.current + 2 < self.size {
2445 self.char_at(self.current + 2)
2446 } else {
2447 '\0'
2448 };
2449
2450 if c == '-' && next == '|' && third == '-' {
2453 self.advance();
2454 self.advance();
2455 self.advance();
2456 return Some(TokenType::Adjacent);
2457 }
2458
2459 if c == '|' && next == '|' && third == '/' {
2461 self.advance();
2462 self.advance();
2463 self.advance();
2464 return Some(TokenType::DPipeSlash);
2465 }
2466
2467 if c == '#' && next == '>' && third == '>' {
2469 self.advance();
2470 self.advance();
2471 self.advance();
2472 return Some(TokenType::DHashArrow);
2473 }
2474
2475 if c == '-' && next == '>' && third == '>' {
2477 self.advance();
2478 self.advance();
2479 self.advance();
2480 return Some(TokenType::DArrow);
2481 }
2482
2483 if c == '<' && next == '=' && third == '>' {
2485 self.advance();
2486 self.advance();
2487 self.advance();
2488 return Some(TokenType::NullsafeEq);
2489 }
2490
2491 if c == '<' && next == '-' && third == '>' {
2493 self.advance();
2494 self.advance();
2495 self.advance();
2496 return Some(TokenType::LrArrow);
2497 }
2498
2499 if c == '<' && next == '@' {
2501 self.advance();
2502 self.advance();
2503 return Some(TokenType::LtAt);
2504 }
2505
2506 if c == '@' && next == '>' {
2508 self.advance();
2509 self.advance();
2510 return Some(TokenType::AtGt);
2511 }
2512
2513 if c == '~' && next == '~' && third == '~' {
2515 self.advance();
2516 self.advance();
2517 self.advance();
2518 return Some(TokenType::Glob);
2519 }
2520
2521 if c == '~' && next == '~' && third == '*' {
2523 self.advance();
2524 self.advance();
2525 self.advance();
2526 return Some(TokenType::ILike);
2527 }
2528
2529 let fourth = if self.current + 3 < self.size {
2531 self.char_at(self.current + 3)
2532 } else {
2533 '\0'
2534 };
2535 if c == '!' && next == '~' && third == '~' && fourth == '*' {
2536 self.advance();
2537 self.advance();
2538 self.advance();
2539 self.advance();
2540 return Some(TokenType::NotILike);
2541 }
2542
2543 if c == '!' && next == '~' && third == '~' {
2545 self.advance();
2546 self.advance();
2547 self.advance();
2548 return Some(TokenType::NotLike);
2549 }
2550
2551 if c == '!' && next == '~' && third == '*' {
2553 self.advance();
2554 self.advance();
2555 self.advance();
2556 return Some(TokenType::NotIRLike);
2557 }
2558
2559 if c == '!' && next == ':' && third == '>' {
2561 self.advance();
2562 self.advance();
2563 self.advance();
2564 return Some(TokenType::NColonGt);
2565 }
2566
2567 if c == '?' && next == ':' && third == ':' {
2569 self.advance();
2570 self.advance();
2571 self.advance();
2572 return Some(TokenType::QDColon);
2573 }
2574
2575 if c == '!' && next == '~' {
2577 self.advance();
2578 self.advance();
2579 return Some(TokenType::NotRLike);
2580 }
2581
2582 if c == '~' && next == '~' {
2584 self.advance();
2585 self.advance();
2586 return Some(TokenType::Like);
2587 }
2588
2589 if c == '~' && next == '*' {
2591 self.advance();
2592 self.advance();
2593 return Some(TokenType::IRLike);
2594 }
2595
2596 if c == ':' && next == ':' && third == '$' {
2599 self.advance();
2600 self.advance();
2601 self.advance();
2602 return Some(TokenType::DColonDollar);
2603 }
2604 if c == ':' && next == ':' && third == '%' {
2605 self.advance();
2606 self.advance();
2607 self.advance();
2608 return Some(TokenType::DColonPercent);
2609 }
2610 if c == ':' && next == ':' && third == '?' {
2611 self.advance();
2612 self.advance();
2613 self.advance();
2614 return Some(TokenType::DColonQMark);
2615 }
2616
2617 let token_type = match (c, next) {
2619 ('.', ':') => Some(TokenType::DotColon),
2620 ('=', '=') => Some(TokenType::Eq), ('<', '=') => Some(TokenType::Lte),
2622 ('>', '=') => Some(TokenType::Gte),
2623 ('!', '=') => Some(TokenType::Neq),
2624 ('<', '>') => Some(TokenType::Neq),
2625 ('^', '=') => Some(TokenType::Neq),
2626 ('<', '<') => Some(TokenType::LtLt),
2627 ('>', '>') => Some(TokenType::GtGt),
2628 ('|', '|') => Some(TokenType::DPipe),
2629 ('|', '/') => Some(TokenType::PipeSlash), (':', ':') => Some(TokenType::DColon),
2631 (':', '=') => Some(TokenType::ColonEq), (':', '>') => Some(TokenType::ColonGt), ('-', '>') => Some(TokenType::Arrow), ('=', '>') => Some(TokenType::FArrow), ('&', '&') => Some(TokenType::DAmp),
2636 ('&', '<') => 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,
2649 };
2650
2651 if token_type.is_some() {
2652 self.advance();
2653 self.advance();
2654 }
2655
2656 token_type
2657 }
2658
2659 fn scan_string(&mut self) -> Result<()> {
2660 self.advance(); if let Some((text_start, text_end)) =
2662 self.try_scan_simple_quoted_content('\'', self.config.string_escapes.contains(&'\\'))
2663 {
2664 self.add_token_from_source(TokenType::String, text_start, text_end);
2665 return Ok(());
2666 }
2667 let mut value = String::new();
2668
2669 while !self.is_at_end() {
2670 let c = self.peek();
2671 if c == '\'' {
2672 if self.peek_next() == '\'' {
2673 value.push('\'');
2675 self.advance();
2676 self.advance();
2677 } else {
2678 break;
2679 }
2680 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2681 if self.config.recover_terminal_backslash_quote
2682 && self.peek_next() == '\''
2683 && !self.range_contains(self.current + 2, '\'')
2684 {
2685 value.push(self.advance());
2686 break;
2687 }
2688
2689 self.scan_backslash_escape(&mut value);
2690 } else {
2691 value.push(self.advance());
2692 }
2693 }
2694
2695 if self.is_at_end() {
2696 if self.config.recover_unterminated_string {
2697 self.add_token_with_text(TokenType::String, value);
2698 return Ok(());
2699 }
2700
2701 return Err(Error::tokenize(
2702 "Unterminated string",
2703 self.line,
2704 self.column,
2705 self.start,
2706 self.current,
2707 ));
2708 }
2709
2710 self.advance(); self.add_token_with_text(TokenType::String, value);
2712 Ok(())
2713 }
2714
2715 fn scan_double_quoted_string(&mut self) -> Result<()> {
2717 self.advance(); let mut value = String::new();
2719
2720 while !self.is_at_end() {
2721 let c = self.peek();
2722 if c == '"' {
2723 if self.peek_next() == '"' {
2724 value.push('"');
2726 self.advance();
2727 self.advance();
2728 } else {
2729 break;
2730 }
2731 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2732 self.scan_backslash_escape(&mut value);
2733 } else {
2734 value.push(self.advance());
2735 }
2736 }
2737
2738 if self.is_at_end() {
2739 return Err(Error::tokenize(
2740 "Unterminated double-quoted string",
2741 self.line,
2742 self.column,
2743 self.start,
2744 self.current,
2745 ));
2746 }
2747
2748 self.advance(); self.add_token_with_text(TokenType::String, value);
2750 Ok(())
2751 }
2752
2753 fn scan_backslash_escape(&mut self, value: &mut String) {
2754 self.advance(); if self.is_at_end() {
2756 value.push('\\');
2757 return;
2758 }
2759
2760 let escaped = self.advance();
2761 let restricted = !self.config.escape_follow_chars.is_empty();
2762 let always_allowed = matches!(escaped, '\\' | '\'' | '"');
2763 if restricted && !always_allowed && !self.config.escape_follow_chars.contains(&escaped) {
2764 value.push(escaped);
2765 return;
2766 }
2767
2768 let supports_octal =
2769 restricted && ('1'..='7').any(|digit| self.config.escape_follow_chars.contains(&digit));
2770 if supports_octal && escaped.is_digit(8) {
2771 if let Some(codepoint) = self.peek_radix_digits(2, 8).and_then(|suffix| {
2772 let first = escaped.to_digit(8)?;
2773 first.checked_mul(64)?.checked_add(suffix)
2774 }) {
2775 if let Ok(byte) = u8::try_from(codepoint) {
2776 self.advance_count(2);
2777 value.push(byte as char);
2778 return;
2779 }
2780 }
2781
2782 if escaped == '0' {
2783 value.push('\0');
2784 } else {
2785 value.push(escaped);
2786 }
2787 return;
2788 }
2789
2790 match escaped {
2791 'n' => value.push('\n'),
2792 'r' => value.push('\r'),
2793 't' => value.push('\t'),
2794 '0' => value.push('\0'),
2795 'Z' => value.push('\x1A'),
2796 'a' => value.push('\x07'),
2797 'b' => value.push('\x08'),
2798 'f' => value.push('\x0C'),
2799 'v' => value.push('\x0B'),
2800 'x' => {
2801 if let Some(codepoint) = self.peek_radix_digits(2, 16) {
2802 self.advance_count(2);
2803 value.push(codepoint as u8 as char);
2804 } else if restricted {
2805 value.push('x');
2807 } else {
2808 value.push('\\');
2811 value.push('x');
2812 for _ in 0..2 {
2813 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2814 value.push(self.advance());
2815 }
2816 }
2817 }
2818 }
2819 'u' if restricted && self.config.escape_follow_chars.contains(&'u') => {
2820 if let Some(codepoint) = self.peek_radix_digits(4, 16).and_then(char::from_u32) {
2821 self.advance_count(4);
2822 value.push(codepoint);
2823 } else {
2824 value.push('u');
2825 }
2826 }
2827 '\\' => value.push('\\'),
2828 '\'' => value.push('\''),
2829 '"' => value.push('"'),
2830 '%' => value.push('%'),
2831 '_' => value.push('_'),
2832 _ if restricted => value.push(escaped),
2833 _ => {
2834 value.push('\\');
2835 value.push(escaped);
2836 }
2837 }
2838 }
2839
2840 fn peek_radix_digits(&self, count: usize, radix: u32) -> Option<u32> {
2841 if self.current + count > self.size {
2842 return None;
2843 }
2844
2845 let mut value = 0_u32;
2846 for offset in 0..count {
2847 value = value
2848 .checked_mul(radix)?
2849 .checked_add(self.char_at(self.current + offset).to_digit(radix)?)?;
2850 }
2851 Some(value)
2852 }
2853
2854 fn advance_count(&mut self, count: usize) {
2855 for _ in 0..count {
2856 self.advance();
2857 }
2858 }
2859
2860 fn scan_triple_quoted_string(&mut self, quote_char: char) -> Result<()> {
2861 self.advance();
2863 self.advance();
2864 self.advance();
2865 let mut value = String::new();
2866
2867 while !self.is_at_end() {
2868 if self.peek() == quote_char
2870 && self.current + 1 < self.size
2871 && self.char_at(self.current + 1) == quote_char
2872 && self.current + 2 < self.size
2873 && self.char_at(self.current + 2) == quote_char
2874 {
2875 break;
2877 }
2878 value.push(self.advance());
2879 }
2880
2881 if self.is_at_end() {
2882 return Err(Error::tokenize(
2883 "Unterminated triple-quoted string",
2884 self.line,
2885 self.column,
2886 self.start,
2887 self.current,
2888 ));
2889 }
2890
2891 self.advance();
2893 self.advance();
2894 self.advance();
2895 let token_type = if quote_char == '"' {
2896 TokenType::TripleDoubleQuotedString
2897 } else {
2898 TokenType::TripleSingleQuotedString
2899 };
2900 self.add_token_with_text(token_type, value);
2901 Ok(())
2902 }
2903
2904 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2905 self.advance(); let mut value = String::new();
2907
2908 loop {
2909 if self.is_at_end() {
2910 return Err(Error::tokenize(
2911 "Unterminated identifier",
2912 self.line,
2913 self.column,
2914 self.start,
2915 self.current,
2916 ));
2917 }
2918 if end_quote == '`' && self.peek() == '\\' && self.peek_next() == end_quote {
2919 value.push(end_quote);
2921 self.advance(); self.advance(); continue;
2924 }
2925 if self.peek() == end_quote {
2926 if self.peek_next() == end_quote {
2927 value.push(end_quote);
2929 self.advance(); self.advance(); } else {
2932 break;
2934 }
2935 } else {
2936 value.push(self.peek());
2937 self.advance();
2938 }
2939 }
2940
2941 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2943 Ok(())
2944 }
2945
2946 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2951 self.advance(); let start = self.current;
2953 let close_quote = if open_quote == '\u{2018}' {
2955 '\u{2019}' } else {
2957 '\u{2019}' };
2959 while !self.is_at_end() && self.peek() != close_quote {
2960 self.advance();
2961 }
2962 let value = self.text_from_range(start, self.current);
2963 if !self.is_at_end() {
2964 self.advance(); }
2966 self.add_token_with_text(TokenType::String, value);
2967 Ok(())
2968 }
2969
2970 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
2973 self.advance(); let start = self.current;
2975 let close_quote = if open_quote == '\u{201C}' {
2976 '\u{201D}' } else {
2978 '\u{201D}' };
2980 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
2981 self.advance();
2982 }
2983 let value = self.text_from_range(start, self.current);
2984 if !self.is_at_end() {
2985 self.advance(); }
2987 self.add_token_with_text(TokenType::QuotedIdentifier, value);
2988 Ok(())
2989 }
2990
2991 fn scan_number(&mut self) -> Result<()> {
2992 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
2994 let next = if self.current + 1 < self.size {
2995 self.char_at(self.current + 1)
2996 } else {
2997 '\0'
2998 };
2999 if next == 'x' || next == 'X' {
3000 self.advance();
3002 self.advance();
3003 let hex_start = self.current;
3005 if !self.advance_ascii_hex_digits() {
3006 while !self.is_at_end()
3007 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3008 {
3009 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3010 break;
3011 }
3012 self.advance();
3013 }
3014 }
3015 if self.current > hex_start {
3016 let mut is_hex_float = false;
3018 if !self.is_at_end() && self.peek() == '.' {
3020 let after_dot = if self.current + 1 < self.size {
3021 self.char_at(self.current + 1)
3022 } else {
3023 '\0'
3024 };
3025 if after_dot.is_ascii_hexdigit() {
3026 is_hex_float = true;
3027 self.advance(); if !self.advance_ascii_hex_digits() {
3029 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3030 self.advance();
3031 }
3032 }
3033 }
3034 }
3035 if !self.is_at_end() && (self.peek() == 'p' || self.peek() == 'P') {
3037 is_hex_float = true;
3038 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3040 self.advance();
3041 }
3042 if !self.advance_ascii_digits() {
3043 while !self.is_at_end() && self.peek().is_ascii_digit() {
3044 self.advance();
3045 }
3046 }
3047 }
3048 if is_hex_float {
3049 let raw_text = self.text_from_range(self.start, self.current);
3051 let full_text = if self.config.numbers_can_be_underscore_separated
3052 && raw_text.contains('_')
3053 {
3054 raw_text.replace('_', "")
3055 } else {
3056 raw_text
3057 };
3058 self.add_token_with_text(TokenType::Number, full_text);
3059 } else if self.config.hex_string_is_integer_type {
3060 let raw_value = self.text_from_range(hex_start, self.current);
3062 let hex_value = if self.config.numbers_can_be_underscore_separated
3063 && raw_value.contains('_')
3064 {
3065 raw_value.replace('_', "")
3066 } else {
3067 raw_value
3068 };
3069 self.add_token_with_text(TokenType::HexNumber, hex_value);
3070 } else {
3071 let raw_value = self.text_from_range(hex_start, self.current);
3073 let hex_value = if self.config.numbers_can_be_underscore_separated
3074 && raw_value.contains('_')
3075 {
3076 raw_value.replace('_', "")
3077 } else {
3078 raw_value
3079 };
3080 self.add_token_with_text(TokenType::HexString, hex_value);
3081 }
3082 return Ok(());
3083 }
3084 self.current = self.start + 1;
3087 }
3088 }
3089
3090 if !self.advance_ascii_digits() {
3092 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3093 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3095 break;
3096 }
3097 self.advance();
3098 }
3099 }
3100
3101 if self.peek() == '.' {
3105 let next = self.peek_next();
3106 if next != '.' {
3112 self.advance(); if !self.advance_ascii_digits() {
3115 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3116 {
3117 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3118 break;
3119 }
3120 self.advance();
3121 }
3122 }
3123 }
3124 }
3125
3126 if self.peek() == 'e' || self.peek() == 'E' {
3128 self.advance();
3129 if self.peek() == '+' || self.peek() == '-' {
3130 self.advance();
3131 }
3132 if !self.advance_ascii_digits() {
3133 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3134 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3135 break;
3136 }
3137 self.advance();
3138 }
3139 }
3140 }
3141
3142 let source_text = self
3143 .cursor
3144 .source_range(self.source, self.start, self.current);
3145 let raw_owned = source_text
3146 .is_none()
3147 .then(|| self.text_from_range(self.start, self.current));
3148 let raw_text = source_text.unwrap_or_else(|| {
3149 raw_owned
3150 .as_deref()
3151 .expect("non-ASCII numbers own their text")
3152 });
3153 let normalized = (self.config.numbers_can_be_underscore_separated
3156 && raw_text.contains('_'))
3157 .then(|| raw_text.replace('_', ""));
3158 let text = normalized.as_deref().unwrap_or(raw_text);
3159
3160 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3162 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3163 let suffix_match = if self.current + 1 < self.size {
3165 let two_char: String = [
3166 self.char_at(self.current).to_ascii_uppercase(),
3167 self.char_at(self.current + 1).to_ascii_uppercase(),
3168 ]
3169 .iter()
3170 .collect();
3171 if self.config.numeric_literals.contains_key(&two_char) {
3172 let after_suffix = if self.current + 2 < self.size {
3174 self.char_at(self.current + 2)
3175 } else {
3176 ' '
3177 };
3178 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3179 Some((two_char, 2))
3180 } else {
3181 None
3182 }
3183 } else if self.config.numeric_literals.contains_key(&next_char) {
3184 let after_suffix = if self.current + 1 < self.size {
3186 self.char_at(self.current + 1)
3187 } else {
3188 ' '
3189 };
3190 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3191 Some((next_char, 1))
3192 } else {
3193 None
3194 }
3195 } else {
3196 None
3197 }
3198 } else if self.config.numeric_literals.contains_key(&next_char) {
3199 Some((next_char, 1))
3201 } else {
3202 None
3203 };
3204
3205 if let Some((suffix, len)) = suffix_match {
3206 for _ in 0..len {
3208 self.advance();
3209 }
3210 let type_name = self
3213 .config
3214 .numeric_literals
3215 .get(&suffix)
3216 .expect("suffix verified by contains_key above")
3217 .clone();
3218 let combined = format!("{}::{}", text, type_name);
3219 self.add_token_with_text(TokenType::Number, combined);
3220 return Ok(());
3221 }
3222 }
3223
3224 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3227 let next = self.peek();
3228 if next.is_alphabetic() || next == '_' {
3229 if !self.advance_ascii_identifier() {
3231 while !self.is_at_end() {
3232 let ch = self.peek();
3233 if ch.is_alphanumeric() || ch == '_' {
3234 self.advance();
3235 } else {
3236 break;
3237 }
3238 }
3239 }
3240 self.add_token(TokenType::Identifier);
3241 return Ok(());
3242 }
3243 }
3244
3245 if let Some(text) = normalized.or(raw_owned) {
3246 self.add_token_with_text(TokenType::Number, text);
3247 } else {
3248 self.add_token(TokenType::Number);
3249 }
3250 Ok(())
3251 }
3252
3253 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3255 self.advance();
3257
3258 if !self.advance_ascii_digits() {
3260 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3261 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3262 break;
3263 }
3264 self.advance();
3265 }
3266 }
3267
3268 if self.peek() == 'e' || self.peek() == 'E' {
3270 self.advance();
3271 if self.peek() == '+' || self.peek() == '-' {
3272 self.advance();
3273 }
3274 if !self.advance_ascii_digits() {
3275 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3276 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3277 break;
3278 }
3279 self.advance();
3280 }
3281 }
3282 }
3283
3284 let source_text = self
3285 .cursor
3286 .source_range(self.source, self.start, self.current);
3287 let raw_owned = source_text
3288 .is_none()
3289 .then(|| self.text_from_range(self.start, self.current));
3290 let raw_text = source_text.unwrap_or_else(|| {
3291 raw_owned
3292 .as_deref()
3293 .expect("non-ASCII numbers own their text")
3294 });
3295 let normalized = (self.config.numbers_can_be_underscore_separated
3298 && raw_text.contains('_'))
3299 .then(|| raw_text.replace('_', ""));
3300 if let Some(text) = normalized.or(raw_owned) {
3301 self.add_token_with_text(TokenType::Number, text);
3302 } else {
3303 self.add_token(TokenType::Number);
3304 }
3305 Ok(())
3306 }
3307
3308 #[inline]
3311 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3312 if text.len() > 128 {
3313 return TokenType::Var;
3314 }
3315 let mut buf = [0u8; 128];
3316 for (i, b) in text.bytes().enumerate() {
3317 buf[i] = b.to_ascii_uppercase();
3318 }
3319 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3320 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3321 } else {
3322 TokenType::Var
3323 }
3324 }
3325
3326 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3327 let first_char = self.peek();
3329 if !first_char.is_alphanumeric() && first_char != '_' {
3330 let c = self.advance();
3332 return Err(Error::tokenize(
3333 format!("Unexpected character: '{}'", c),
3334 self.line,
3335 self.column,
3336 self.start,
3337 self.current,
3338 ));
3339 }
3340
3341 if !self.advance_ascii_identifier() {
3342 while !self.is_at_end() {
3343 let c = self.peek();
3344 if c == '#' {
3348 let next_c = if self.current + 1 < self.size {
3349 self.char_at(self.current + 1)
3350 } else {
3351 '\0'
3352 };
3353 if next_c == '>' || next_c == '-' {
3354 break; }
3356 self.advance();
3357 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3358 self.advance();
3359 } else {
3360 break;
3361 }
3362 }
3363 }
3364
3365 let source_text = self
3366 .cursor
3367 .source_range(self.source, self.start, self.current);
3368 let owned_text = source_text
3369 .is_none()
3370 .then(|| self.text_from_range(self.start, self.current));
3371 let text = source_text.unwrap_or_else(|| {
3372 owned_text
3373 .as_deref()
3374 .expect("non-ASCII identifiers own their text")
3375 });
3376
3377 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3379 self.advance(); self.add_token(TokenType::Neq);
3381 return Ok(());
3382 }
3383
3384 let next_char = self.peek();
3387 let is_single_quote = next_char == '\'';
3388 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3389 let is_double_quote_for_raw = next_char == '"';
3392
3393 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3396 let quote_char = if is_single_quote { '\'' } else { '"' };
3399 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3403 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3407 self.add_token_with_text(TokenType::RawString, string_value);
3408 } else {
3409 let string_value = self.scan_raw_string_content(quote_char)?;
3410 self.add_token_with_text(TokenType::RawString, string_value);
3411 }
3412 return Ok(());
3413 }
3414
3415 if is_single_quote || is_double_quote {
3416 if text.eq_ignore_ascii_case("N") {
3417 self.advance(); let string_value = if is_single_quote {
3420 self.scan_string_content()?
3421 } else {
3422 self.scan_double_quoted_string_content()?
3423 };
3424 self.add_token_with_text(TokenType::NationalString, string_value);
3425 return Ok(());
3426 } else if text.eq_ignore_ascii_case("E") {
3427 let lowercase = text == "e";
3431 let prefix = if lowercase { "e:" } else { "E:" };
3432 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3434 self.add_token_with_text(
3435 TokenType::EscapeString,
3436 format!("{}{}", prefix, string_value),
3437 );
3438 return Ok(());
3439 } else if text.eq_ignore_ascii_case("X") {
3440 self.advance(); let string_value = if is_single_quote {
3443 self.scan_string_content()?
3444 } else {
3445 self.scan_double_quoted_string_content()?
3446 };
3447 self.add_token_with_text(TokenType::HexString, string_value);
3448 return Ok(());
3449 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3450 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3453 self.add_token_with_text(TokenType::ByteString, string_value);
3454 return Ok(());
3455 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3456 self.advance(); let string_value = self.scan_string_content()?;
3460 if self.config.b_prefix_is_byte_string {
3461 self.add_token_with_text(TokenType::ByteString, string_value);
3462 } else {
3463 self.add_token_with_text(TokenType::BitString, string_value);
3464 }
3465 return Ok(());
3466 }
3467 }
3468
3469 if text.eq_ignore_ascii_case("U")
3471 && self.peek() == '&'
3472 && self.current + 1 < self.size
3473 && self.char_at(self.current + 1) == '\''
3474 {
3475 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3478 self.add_token_with_text(TokenType::UnicodeString, string_value);
3479 return Ok(());
3480 }
3481
3482 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3483
3484 if let Some(text) = owned_text {
3485 self.add_token_with_text(token_type, text);
3486 } else {
3487 self.add_token_from_source(token_type, self.start, self.current);
3488 }
3489 Ok(())
3490 }
3491
3492 fn scan_string_content_with_escapes(
3496 &mut self,
3497 force_backslash_escapes: bool,
3498 ) -> Result<String> {
3499 let use_backslash_escapes =
3500 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3501 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3502 {
3503 return Ok(self.text_from_range(start, end));
3504 }
3505 let mut value = String::new();
3506
3507 while !self.is_at_end() {
3508 let c = self.peek();
3509 if c == '\'' {
3510 if self.peek_next() == '\'' {
3511 value.push('\'');
3513 self.advance();
3514 self.advance();
3515 } else {
3516 break;
3517 }
3518 } else if c == '\\' && use_backslash_escapes {
3519 value.push(self.advance());
3521 if !self.is_at_end() {
3522 value.push(self.advance());
3523 }
3524 } else {
3525 value.push(self.advance());
3526 }
3527 }
3528
3529 if self.is_at_end() {
3530 return Err(Error::tokenize(
3531 "Unterminated string",
3532 self.line,
3533 self.column,
3534 self.start,
3535 self.current,
3536 ));
3537 }
3538
3539 self.advance(); Ok(value)
3541 }
3542
3543 fn scan_string_content(&mut self) -> Result<String> {
3545 self.scan_string_content_with_escapes(false)
3546 }
3547
3548 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3551 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3552 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3553 {
3554 return Ok(self.text_from_range(start, end));
3555 }
3556 let mut value = String::new();
3557
3558 while !self.is_at_end() {
3559 let c = self.peek();
3560 if c == '"' {
3561 if self.peek_next() == '"' {
3562 value.push('"');
3564 self.advance();
3565 self.advance();
3566 } else {
3567 break;
3568 }
3569 } else if c == '\\' && use_backslash_escapes {
3570 self.advance(); if !self.is_at_end() {
3573 let escaped = self.advance();
3574 match escaped {
3575 'n' => value.push('\n'),
3576 'r' => value.push('\r'),
3577 't' => value.push('\t'),
3578 '0' => value.push('\0'),
3579 '\\' => value.push('\\'),
3580 '"' => value.push('"'),
3581 '\'' => value.push('\''),
3582 'x' => {
3583 let mut hex = String::new();
3585 for _ in 0..2 {
3586 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3587 hex.push(self.advance());
3588 }
3589 }
3590 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3591 value.push(byte as char);
3592 } else {
3593 value.push('\\');
3595 value.push('x');
3596 value.push_str(&hex);
3597 }
3598 }
3599 _ => {
3600 value.push('\\');
3602 value.push(escaped);
3603 }
3604 }
3605 }
3606 } else {
3607 value.push(self.advance());
3608 }
3609 }
3610
3611 if self.is_at_end() {
3612 return Err(Error::tokenize(
3613 "Unterminated double-quoted string",
3614 self.line,
3615 self.column,
3616 self.start,
3617 self.current,
3618 ));
3619 }
3620
3621 self.advance(); Ok(value)
3623 }
3624
3625 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3630 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3631 quote_char,
3632 self.config.string_escapes_allowed_in_raw_strings,
3633 ) {
3634 return Ok(self.text_from_range(start, end));
3635 }
3636 let mut value = String::new();
3637
3638 while !self.is_at_end() {
3639 let c = self.peek();
3640 if c == quote_char {
3641 if self.peek_next() == quote_char {
3642 value.push(quote_char);
3644 self.advance();
3645 self.advance();
3646 } else {
3647 break;
3648 }
3649 } else if c == '\\'
3650 && self.peek_next() == quote_char
3651 && self.config.string_escapes_allowed_in_raw_strings
3652 {
3653 value.push(quote_char);
3657 self.advance(); self.advance(); } else {
3660 value.push(self.advance());
3662 }
3663 }
3664
3665 if self.is_at_end() {
3666 return Err(Error::tokenize(
3667 "Unterminated raw string",
3668 self.line,
3669 self.column,
3670 self.start,
3671 self.current,
3672 ));
3673 }
3674
3675 self.advance(); Ok(value)
3677 }
3678
3679 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3682 let mut value = String::new();
3683
3684 while !self.is_at_end() {
3685 let c = self.peek();
3686 if c == quote_char && self.peek_next() == quote_char {
3687 if self.current + 2 < self.size && self.char_at(self.current + 2) == quote_char {
3689 self.advance(); self.advance(); self.advance(); return Ok(value);
3694 }
3695 }
3696 let ch = self.advance();
3698 value.push(ch);
3699 }
3700
3701 Err(Error::tokenize(
3702 "Unterminated raw triple-quoted string",
3703 self.line,
3704 self.column,
3705 self.start,
3706 self.current,
3707 ))
3708 }
3709
3710 fn scan_dollar_identifier(&mut self) -> Result<()> {
3715 self.advance();
3717
3718 while !self.is_at_end() {
3720 let c = self.peek();
3721 if c.is_alphanumeric() || c == '_' || c == '$' {
3722 self.advance();
3723 } else {
3724 break;
3725 }
3726 }
3727
3728 self.add_token(TokenType::Var);
3729 Ok(())
3730 }
3731
3732 fn scan_tsql_identifier(&mut self) -> Result<()> {
3733 let first = self.advance();
3735
3736 if first == '#' && self.peek() == '#' {
3738 self.advance();
3739 }
3740
3741 if !self.advance_ascii_identifier() {
3743 while !self.is_at_end() {
3744 let c = self.peek();
3745 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3746 self.advance();
3747 } else {
3748 break;
3749 }
3750 }
3751 }
3752
3753 self.add_token(TokenType::Var);
3755 Ok(())
3756 }
3757
3758 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3762 let len = self.tokens.len();
3763 if len < 3 {
3764 return None;
3765 }
3766
3767 let last = &self.tokens[len - 1];
3769 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3770 return None;
3771 }
3772 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3773 return None;
3774 }
3775
3776 let format_tok = &self.tokens[len - 2];
3778 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3779 return None;
3780 }
3781
3782 let has_insert = self.tokens[..len - 2]
3784 .iter()
3785 .rev()
3786 .take(20)
3787 .any(|t| t.token_type() == TokenType::Insert);
3788 if !has_insert {
3789 return None;
3790 }
3791
3792 let raw_start = self.current;
3796 while !self.is_at_end() {
3797 let c = self.peek();
3798 if c == '\n' {
3799 let saved = self.current;
3801 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3804 self.advance();
3805 }
3806 if self.is_at_end() || self.peek() == '\n' {
3807 let raw = self.text_from_range(raw_start, saved);
3810 return Some(raw.trim().to_string());
3811 }
3812 } else {
3814 self.advance();
3815 }
3816 }
3817
3818 let raw = self.text_from_range(raw_start, self.current);
3820 let trimmed = raw.trim().to_string();
3821 if trimmed.is_empty() {
3822 None
3823 } else {
3824 Some(trimmed)
3825 }
3826 }
3827
3828 fn add_token(&mut self, token_type: TokenType) {
3829 self.add_token_from_source(token_type, self.start, self.current);
3830 }
3831
3832 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3833 let span = Span::new(self.start, self.current, self.line, self.column);
3834 if let Some(stats) = &mut self.guard_stats {
3835 stats.observe(token_type, span);
3836 }
3837 let mut token = if self
3838 .cursor
3839 .source_range(self.source, text_start, text_end)
3840 .is_some()
3841 {
3842 T::from_source(
3843 token_type,
3844 self.source,
3845 text_start,
3846 text_end,
3847 span,
3848 self.shared_source.as_ref(),
3849 )
3850 } else {
3851 T::from_owned(
3852 token_type,
3853 self.cursor
3854 .text_from_range(self.source, text_start, text_end),
3855 span,
3856 )
3857 };
3858 token.comments_mut().append(&mut self.comments);
3859 self.tokens.push(token);
3860 }
3861
3862 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3863 let span = Span::new(self.start, self.current, self.line, self.column);
3864 if let Some(stats) = &mut self.guard_stats {
3865 stats.observe(token_type, span);
3866 }
3867 let mut token = T::from_owned(token_type, text, span);
3868 token.comments_mut().append(&mut self.comments);
3869 self.tokens.push(token);
3870 }
3871}
3872
3873#[cfg(test)]
3874mod tests {
3875 use super::*;
3876
3877 #[test]
3878 fn ascii_fast_path_matches_character_buffer_path() {
3879 let tokenizer = Tokenizer::default();
3880 let inputs = [
3881 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3882 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3883 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3884 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3885 ];
3886
3887 for sql in inputs {
3888 assert_eq!(
3889 tokenizer.tokenize(sql).unwrap(),
3890 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3891 "tokenization differs for {sql}"
3892 );
3893 }
3894 }
3895
3896 #[test]
3897 fn parser_tokens_match_public_tokens() {
3898 let tokenizer = Tokenizer::default();
3899 let inputs = [
3900 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3901 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3902 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3903 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3904 ];
3905
3906 for sql in inputs {
3907 let public = tokenizer.tokenize(sql).unwrap();
3908 let source: Arc<str> = Arc::from(sql);
3909 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3910 let materialized = parser
3911 .iter()
3912 .map(|token| Token {
3913 token_type: token.token_type,
3914 text: token.text_owned(),
3915 span: token.span,
3916 comments: token.comments.clone(),
3917 trailing_comments: token.trailing_comments.clone(),
3918 })
3919 .collect::<Vec<_>>();
3920
3921 assert_eq!(
3922 materialized, public,
3923 "parser tokenization differs for {sql}"
3924 );
3925 assert_eq!(stats.token_count, public.len());
3926 }
3927 }
3928
3929 #[test]
3930 fn parser_tokens_borrow_unchanged_ascii_text() {
3931 let tokenizer = Tokenizer::default();
3932 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3933 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3934
3935 assert!(tokens
3936 .iter()
3937 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3938 }
3939
3940 #[test]
3941 fn test_simple_select() {
3942 let tokenizer = Tokenizer::default();
3943 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3944
3945 assert_eq!(tokens.len(), 2);
3946 assert_eq!(tokens[0].token_type, TokenType::Select);
3947 assert_eq!(tokens[1].token_type, TokenType::Number);
3948 assert_eq!(tokens[1].text, "1");
3949 }
3950
3951 #[test]
3952 fn test_select_with_identifier() {
3953 let tokenizer = Tokenizer::default();
3954 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
3955
3956 assert_eq!(tokens.len(), 6);
3957 assert_eq!(tokens[0].token_type, TokenType::Select);
3958 assert_eq!(tokens[1].token_type, TokenType::Var);
3959 assert_eq!(tokens[1].text, "a");
3960 assert_eq!(tokens[2].token_type, TokenType::Comma);
3961 assert_eq!(tokens[3].token_type, TokenType::Var);
3962 assert_eq!(tokens[3].text, "b");
3963 assert_eq!(tokens[4].token_type, TokenType::From);
3964 assert_eq!(tokens[5].token_type, TokenType::Var);
3965 assert_eq!(tokens[5].text, "t");
3966 }
3967
3968 #[test]
3969 fn test_string_literal() {
3970 let tokenizer = Tokenizer::default();
3971 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
3972
3973 assert_eq!(tokens.len(), 2);
3974 assert_eq!(tokens[1].token_type, TokenType::String);
3975 assert_eq!(tokens[1].text, "hello");
3976 }
3977
3978 #[test]
3979 fn test_escaped_string() {
3980 let tokenizer = Tokenizer::default();
3981 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
3982
3983 assert_eq!(tokens.len(), 2);
3984 assert_eq!(tokens[1].token_type, TokenType::String);
3985 assert_eq!(tokens[1].text, "it's");
3986 }
3987
3988 #[test]
3989 fn test_escape_follow_chars_gate_builtin_decoding() {
3990 let mut config = TokenizerConfig::default();
3991 config.string_escapes.push('\\');
3992 config.escape_follow_chars = vec!['n'];
3993 let tokenizer = Tokenizer::new(config);
3994 let tokens = tokenizer.tokenize(r"SELECT '\n\a\f\Z\x21'").unwrap();
3995
3996 assert_eq!(tokens[1].text, "\nafZx21");
3997 }
3998
3999 #[test]
4000 fn test_configured_numeric_escapes_require_complete_sequences() {
4001 let mut config = TokenizerConfig::default();
4002 config.string_escapes.push('\\');
4003 config.escape_follow_chars = vec!['0', '1', '2', '3', '4', '5', '6', '7', 'x', 'u'];
4004 let tokenizer = Tokenizer::new(config);
4005 let tokens = tokenizer
4006 .tokenize(r"SELECT '\041\x21\u26c4-\777\x2\u26c'")
4007 .unwrap();
4008
4009 assert_eq!(tokens[1].text, "!!\u{26c4}-777x2u26c");
4010 }
4011
4012 #[test]
4013 fn test_terminal_backslash_quote_recovery() {
4014 let mut config = TokenizerConfig::default();
4015 config.string_escapes.push('\\');
4016 config.recover_terminal_backslash_quote = true;
4017 let tokenizer = Tokenizer::new(config);
4018 let tokens = tokenizer
4019 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4020 .unwrap();
4021
4022 assert_eq!(tokens.len(), 8);
4023 assert_eq!(tokens[3].token_type, TokenType::String);
4024 assert_eq!(tokens[3].text, "a\\");
4025 assert_eq!(tokens[4].token_type, TokenType::Or);
4026 }
4027
4028 #[test]
4029 fn test_comments() {
4030 let tokenizer = Tokenizer::default();
4031 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4032
4033 assert_eq!(tokens.len(), 2);
4034 assert_eq!(tokens[0].trailing_comments.len(), 1);
4037 assert_eq!(tokens[0].trailing_comments[0], " comment");
4038 }
4039
4040 #[test]
4041 fn test_comment_in_and_chain() {
4042 use crate::generator::Generator;
4043 use crate::parser::Parser;
4044
4045 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4047 let ast = Parser::parse_sql(sql).unwrap();
4048 let mut gen = Generator::default();
4049 let output = gen.generate(&ast[0]).unwrap();
4050 assert_eq!(
4051 output,
4052 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4053 );
4054 }
4055
4056 #[test]
4057 fn test_operators() {
4058 let tokenizer = Tokenizer::default();
4059 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4060
4061 assert_eq!(tokens.len(), 5);
4062 assert_eq!(tokens[0].token_type, TokenType::Number);
4063 assert_eq!(tokens[1].token_type, TokenType::Plus);
4064 assert_eq!(tokens[2].token_type, TokenType::Number);
4065 assert_eq!(tokens[3].token_type, TokenType::Star);
4066 assert_eq!(tokens[4].token_type, TokenType::Number);
4067 }
4068
4069 #[test]
4070 fn test_comparison_operators() {
4071 let tokenizer = Tokenizer::default();
4072 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4073
4074 assert_eq!(tokens[1].token_type, TokenType::Lte);
4075 assert_eq!(tokens[3].token_type, TokenType::Gte);
4076 assert_eq!(tokens[5].token_type, TokenType::Neq);
4077 }
4078
4079 #[test]
4080 fn test_national_string() {
4081 let tokenizer = Tokenizer::default();
4082 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4083
4084 assert_eq!(
4085 tokens.len(),
4086 1,
4087 "Expected 1 token for N'abc', got {:?}",
4088 tokens
4089 );
4090 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4091 assert_eq!(tokens[0].text, "abc");
4092 }
4093
4094 #[test]
4095 fn test_hex_string() {
4096 let tokenizer = Tokenizer::default();
4097 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4098
4099 assert_eq!(
4100 tokens.len(),
4101 1,
4102 "Expected 1 token for X'ABCD', got {:?}",
4103 tokens
4104 );
4105 assert_eq!(tokens[0].token_type, TokenType::HexString);
4106 assert_eq!(tokens[0].text, "ABCD");
4107 }
4108
4109 #[test]
4110 fn test_bit_string() {
4111 let tokenizer = Tokenizer::default();
4112 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4113
4114 assert_eq!(
4115 tokens.len(),
4116 1,
4117 "Expected 1 token for B'01010', got {:?}",
4118 tokens
4119 );
4120 assert_eq!(tokens[0].token_type, TokenType::BitString);
4121 assert_eq!(tokens[0].text, "01010");
4122 }
4123
4124 #[test]
4125 fn test_trailing_dot_number() {
4126 let tokenizer = Tokenizer::default();
4127
4128 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4130 assert_eq!(
4131 tokens.len(),
4132 2,
4133 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4134 tokens
4135 );
4136 assert_eq!(tokens[1].token_type, TokenType::Number);
4137 assert_eq!(tokens[1].text, "1.");
4138
4139 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4141 assert_eq!(tokens[1].text, "1.5");
4142
4143 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4146 assert_eq!(
4147 tokens.len(),
4148 3,
4149 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4150 tokens
4151 );
4152 assert_eq!(tokens[1].token_type, TokenType::Number);
4153 assert_eq!(tokens[1].text, "1.");
4154 assert_eq!(tokens[2].token_type, TokenType::Var);
4155
4156 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4158 assert_eq!(tokens[1].token_type, TokenType::Number);
4159 assert_eq!(tokens[1].text, "1");
4160 assert_eq!(tokens[2].token_type, TokenType::Dot);
4161 assert_eq!(tokens[3].token_type, TokenType::Dot);
4162 assert_eq!(tokens[4].token_type, TokenType::Number);
4163 assert_eq!(tokens[4].text, "2");
4164 }
4165
4166 #[test]
4167 fn test_leading_dot_number() {
4168 let tokenizer = Tokenizer::default();
4169
4170 let tokens = tokenizer.tokenize(".25").unwrap();
4172 assert_eq!(
4173 tokens.len(),
4174 1,
4175 "Expected 1 token for '.25', got {:?}",
4176 tokens
4177 );
4178 assert_eq!(tokens[0].token_type, TokenType::Number);
4179 assert_eq!(tokens[0].text, ".25");
4180
4181 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4183 assert_eq!(
4184 tokens.len(),
4185 4,
4186 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4187 tokens
4188 );
4189 assert_eq!(tokens[0].token_type, TokenType::Sample);
4190 assert_eq!(tokens[1].token_type, TokenType::LParen);
4191 assert_eq!(tokens[2].token_type, TokenType::Number);
4192 assert_eq!(tokens[2].text, ".25");
4193 assert_eq!(tokens[3].token_type, TokenType::RParen);
4194
4195 let tokens = tokenizer.tokenize(".5e10").unwrap();
4197 assert_eq!(
4198 tokens.len(),
4199 1,
4200 "Expected 1 token for '.5e10', got {:?}",
4201 tokens
4202 );
4203 assert_eq!(tokens[0].token_type, TokenType::Number);
4204 assert_eq!(tokens[0].text, ".5e10");
4205
4206 let tokens = tokenizer.tokenize("a.b").unwrap();
4208 assert_eq!(
4209 tokens.len(),
4210 3,
4211 "Expected 3 tokens for 'a.b', got {:?}",
4212 tokens
4213 );
4214 assert_eq!(tokens[1].token_type, TokenType::Dot);
4215 }
4216
4217 #[test]
4218 fn test_unrecognized_character() {
4219 let tokenizer = Tokenizer::default();
4220
4221 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4223 assert!(
4224 result.is_ok(),
4225 "Curly quotes should be tokenized as strings"
4226 );
4227
4228 let result = tokenizer.tokenize("SELECT • FROM t");
4230 assert!(result.is_err());
4231 }
4232
4233 #[test]
4234 fn test_colon_eq_tokenization() {
4235 let tokenizer = Tokenizer::default();
4236
4237 let tokens = tokenizer.tokenize("a := 1").unwrap();
4239 assert_eq!(tokens.len(), 3);
4240 assert_eq!(tokens[0].token_type, TokenType::Var);
4241 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4242 assert_eq!(tokens[2].token_type, TokenType::Number);
4243
4244 let tokens = tokenizer.tokenize("a:b").unwrap();
4246 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4247 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4248
4249 let tokens = tokenizer.tokenize("a::INT").unwrap();
4251 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4252 }
4253
4254 #[test]
4255 fn test_colon_eq_parsing() {
4256 use crate::generator::Generator;
4257 use crate::parser::Parser;
4258
4259 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4261 .expect("Failed to parse MySQL @var := expr");
4262 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4263 assert_eq!(output, "SELECT @var1 := 1, @var2");
4264
4265 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4267 .expect("Failed to parse MySQL @var2 := @var1");
4268 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4269 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4270
4271 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4273 .expect("Failed to parse MySQL @var := COUNT(*)");
4274 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4275 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4276
4277 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4279 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4280 assert_eq!(output, "SET @var1 = 1");
4281
4282 let ast =
4284 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4285 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4286 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4287
4288 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4290 .expect("Failed to parse UNNEST with :=");
4291 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4292 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4293
4294 let ast =
4296 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4297 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4298 assert_eq!(output, "SELECT 1 AS foo");
4299
4300 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4302 .expect("Failed to parse DuckDB multiple prefix aliases");
4303 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4304 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4305 }
4306
4307 #[test]
4308 fn test_colon_eq_dialect_roundtrip() {
4309 use crate::dialects::{Dialect, DialectType};
4310
4311 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4312 let d = Dialect::get(dialect);
4313 let ast = d
4314 .parse(sql)
4315 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4316 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4317 let transformed = d
4318 .transform(ast[0].clone())
4319 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4320 let output = d
4321 .generate(&transformed)
4322 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4323 let expected = expected.unwrap_or(sql);
4324 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4325 }
4326
4327 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4329 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4330 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4331 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4332
4333 check(
4335 DialectType::DuckDB,
4336 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4337 None,
4338 );
4339 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4340
4341 {
4344 let d = Dialect::get(DialectType::DuckDB);
4345 let ast = d
4346 .parse("STRUCT_PACK(a := 'b')::json")
4347 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4348 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4349 }
4350
4351 check(
4353 DialectType::DuckDB,
4354 "SELECT foo: 1",
4355 Some("SELECT 1 AS foo"),
4356 );
4357 check(
4358 DialectType::DuckDB,
4359 "SELECT foo: 1, bar: 2, baz: 3",
4360 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4361 );
4362 }
4363
4364 #[test]
4365 fn test_comment_roundtrip() {
4366 use crate::generator::Generator;
4367 use crate::parser::Parser;
4368
4369 fn check_roundtrip(sql: &str) -> Option<String> {
4370 let ast = match Parser::parse_sql(sql) {
4371 Ok(a) => a,
4372 Err(e) => return Some(format!("Parse error: {:?}", e)),
4373 };
4374 if ast.is_empty() {
4375 return Some("Empty AST".to_string());
4376 }
4377 let mut generator = Generator::default();
4378 let output = match generator.generate(&ast[0]) {
4379 Ok(o) => o,
4380 Err(e) => return Some(format!("Gen error: {:?}", e)),
4381 };
4382 if output == sql {
4383 None
4384 } else {
4385 Some(format!(
4386 "Mismatch:\n input: {}\n output: {}",
4387 sql, output
4388 ))
4389 }
4390 }
4391
4392 let tests = vec![
4393 "SELECT c /* c1 */ AS alias /* c2 */",
4399 "SELECT a /* x */, b /* x */",
4401 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4403 "SELECT * FROM foo /* x */, bla /* x */",
4405 "SELECT 1 /* comment */ + 1",
4407 "SELECT 1 /* c1 */ + 2 /* c2 */",
4408 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4409 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4411 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4413 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4415 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4417 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4419 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4420 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4421 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4422 "/* comment */ CREATE TABLE foo AS SELECT 1",
4423 "INSERT INTO foo SELECT * FROM bar /* comment */",
4425 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4427 ];
4428
4429 let mut failures = Vec::new();
4430 for sql in tests {
4431 if let Some(e) = check_roundtrip(sql) {
4432 failures.push(e);
4433 }
4434 }
4435
4436 if !failures.is_empty() {
4437 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4438 }
4439 }
4440
4441 #[test]
4442 fn test_dollar_quoted_string_parsing() {
4443 use crate::dialects::{Dialect, DialectType};
4444
4445 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4447 assert_eq!(tag, Some("FOO".to_string()));
4448 assert_eq!(content, "content here");
4449
4450 let (tag, content) = super::parse_dollar_string_token("just content");
4451 assert_eq!(tag, None);
4452 assert_eq!(content, "just content");
4453
4454 fn check_databricks(sql: &str, expected: Option<&str>) {
4456 let d = Dialect::get(DialectType::Databricks);
4457 let ast = d
4458 .parse(sql)
4459 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4460 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4461 let transformed = d
4462 .transform(ast[0].clone())
4463 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4464 let output = d
4465 .generate(&transformed)
4466 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4467 let expected = expected.unwrap_or(sql);
4468 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4469 }
4470
4471 check_databricks(
4473 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4474 None
4475 );
4476
4477 check_databricks(
4479 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4480 None
4481 );
4482 }
4483
4484 #[test]
4485 fn test_numeric_underscore_stripping() {
4486 let mut config = TokenizerConfig::default();
4488 config.numbers_can_be_underscore_separated = true;
4489 let tokenizer = Tokenizer::new(config);
4490
4491 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4493 assert_eq!(tokens[1].token_type, TokenType::Number);
4494 assert_eq!(tokens[1].text, "12345");
4495
4496 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4498 assert_eq!(tokens[1].token_type, TokenType::Number);
4499 assert_eq!(tokens[1].text, "20000");
4500
4501 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4503 assert_eq!(tokens[1].token_type, TokenType::Number);
4504 assert_eq!(tokens[1].text, "12E+10");
4505
4506 let default_tokenizer = Tokenizer::default();
4508 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4509 assert_eq!(tokens[1].token_type, TokenType::Number);
4510 assert_eq!(tokens[1].text, "1_2_3_4_5");
4511 }
4512}